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