diff --git a/common/api.js b/common/api.js index 9b4a3c7..66f8a8f 100644 --- a/common/api.js +++ b/common/api.js @@ -293,6 +293,12 @@ export async function CheckInEvent({ eventId }) { return { data: res } } +/** 审核报名(仅发起人可操作;approve: 1=待审核 2=通过 3=拒绝) */ +export async function ApproveEvent({ eventId, userId, approve }) { + const res = await client.ApproveEvent({ id: eventId, userId, approve }) + return { data: res } +} + // ========================================== // 私信 API(基于 HaveADrink SDK 真实接口) // ========================================== diff --git a/common/utils.js b/common/utils.js index 9b3c644..b83735e 100644 --- a/common/utils.js +++ b/common/utils.js @@ -205,6 +205,69 @@ export function calcStats(records) { } } +/** + * 本地计算连续打卡天数(修正后端 streak 不区分饮酒/戒酒模式的问题) + * 双轨计算: + * - 连续饮酒:从今天的饮酒记录向前追溯;今天未饮酒(戒酒/未打卡)则从昨天算起 + * - 连续戒酒:从今天的戒酒记录向前追溯;今天饮酒/未打卡则从昨天算起 + * @param {Array} records 记录列表,每项需含 date 和 mode 字段 + * @returns {{ streak, streakType, drankStreak, abstainStreak }} + * streak/streakType:主展示值(今天有记录取今天模式,否则取最近记录模式) + */ +export function calcLocalStreak(records) { + if (!records || !records.length) { + return { streak: 0, streakType: 'drank', drankStreak: 0, abstainStreak: 0 } + } + const today = formatDate(new Date(), 'YYYY-MM-DD') + // 按日期去重(同一天多条取第一条) + const byDate = {} + records.forEach(r => { + if (r && r.date && byDate[r.date] === undefined) byDate[r.date] = r + }) + const dates = Object.keys(byDate).sort() + // 单模式连续链:优先从今天起算,今天非该模式则宽限到昨天 + const chainOf = (mode) => { + const todayMode = byDate[today] ? byDate[today].mode : null + let base = null + if (todayMode === mode) { + base = today + } else { + const y = new Date() + y.setDate(y.getDate() - 1) + const yStr = formatDate(y, 'YYYY-MM-DD') + if (byDate[yStr] && byDate[yStr].mode === mode) base = yStr + } + if (!base) return 0 + let count = 1 + const d = new Date(base) + for (let i = 1; i < 366; i++) { + const prev = new Date(d) + prev.setDate(prev.getDate() - i) + const r = byDate[formatDate(prev, 'YYYY-MM-DD')] + if (r && r.mode === mode) { + count++ + } else { + break + } + } + return count + } + const drankStreak = chainOf('drank') + const abstainStreak = chainOf('abstain') + // 主展示值:今天有记录取今天模式,否则取最近一次记录的模式 + let streak + let streakType + if (byDate[today]) { + streakType = byDate[today].mode || 'drank' + streak = streakType === 'drank' ? drankStreak : abstainStreak + } else { + const lastDate = dates[dates.length - 1] + streakType = byDate[lastDate].mode || 'drank' + streak = streakType === 'drank' ? drankStreak : abstainStreak + } + return { streak, streakType, drankStreak, abstainStreak } +} + /** * 主题系统:日夜自动切换 * 白天:6:00-16:00 → light diff --git a/common/websocket.js b/common/websocket.js index 114bc78..a3cd8b7 100644 --- a/common/websocket.js +++ b/common/websocket.js @@ -97,7 +97,9 @@ class WebSocketManager { } // ===== 服务端协议消息(SDK 已解析 { mesgType, data }) ===== - conn.onServerChatData = (data) => { + conn.onServerChatData = (data) => + console.log(data,'==============='); + { this._emit('message', data) } diff --git a/components/EventCard.vue b/components/EventCard.vue index b85d49f..573757c 100644 --- a/components/EventCard.vue +++ b/components/EventCard.vue @@ -42,7 +42,8 @@ - ✓ 已报名 + ⏳ 待审核 + ✓ 已报名 报名 @@ -68,6 +69,10 @@ export default { statusLabel() { const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' } return map[this.statusKey] || '报名中' + }, + /** 已报名但审核状态为待审核(1) */ + isPending() { + return Number(this.event.approveStatus) === 1 } } } @@ -199,6 +204,11 @@ export default { font-weight: $fw-bold; } +.event-action-pending { + color: $amber; + background: rgba(232,168,56,0.12); +} + .event-manage { display: flex; align-items: center; diff --git a/components/FeedCard.vue b/components/FeedCard.vue index 1504f7b..be9cc92 100644 --- a/components/FeedCard.vue +++ b/components/FeedCard.vue @@ -31,15 +31,16 @@ {{ feelingLabel(feed.feeling) }} - + @@ -76,9 +77,87 @@ export default { canDelete: { type: Boolean, default: false } }, emits: ['item-click', 'like', 'comment', 'share', 'delete'], + data() { + return { + // 图片宽高比缓存:url -> width/height + imgRatios: {} + } + }, + watch: { + 'feed.images': { + handler(list) { this.loadImgMeta(list) }, + immediate: true + } + }, methods: { getCatIcon, isIconPath, + /** 拉取图片真实尺寸,用于按比例计算展示高度 */ + loadImgMeta(list) { + if (!list || !list.length) return + list.forEach(url => { + if (!url || this.imgRatios[url] !== undefined) return + uni.getImageInfo({ + src: url, + success: res => { + if (res.width && res.height) { + this.imgRatios[url] = res.width / res.height + } + } + }) + }) + }, + /** 每行图片数量:4张用2x2,其余每行最多3张 */ + perRow() { + const n = (this.feed.images || []).length + return n === 4 ? 2 : Math.min(n || 1, 3) + }, + /** 列宽(rpx):750 - 页面边距64 - 卡片内边距96 - 行间gap */ + colWidth() { + const gap = 16 + const total = 750 - 64 - 96 + const cols = this.perRow() + return (total - gap * (cols - 1)) / cols + }, + /** 单张图片的目标高度:按真实比例,夹在上下限内 */ + targetHeight(ratio, colW, minH, maxH) { + const r = ratio && ratio > 0 ? ratio : 1 + return Math.max(minH, Math.min(colW / r, maxH)) + }, + /** 同行图片统一高度(取同行目标高度的均值) */ + rowHeight(rowIdx) { + const list = this.feed.images || [] + const cols = this.perRow() + const colW = this.colWidth() + const single = cols === 1 + const minH = single ? 300 : 180 + const maxH = single ? 720 : 400 + const start = rowIdx * cols + const items = list.slice(start, start + cols) + let sum = 0 + items.forEach(url => { + sum += this.targetHeight(this.imgRatios[url], colW, minH, maxH) + }) + return Math.round(sum / items.length) + }, + /** 点击图片预览大图(支持左右滑动切换) */ + previewImage(i) { + const urls = this.feed.images || [] + if (!urls.length) return + uni.previewImage({ + urls, + current: urls[i] + }) + }, + imgStyle(i) { + const cols = this.perRow() + const gap = 16 + const width = cols === 1 ? '100%' : `calc(${(100 / cols).toFixed(4)}% - ${((gap * (cols - 1)) / cols).toFixed(2)}rpx)` + return { + width, + height: this.rowHeight(Math.floor(i / cols)) + 'rpx' + } + }, feelingLabel(f) { const val = String(f).toLowerCase() const map = { @@ -211,20 +290,11 @@ export default { .feed-img { border-radius: $radius-md; - height: 200rpx; -} - -.feed-img-1 { - width: 100%; - height: 320rpx; -} - -.feed-img-2 { - width: calc(50% - 8rpx); -} - -.feed-img-3 { - width: calc(33.33% - 11rpx); + display: block; + /* 比例未获取前的占位尺寸,避免布局跳动 */ + width: 33%; + height: 220rpx; + background: $bg-elevated; } .feed-actions { diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index c775d5f..bbad5d6 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -4,9 +4,9 @@ "requires": true, "packages": { "node_modules/HaveADrink": { - "version": "1.0.12", - "resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.12.tgz", - "integrity": "sha512-B9/2sOCIvMczQzsWA1qqzEOlwKxl9eC5OEqvRgh1dewgsbA4zzOkCQztuJxZOq9Sz5mwzEBM6/bcYdJyGAox3A==", + "version": "1.0.15", + "resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.15.tgz", + "integrity": "sha512-ZKgrIKuwXXuKRY2D34FRNUwlr1Y4bAUtXwrjpoHwLPa30KNGVvuXhfYvxAWf5cDvTJw3b/lm8JSSYwCqcIZ3Bw==", "license": "ISC" } } diff --git a/node_modules/HaveADrink/README.md b/node_modules/HaveADrink/README.md index 63e00aa..c378baf 100644 --- a/node_modules/HaveADrink/README.md +++ b/node_modules/HaveADrink/README.md @@ -14,7 +14,7 @@ * 6. 社交模块 - 朋友圈动态、点赞 -**版本:** v1.0.12 +**版本:** v1.0.15 ## 安装 @@ -1116,6 +1116,34 @@ const req = new UpdatePreferencesReq() client.UpdatePreferences(req).then(...).catch(...) ``` +### 获取用户基础信息 + + + +GET `/api/have_a_drink/v1/user/user/basic_info` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 用户ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|nickname|`string`| 用户昵称
| +|avatar|`string`| 头像URL
| + + + + +```javascript +const req = new GetUserBasicInfoReq() +client.GetUserBasicInfo(req).then(...).catch(...) +``` + ### 上传文件 @@ -1865,6 +1893,35 @@ const req = new CheckInEventReq() client.CheckInEvent(req).then(...).catch(...) ``` +### 酒局发起者审核报名用户 + + + +POST `/api/have_a_drink/v1/events/events/approve` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 酒局ID| +|userId|`string\|number`|| 报名用户ID| +|approve|`ApproveStatus`|待审核: ApproveStatus.Pending = 1
已通过: ApproveStatus.Approved = 2
已拒绝: ApproveStatus.Rejected = 3| 审核结果| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|ok|`boolean`| 是否成功
| + + + + +```javascript +const req = new ApproveEventReq() +client.ApproveEvent(req).then(...).catch(...) +``` + ### 获取会话列表 @@ -1935,6 +1992,10 @@ client.GetConversations(req).then(...).catch(...) |conversationId|`string`| 会话ID| |senderId|`string\|number`| 发送者ID| |receiverId|`string\|number`| 接收者ID| +|senderName|`string`| 发送者昵称| +|receiverName|`string`| 接收者昵称| +|senderAvatar|`string`| 发送者头像URL| +|receiverAvatar|`string`| 接收者头像URL| |type|`MessageType`| 消息类型| |content|`string`| 消息内容| |timestamp|`number`| 发送时间(毫秒时间戳)| diff --git a/node_modules/HaveADrink/index.d.ts b/node_modules/HaveADrink/index.d.ts index e7f0623..16596ec 100644 --- a/node_modules/HaveADrink/index.d.ts +++ b/node_modules/HaveADrink/index.d.ts @@ -297,6 +297,27 @@ export declare class EventStatus extends Enum { static getLabel(v: number|string): string; } +/** + * 报名审核状态 + */ +export declare class ApproveStatus extends Enum { + /** + * 待审核 + */ + static Pending: ApproveStatus; + /** + * 已通过 + */ + static Approved: ApproveStatus; + /** + * 已拒绝 + */ + static Rejected: ApproveStatus; + + static entries(): Array