feat(circle): 集成HaveADrink SDK实现酒友圈功能
- 集成HaveADrink SDK版本从1.0.4升级至1.0.8 - 实现酒友圈API接口,替换原有的mock数据 - 添加地理位置权限配置(requiredPrivateInfos) - 重构API调用方式,统一使用client实例调用真实接口 - 更新WebSocket协议与SDK保持一致,支持聊天、输入状态、已读回执等功能 - 实现游标分页获取动态信息流 - 添加文件上传API支持图片上传 - 优化API错误处理,支持业务级错误提示 - 调整酒局状态显示,新增upcoming待开始状态 - 优化自我感觉标签映射逻辑,支持更多状态类型 - 更新websocket连接认证方式,使用Authorization header传递token - 添加服务端连接确认和被踢下线事件处理
This commit is contained in:
+90
-72
@@ -32,8 +32,17 @@ function httpRequest(url, params) {
|
|||||||
success: (res) => {
|
success: (res) => {
|
||||||
const code = res.statusCode
|
const code = res.statusCode
|
||||||
if (code === 200 || code === 201 || code === 204) {
|
if (code === 200 || code === 201 || code === 204) {
|
||||||
|
// 业务级错误:HTTP 200 但 fail:true
|
||||||
|
if (res.data && res.data.fail === true) {
|
||||||
|
const bizMsg = res.data.msg || '操作失败'
|
||||||
|
if (!silent) {
|
||||||
|
uni.showToast({ title: bizMsg, icon: 'none', duration: 2500 })
|
||||||
|
}
|
||||||
|
reject({ msg: bizMsg, code })
|
||||||
|
return
|
||||||
|
}
|
||||||
resolve(res.data)
|
resolve(res.data)
|
||||||
} else if (code === 401 || code === 418) {
|
} else if (code === 401 ) {
|
||||||
// token/refresh_token 过期或无效,清除登录态
|
// token/refresh_token 过期或无效,清除登录态
|
||||||
uni.removeStorageSync('auth_token')
|
uni.removeStorageSync('auth_token')
|
||||||
uni.removeStorageSync('refresh_token')
|
uni.removeStorageSync('refresh_token')
|
||||||
@@ -76,8 +85,8 @@ function uploadRequest(url, params) {
|
|||||||
|
|
||||||
uni.uploadFile({
|
uni.uploadFile({
|
||||||
url: url,
|
url: url,
|
||||||
filePath: params.data.fileName || params.data.filePath,
|
filePath: params.data.image || params.data.fileName || params.data.filePath,
|
||||||
name: params.data.name || 'file',
|
name: params.data.name || 'image',
|
||||||
header: headers,
|
header: headers,
|
||||||
formData: params.data.formData || {},
|
formData: params.data.formData || {},
|
||||||
success: (uploadRes) => {
|
success: (uploadRes) => {
|
||||||
@@ -156,137 +165,146 @@ export async function refreshAuthToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 酒友圈 API(当前返回 Mock 数据,后端就绪后切换为 SDK 调用)
|
// 酒友圈 API(基于 HaveADrink SDK 真实接口)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
import { MOCK_FEEDS, MOCK_COMMENTS, MOCK_FRIENDS, MOCK_FRIEND_REQUESTS, MOCK_EVENTS, MOCK_CONVERSATIONS, MOCK_MESSAGES } from './mock-data'
|
|
||||||
|
|
||||||
/** 模拟网络延迟 */
|
/** 获取动态信息流(游标分页) */
|
||||||
function mockDelay(data, ms = 300) {
|
export async function GetCircleFeeds({ lastId = 0, pageSize = 10 } = {}) {
|
||||||
return new Promise(resolve => setTimeout(() => resolve({ data }), ms))
|
const res = await client.GetFeeds({ lastId, pageSize })
|
||||||
}
|
return { data: res }
|
||||||
|
|
||||||
/** 获取动态信息流 */
|
|
||||||
export function GetCircleFeeds({ page = 1, pageSize = 10 } = {}) {
|
|
||||||
// TODO: 后端就绪后替换为 client.GetCircleFeeds(...)
|
|
||||||
const start = (page - 1) * pageSize
|
|
||||||
const list = MOCK_FEEDS.slice(start, start + pageSize)
|
|
||||||
return mockDelay({ list, total: MOCK_FEEDS.length, hasMore: start + pageSize < MOCK_FEEDS.length })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 点赞动态 */
|
/** 点赞动态 */
|
||||||
export function LikeFeed({ feedId }) {
|
export async function LikeFeed({ feedId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.LikeFeed({ id: feedId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 取消点赞 */
|
/** 取消点赞 */
|
||||||
export function UnlikeFeed({ feedId }) {
|
export async function UnlikeFeed({ feedId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.UnlikeFeed({ id: feedId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取动态评论 */
|
/** 获取动态评论 */
|
||||||
export function GetFeedComments({ feedId }) {
|
export async function GetFeedComments({ feedId }) {
|
||||||
const list = MOCK_COMMENTS[feedId] || []
|
const res = await client.GetFeedComments({ id: feedId })
|
||||||
return mockDelay({ list })
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 发表评论 */
|
/** 发表评论 */
|
||||||
export function AddComment({ feedId, content }) {
|
export async function AddComment({ feedId, content }) {
|
||||||
return mockDelay({ success: true, comment: { id: 'c_' + Date.now(), content, time: '刚刚' } })
|
const res = await client.AddComment({ id: feedId, content })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 发布动态 */
|
/** 发布动态 */
|
||||||
export function PublishFeed({ text, images, recordId, visibility }) {
|
export async function PublishFeed({ text, images, recordId, visibility }) {
|
||||||
return mockDelay({ success: true, feedId: 'feed_' + Date.now() })
|
const res = await client.PublishFeed({ text, images, recordId, visibility })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取酒友列表 */
|
/** 获取酒友列表 */
|
||||||
export function GetFriends({ keyword = '' } = {}) {
|
export async function GetFriends({ keyword = '' } = {}) {
|
||||||
const list = keyword
|
const res = await client.GetFriends({ keyword })
|
||||||
? MOCK_FRIENDS.filter(f => f.nickname.includes(keyword))
|
return { data: res }
|
||||||
: MOCK_FRIENDS
|
|
||||||
return mockDelay({ list })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取好友请求 */
|
/** 获取好友请求 */
|
||||||
export function GetFriendRequests() {
|
export async function GetFriendRequests() {
|
||||||
return mockDelay({ list: MOCK_FRIEND_REQUESTS })
|
const res = await client.GetFriendRequests({})
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 发送好友请求 */
|
/** 发送好友请求 */
|
||||||
export function SendFriendRequest({ userId }) {
|
export async function SendFriendRequest({ userId, message = '' }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.SendFriendRequest({ userId, message })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 接受好友请求 */
|
/** 接受好友请求 */
|
||||||
export function AcceptFriendRequest({ requestId }) {
|
export async function AcceptFriendRequest({ requestId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.AcceptFriendRequest({ id: requestId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除酒友 */
|
/** 删除酒友 */
|
||||||
export function RemoveFriend({ userId }) {
|
export async function RemoveFriend({ userId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.RemoveFriend({ userId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取酒局列表 */
|
/** 获取酒局列表 */
|
||||||
export function GetEvents({ status = '' } = {}) {
|
export async function GetEvents({ status = '' } = {}) {
|
||||||
const list = status ? MOCK_EVENTS.filter(e => e.status === status) : MOCK_EVENTS
|
const res = await client.GetEvents({ status })
|
||||||
return mockDelay({ list })
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取酒局详情 */
|
/** 获取酒局详情 */
|
||||||
export function GetEventDetail({ eventId }) {
|
export async function GetEventDetail({ eventId }) {
|
||||||
const evt = MOCK_EVENTS.find(e => e.id === eventId) || MOCK_EVENTS[0]
|
const res = await client.GetEventDetail({ id: eventId })
|
||||||
return mockDelay(evt)
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 发起酒局 */
|
/** 发起酒局 */
|
||||||
export function CreateEvent({ title, time, location, maxPeople, note }) {
|
export async function CreateEvent({ title, time, location, maxPeople, note, latitude, longitude }) {
|
||||||
return mockDelay({ success: true, eventId: 'evt_' + Date.now() })
|
const geo = (latitude != null && longitude != null) ? { latitude, longitude } : undefined
|
||||||
|
const res = await client.CreateEvent({ title, time, location, maxPeople, geo, note })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 报名酒局 */
|
/** 报名酒局 */
|
||||||
export function JoinEvent({ eventId }) {
|
export async function JoinEvent({ eventId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.JoinEvent({ id: eventId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 取消报名 */
|
/** 取消报名 */
|
||||||
export function QuitEvent({ eventId }) {
|
export async function QuitEvent({ eventId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.QuitEvent({ id: eventId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 酒局签到 */
|
/** 酒局签到 */
|
||||||
export function CheckInEvent({ eventId }) {
|
export async function CheckInEvent({ eventId }) {
|
||||||
return mockDelay({ success: true })
|
const res = await client.CheckInEvent({ id: eventId })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// 私信 API(当前返回 Mock 数据,后端就绪后切换为 SDK 调用)
|
// 私信 API(基于 HaveADrink SDK 真实接口)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
/** 获取会话列表 */
|
/** 获取会话列表 */
|
||||||
export function GetConversations() {
|
export async function GetConversations() {
|
||||||
// TODO: 后端就绪后替换为 client.GetConversations(...)
|
const res = await client.GetConversations({})
|
||||||
return mockDelay({ list: MOCK_CONVERSATIONS })
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取历史消息 */
|
/** 获取历史消息(游标分页) */
|
||||||
export function GetMessages({ conversationId, page = 1, pageSize = 20 } = {}) {
|
export async function GetMessages({ conversationId, lastID = 0, pageSize = 20 } = {}) {
|
||||||
// TODO: 后端就绪后替换为 client.GetMessages(...)
|
const res = await client.GetMessages({ conversationId, lastID, pageSize })
|
||||||
const all = MOCK_MESSAGES[conversationId] || []
|
return { data: res }
|
||||||
const start = (page - 1) * pageSize
|
|
||||||
const list = all.slice(start, start + pageSize)
|
|
||||||
return mockDelay({ list, hasMore: start + pageSize < all.length })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 标记会话已读 */
|
/** 标记会话已读 */
|
||||||
export function MarkRead({ conversationId }) {
|
export async function MarkRead({ conversationId }) {
|
||||||
// TODO: 后端就绪后替换为 client.MarkRead(...)
|
const res = await client.MarkRead({ id: conversationId })
|
||||||
return mockDelay({ success: true })
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 获取未读消息总数 */
|
/** 获取未读消息总数 */
|
||||||
export function GetUnreadCount() {
|
export async function GetUnreadCount() {
|
||||||
// TODO: 后端就绪后替换为 client.GetUnreadCount(...)
|
const res = await client.GetUnreadCount({})
|
||||||
const total = MOCK_CONVERSATIONS.reduce((sum, c) => sum + c.unread, 0)
|
return { data: res }
|
||||||
return mockDelay({ total })
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// 文件上传 API
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
/** 上传图片(返回 url) */
|
||||||
|
export async function UploadImage({ filePath }) {
|
||||||
|
const res = await client.UploadImage({ image: filePath })
|
||||||
|
return { data: res }
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-22
@@ -1,9 +1,29 @@
|
|||||||
/* 碰盏日记 - WebSocket 管理器
|
/* 碰盏日记 - WebSocket 管理器
|
||||||
* 单例模式,负责私信实时通信
|
* 单例模式,负责私信实时通信
|
||||||
|
* 协议格式与 HaveADrink SDK ChatWebSocket 一致:{ mesgType, data }
|
||||||
* 支持心跳、自动重连、消息队列
|
* 支持心跳、自动重连、消息队列
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const WS_HOST = 'wss://dev.wash-painting.cn/ws'
|
const WS_HOST = 'wss://dev.wash-painting.cn/api/have_a_drink/v1/chat/chat'
|
||||||
|
|
||||||
|
// 客户端消息类型(与 SDK ClientMesgType 枚举值一致)
|
||||||
|
const ClientMesgType = {
|
||||||
|
Chat: 'chat',
|
||||||
|
Typing: 'typing',
|
||||||
|
Read: 'read',
|
||||||
|
Ping: 'ping'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务端消息类型(与 SDK ServerMesgType 枚举值一致)
|
||||||
|
const ServerMesgType = {
|
||||||
|
Chat: 'chat',
|
||||||
|
Typing: 'typing',
|
||||||
|
Read: 'read',
|
||||||
|
Ack: 'ack',
|
||||||
|
Pong: 'pong',
|
||||||
|
Connected: 'connected',
|
||||||
|
Kicked: 'kicked'
|
||||||
|
}
|
||||||
|
|
||||||
class WebSocketManager {
|
class WebSocketManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -21,6 +41,7 @@ class WebSocketManager {
|
|||||||
this.heartbeatWait = 60000 // 60s 无 pong 则重连
|
this.heartbeatWait = 60000 // 60s 无 pong 则重连
|
||||||
this.manualClose = false
|
this.manualClose = false
|
||||||
this.token = ''
|
this.token = ''
|
||||||
|
this.userId = '' // 连接成功后服务端返回的用户ID
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,11 +58,14 @@ class WebSocketManager {
|
|||||||
this.manualClose = false
|
this.manualClose = false
|
||||||
this.isConnecting = true
|
this.isConnecting = true
|
||||||
|
|
||||||
const url = `${WS_HOST}?token=${this.token}`
|
const url = WS_HOST
|
||||||
console.log('[WS] 正在连接...', url)
|
console.log('[WS] 正在连接...')
|
||||||
|
|
||||||
this.socketTask = uni.connectSocket({
|
this.socketTask = uni.connectSocket({
|
||||||
url,
|
url,
|
||||||
|
header: {
|
||||||
|
'Authorization': `Bearer ${this.token}`
|
||||||
|
},
|
||||||
complete: () => {}
|
complete: () => {}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -95,17 +119,17 @@ class WebSocketManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送消息
|
* 发送消息(与 SDK writeClient* 方法格式一致)
|
||||||
* @param {string} type - 消息类型: chat | typing | read | ping
|
* @param {string} mesgType - 消息类型: chat | typing | read | ping
|
||||||
* @param {object} data - 消息数据
|
* @param {object} data - 消息数据
|
||||||
*/
|
*/
|
||||||
send(type, data = {}) {
|
send(mesgType, data = {}) {
|
||||||
const payload = JSON.stringify({ type, data, ts: Date.now() })
|
const payload = JSON.stringify({ mesgType, data })
|
||||||
if (this.isConnected && this.socketTask) {
|
if (this.isConnected && this.socketTask) {
|
||||||
this.socketTask.send({ data: payload })
|
this.socketTask.send({ data: payload })
|
||||||
} else {
|
} else {
|
||||||
// 断线暂存队列
|
// 断线暂存队列(ping 不暂存)
|
||||||
if (type !== 'ping') {
|
if (mesgType !== ClientMesgType.Ping) {
|
||||||
this.messageQueue.push(payload)
|
this.messageQueue.push(payload)
|
||||||
}
|
}
|
||||||
// 尝试重连
|
// 尝试重连
|
||||||
@@ -117,7 +141,7 @@ class WebSocketManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 订阅事件
|
* 订阅事件
|
||||||
* @param {string} event - message | typing | read | ack | connect | disconnect
|
* @param {string} event - message | typing | read | ack | connected | kicked | connect | disconnect
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
on(event, callback) {
|
on(event, callback) {
|
||||||
@@ -143,27 +167,38 @@ class WebSocketManager {
|
|||||||
|
|
||||||
// ========== 内部方法 ==========
|
// ========== 内部方法 ==========
|
||||||
|
|
||||||
/** 处理收到的消息 */
|
/** 处理收到的消息(SDK ServerMesgType 协议) */
|
||||||
_handleMessage(msg) {
|
_handleMessage(msg) {
|
||||||
const { type, data } = msg
|
const { mesgType, data } = msg
|
||||||
switch (type) {
|
switch (mesgType) {
|
||||||
case 'pong':
|
case ServerMesgType.Chat:
|
||||||
this._onPong()
|
|
||||||
break
|
|
||||||
case 'chat':
|
|
||||||
this._emit('message', data)
|
this._emit('message', data)
|
||||||
break
|
break
|
||||||
case 'typing':
|
case ServerMesgType.Typing:
|
||||||
this._emit('typing', data)
|
this._emit('typing', data)
|
||||||
break
|
break
|
||||||
case 'read':
|
case ServerMesgType.Read:
|
||||||
this._emit('read', data)
|
this._emit('read', data)
|
||||||
break
|
break
|
||||||
case 'ack':
|
case ServerMesgType.Ack:
|
||||||
this._emit('ack', data)
|
this._emit('ack', data)
|
||||||
break
|
break
|
||||||
|
case ServerMesgType.Pong:
|
||||||
|
this._onPong()
|
||||||
|
break
|
||||||
|
case ServerMesgType.Connected:
|
||||||
|
// 服务端验证 token 成功后推送,携带 userId
|
||||||
|
this.userId = data && data.userId ? data.userId : ''
|
||||||
|
console.log('[WS] 服务端确认连接, userId:', this.userId)
|
||||||
|
this._emit('connected', data)
|
||||||
|
break
|
||||||
|
case ServerMesgType.Kicked:
|
||||||
|
console.warn('[WS] 被踢下线')
|
||||||
|
this._emit('kicked', data)
|
||||||
|
this.disconnect()
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
console.log('[WS] 未知消息类型', type)
|
console.log('[WS] 未知消息类型', mesgType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +216,7 @@ class WebSocketManager {
|
|||||||
_startHeartbeat() {
|
_startHeartbeat() {
|
||||||
this._stopHeartbeat()
|
this._stopHeartbeat()
|
||||||
this.heartbeatTimer = setInterval(() => {
|
this.heartbeatTimer = setInterval(() => {
|
||||||
this.send('ping')
|
this.send(ClientMesgType.Ping, {})
|
||||||
// 设置超时检测
|
// 设置超时检测
|
||||||
this.heartbeatTimeout = setTimeout(() => {
|
this.heartbeatTimeout = setTimeout(() => {
|
||||||
console.warn('[WS] 心跳超时,触发重连')
|
console.warn('[WS] 心跳超时,触发重连')
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ export default {
|
|||||||
emits: ['item-click'],
|
emits: ['item-click'],
|
||||||
computed: {
|
computed: {
|
||||||
statusLabel() {
|
statusLabel() {
|
||||||
const map = { open: '报名中', ongoing: '进行中', ended: '已结束' }
|
const val = String(this.event.status).toLowerCase()
|
||||||
return map[this.event.status] || '报名中'
|
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
||||||
|
return map[val] || '报名中'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,6 +81,9 @@ export default {
|
|||||||
&.status-open {
|
&.status-open {
|
||||||
background: rgba(94,198,160,0.12);
|
background: rgba(94,198,160,0.12);
|
||||||
}
|
}
|
||||||
|
&.status-upcoming {
|
||||||
|
background: rgba(139,133,184,0.12);
|
||||||
|
}
|
||||||
&.status-ongoing {
|
&.status-ongoing {
|
||||||
background: rgba(232,168,56,0.12);
|
background: rgba(232,168,56,0.12);
|
||||||
}
|
}
|
||||||
@@ -93,6 +97,7 @@ export default {
|
|||||||
font-weight: $fw-medium;
|
font-weight: $fw-medium;
|
||||||
|
|
||||||
.status-open & { color: $mint; }
|
.status-open & { color: $mint; }
|
||||||
|
.status-upcoming & { color: $lavender; }
|
||||||
.status-ongoing & { color: $amber; }
|
.status-ongoing & { color: $amber; }
|
||||||
.status-ended & { color: $text-tertiary; }
|
.status-ended & { color: $text-tertiary; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,15 @@ export default {
|
|||||||
getCatIcon,
|
getCatIcon,
|
||||||
isIconPath,
|
isIconPath,
|
||||||
feelingLabel(f) {
|
feelingLabel(f) {
|
||||||
const map = { sober: '😌 清醒', buzzed: '😊 微醺', tipsy: '😄 小醉', drunk: '🥴 喝多了' }
|
const val = String(f).toLowerCase()
|
||||||
return map[f] || '🍻 喝了点'
|
const map = {
|
||||||
|
tipsy: '😌 微醺',
|
||||||
|
buzzed: '😊 到位',
|
||||||
|
drunk: '🥴 醉了',
|
||||||
|
blackout: '😵 断片',
|
||||||
|
sober: '😌 清醒'
|
||||||
|
}
|
||||||
|
return map[val] || '🍻 喝了点'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
"desc" : "用于选择酒局地点和导航"
|
"desc" : "用于选择酒局地点和导航"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"requiredPrivateInfos" : ["chooseLocation", "getLocation"],
|
||||||
"optimization" : {
|
"optimization" : {
|
||||||
"subPackages" : true
|
"subPackages" : true
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -4,9 +4,9 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"node_modules/HaveADrink": {
|
"node_modules/HaveADrink": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.8",
|
||||||
"resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.4.tgz",
|
"resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.8.tgz",
|
||||||
"integrity": "sha512-kSSpTZrQGjRueftSAopurZcwXGMS3oKQ0mvP3/sePJh7odO5ev9FT31isXX0LLmL1HojzB4nrIeDb/PTdGhYuA==",
|
"integrity": "sha512-YOaqEZg+T1MKz1OtRJc09niyxEwm+52g91Yj0L2aGEy6bK6pTLJYlmREdneIStiniUGHVjL2/U5Qk57lTBAuFw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+809
-55
@@ -14,7 +14,7 @@
|
|||||||
* 6. 社交模块 - 朋友圈动态、点赞
|
* 6. 社交模块 - 朋友圈动态、点赞
|
||||||
|
|
||||||
|
|
||||||
**版本:** v1.0.4
|
**版本:** v1.0.8
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
@@ -350,58 +350,6 @@ const req = new GetFeedReq()
|
|||||||
client.GetFeed(req).then(...).catch(...)
|
client.GetFeed(req).then(...).catch(...)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 点赞
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<font color="green">POST</font> `/api/have_a_drink/v1/communication/records/like`
|
|
||||||
|
|
||||||
#### 请求参数
|
|
||||||
|名称|类型|校验规则|说明|
|
|
||||||
|:-|:-|:-|:-|
|
|
||||||
|id|`string\|number`|required| 记录ID|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 返回值
|
|
||||||
|名称|类型|说明|
|
|
||||||
|:-|:-:|:-|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const req = new LikeRecordReq()
|
|
||||||
client.LikeRecord(req).then(...).catch(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 取消点赞
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<font color="green">POST</font> `/api/have_a_drink/v1/communication/records/unlike`
|
|
||||||
|
|
||||||
#### 请求参数
|
|
||||||
|名称|类型|校验规则|说明|
|
|
||||||
|:-|:-|:-|:-|
|
|
||||||
|id|`string\|number`|required| 记录ID|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### 返回值
|
|
||||||
|名称|类型|说明|
|
|
||||||
|:-|:-:|:-|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const req = new LikeRecordReq()
|
|
||||||
client.UnlikeRecord(req).then(...).catch(...)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 上传照片
|
### 上传照片
|
||||||
|
|
||||||
|
|
||||||
@@ -479,7 +427,7 @@ client.UploadPhotos(req).then(...).catch(...)
|
|||||||
**DrinkItem**
|
**DrinkItem**
|
||||||
|名称|类型|校验规则|说明|
|
|名称|类型|校验规则|说明|
|
||||||
|:-|:-|:-|:-|
|
|:-|:-|:-|:-|
|
||||||
|category|`Category`|required| 酒类分类ID|
|
|category|`Category`|| 酒类分类ID|
|
||||||
|brand|`string`|required| 品牌名称|
|
|brand|`string`|required| 品牌名称|
|
||||||
|product|`string`|| 产品名|
|
|product|`string`|| 产品名|
|
||||||
|amount|`number`|required,gt=0| 饮用量数值|
|
|amount|`number`|required,gt=0| 饮用量数值|
|
||||||
@@ -532,7 +480,7 @@ client.UploadPhotos(req).then(...).catch(...)
|
|||||||
**FoodInfo**
|
**FoodInfo**
|
||||||
|名称|类型|校验规则|说明|
|
|名称|类型|校验规则|说明|
|
||||||
|:-|:-|:-|:-|
|
|:-|:-|:-|:-|
|
||||||
|category|`FoodCategory`|required| 配餐分类ID|
|
|category|`FoodCategory`|| 配餐分类ID|
|
||||||
|name|`string`|| 具体菜名|
|
|name|`string`|| 具体菜名|
|
||||||
|
|
||||||
|
|
||||||
@@ -1193,3 +1141,809 @@ client.UpdatePreferences(req).then(...).catch(...)
|
|||||||
const req = new UploadImageReq()
|
const req = new UploadImageReq()
|
||||||
client.UploadImage(req).then(...).catch(...)
|
client.UploadImage(req).then(...).catch(...)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 动态信息流
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/moments/feeds`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|lastId|`string\|number`|| 最后一条动态ID,用于分页|
|
||||||
|
|pageSize|`number`|| 每页数量|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<Feed>`| 动态列表<br>|
|
||||||
|
|hasMore|`boolean`| 是否还有更多动态<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Feed**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 动态ID|
|
||||||
|
|userId|`string\|number`| 用户ID|
|
||||||
|
|nickname|`string`| 昵称|
|
||||||
|
|avatar|`string`| 头像|
|
||||||
|
|time|`string`| 发布时间|
|
||||||
|
|text|`string`| 动态文本|
|
||||||
|
|drinks|`Array<DrinkTag>`| 酒水标签|
|
||||||
|
|feeling|`Feeling`| 自我感觉|
|
||||||
|
|images|`Array<string>`| 图片列表|
|
||||||
|
|likes|`number`| 点赞数|
|
||||||
|
|liked|`boolean`| 是否点赞|
|
||||||
|
|comments|`number`| 评论数|
|
||||||
|
|
||||||
|
|
||||||
|
**DrinkTag**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|category|`Category`| 酒类分类|
|
||||||
|
|name|`string`| 酒类名称|
|
||||||
|
|amount|`number`| 酒类数量|
|
||||||
|
|
||||||
|
|
||||||
|
**Feeling**
|
||||||
|
|名称|值|类型|说明|
|
||||||
|
|:-|:-:|:-|:-|
|
||||||
|
|Tipsy|"tipsy"|`Feeling`|微醺|
|
||||||
|
|Buzzed|"buzzed"|`Feeling`|到位|
|
||||||
|
|Drunk|"drunk"|`Feeling`|醉了|
|
||||||
|
|Blackout|"blackout"|`Feeling`|断片|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetFeedsReq()
|
||||||
|
client.GetFeeds(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发布动态
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/moments/feeds`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|text|`string`|| 动态文本|
|
||||||
|
|images|`Array<string>`|| 图片列表|
|
||||||
|
|recordId|`string\|number`|| 关联的记录ID|
|
||||||
|
|visibility|`Visibility`|公开: Visibility.Public = "public"<br>仅好友: Visibility.Friends = "friends"<br>仅自己: Visibility.Private = "private"| 可见性|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|success|`boolean`| 是否成功<br>|
|
||||||
|
|feedId|`string\|number`| 动态ID<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new PublishFeedReq()
|
||||||
|
client.PublishFeed(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 点赞
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/moments/feeds/like`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 动态ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|ok|`boolean`| 是否成功<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new LikeFeedReq()
|
||||||
|
client.LikeFeed(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 取消点赞
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/moments/feeds/unlike`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 动态ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|ok|`boolean`| 是否成功<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new UnlikeFeedReq()
|
||||||
|
client.UnlikeFeed(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取评论
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/moments/feeds/comments`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 动态ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<Comment>`| 评论列表<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Comment**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 评论ID|
|
||||||
|
|userId|`string\|number`| 用户ID|
|
||||||
|
|nickname|`string`| 昵称|
|
||||||
|
|avatar|`string`| 头像|
|
||||||
|
|content|`string`| 评论内容|
|
||||||
|
|time|`string`| 评论时间|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetFeedCommentsReq()
|
||||||
|
client.GetFeedComments(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发表评论
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/moments/feeds/comments`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 动态ID|
|
||||||
|
|content|`string`|| 评论内容|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|id|`string\|number`| 评论ID<br>|
|
||||||
|
|userId|`string\|number`| 用户ID<br>|
|
||||||
|
|nickname|`string`| 昵称<br>|
|
||||||
|
|avatar|`string`| 头像<br>|
|
||||||
|
|content|`string`| 评论内容<br>|
|
||||||
|
|time|`string`| 评论时间<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new AddCommentFullReq()
|
||||||
|
client.AddComment(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 酒友管理
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/friends/friends`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|keyword|`string`|| 搜索关键词|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<Friend>`| 分页数据<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Friend**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 酒友ID|
|
||||||
|
|nickname|`string`| 昵称|
|
||||||
|
|avatar|`string`| 头像|
|
||||||
|
|lastDrink|`string\|number`| 最近一次喝的酒|
|
||||||
|
|lastDrinkTime|`string`| 最近一次喝的酒时间|
|
||||||
|
|online|`boolean`| 是否在线|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetFriendsReq()
|
||||||
|
client.GetFriends(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 好友请求管理
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/friends/friend-requests`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<FriendRequest>`| 分页数据<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**FriendRequest**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 好友请求ID|
|
||||||
|
|userId|`string\|number`| 酒友ID|
|
||||||
|
|nickname|`string`| 昵称|
|
||||||
|
|avatar|`string`| 头像|
|
||||||
|
|message|`string`| 消息|
|
||||||
|
|time|`string`| 创建时间|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetFriendRequestsReq()
|
||||||
|
client.GetFriendRequests(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发送好友请求
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/friends/friend-requests`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|userId|`string\|number`|| 酒友ID|
|
||||||
|
|message|`string`|| 消息|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new SendFriendRequestReq()
|
||||||
|
client.SendFriendRequest(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 接受好友请求
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/friends/friend-requests/accept`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 好友请求ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new AcceptFriendRequestReq()
|
||||||
|
client.AcceptFriendRequest(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 删除酒友
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">DELETE</font> `/api/have_a_drink/v1/friends/friends`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|userId|`string\|number`|| 酒友ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new RemoveFriendReq()
|
||||||
|
client.RemoveFriend(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 约酒活动
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/events/events`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|status|`EventStatus`|开放报名: EventStatus.Open = 1<br>待开始: EventStatus.Upcoming = 2<br>进行中: EventStatus.Ongoing = 3<br>已结束: EventStatus.Ended = 4| 状态筛选|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<Event>`| 分页数据<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Event**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 酒局ID|
|
||||||
|
|title|`string`| 酒局标题|
|
||||||
|
|organizer|`User`| 酒局组织者|
|
||||||
|
|time|`string`| 酒局时间|
|
||||||
|
|location|`string`| 酒局地点|
|
||||||
|
|maxPeople|`number`| 最大人数|
|
||||||
|
|joined|`number`| 已报名人数|
|
||||||
|
|participants|`Array<User>`| 已报名用户|
|
||||||
|
|status|`EventStatus`| 酒局状态|
|
||||||
|
|note|`string`| 酒局备注|
|
||||||
|
|isJoined|`boolean`| 是否已报名|
|
||||||
|
|isOrganizer|`boolean`| 是否为组织者|
|
||||||
|
|
||||||
|
|
||||||
|
**User**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 用户ID|
|
||||||
|
|nickname|`string`| 用户昵称|
|
||||||
|
|avatar|`string`| 头像URL|
|
||||||
|
|joinedAt|`string`| 加入时间|
|
||||||
|
|preferences|`UserPreferences`| 用户偏好|
|
||||||
|
|
||||||
|
|
||||||
|
**UserPreferences**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|favoriteCategories|`Array<Category>`| 偏好酒类ID列表|
|
||||||
|
|frequency|`Frequency`| 饮酒频率|
|
||||||
|
|
||||||
|
|
||||||
|
**Frequency**
|
||||||
|
|名称|值|类型|说明|
|
||||||
|
|:-|:-:|:-|:-|
|
||||||
|
|Rarely|"rarely"|`Frequency`|很少|
|
||||||
|
|Sometimes|"sometimes"|`Frequency`|有时|
|
||||||
|
|Often|"often"|`Frequency`|经常|
|
||||||
|
|
||||||
|
|
||||||
|
**EventStatus**
|
||||||
|
|名称|值|类型|说明|
|
||||||
|
|:-|:-:|:-|:-|
|
||||||
|
|Open|1|`EventStatus`|开放报名|
|
||||||
|
|Upcoming|2|`EventStatus`|待开始|
|
||||||
|
|Ongoing|3|`EventStatus`|进行中|
|
||||||
|
|Ended|4|`EventStatus`|已结束|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetEventsReq()
|
||||||
|
client.GetEvents(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取酒局详情
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/events/events/detail`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 酒局ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|id|`string\|number`| 酒局ID<br>|
|
||||||
|
|title|`string`| 酒局标题<br>|
|
||||||
|
|organizer|`User`| 酒局组织者<br>|
|
||||||
|
|time|`string`| 酒局时间<br>|
|
||||||
|
|location|`string`| 酒局地点<br>|
|
||||||
|
|maxPeople|`number`| 最大人数<br>|
|
||||||
|
|joined|`number`| 已报名人数<br>|
|
||||||
|
|participants|`Array<User>`| 已报名用户<br>|
|
||||||
|
|status|`EventStatus`| 酒局状态<br>开放报名: EventStatus.Open = 1<br>待开始: EventStatus.Upcoming = 2<br>进行中: EventStatus.Ongoing = 3<br>已结束: EventStatus.Ended = 4|
|
||||||
|
|note|`string`| 酒局备注<br>|
|
||||||
|
|isJoined|`boolean`| 是否已报名<br>|
|
||||||
|
|isOrganizer|`boolean`| 是否为组织者<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**User**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 用户ID|
|
||||||
|
|nickname|`string`| 用户昵称|
|
||||||
|
|avatar|`string`| 头像URL|
|
||||||
|
|joinedAt|`string`| 加入时间|
|
||||||
|
|preferences|`UserPreferences`| 用户偏好|
|
||||||
|
|
||||||
|
|
||||||
|
**UserPreferences**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|favoriteCategories|`Array<Category>`| 偏好酒类ID列表|
|
||||||
|
|frequency|`Frequency`| 饮酒频率|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetEventDetailReq()
|
||||||
|
client.GetEventDetail(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发起酒局
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/events/events`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|title|`string`|| 酒局标题|
|
||||||
|
|time|`string`|| 酒局时间|
|
||||||
|
|location|`string`|| 酒局地点|
|
||||||
|
|maxPeople|`number`|| 最大人数|
|
||||||
|
|geo|`GeoPoint`|| 坐标信息|
|
||||||
|
|note|`string`|| 酒局备注|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**GeoPoint**
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|latitude|`number`|| 纬度|
|
||||||
|
|longitude|`number`|| 经度|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|eventId|`string\|number`| 酒局ID<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new CreateEventReq()
|
||||||
|
client.CreateEvent(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 报名酒局
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/events/events/join`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 酒局ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|ok|`boolean`| 是否成功<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new JoinEventReq()
|
||||||
|
client.JoinEvent(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 取消报名酒局
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/events/events/quit`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 酒局ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|ok|`boolean`| 是否成功<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new QuitEventReq()
|
||||||
|
client.QuitEvent(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 签到酒局
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/events/events/checkin`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string\|number`|| 酒局ID|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|ok|`boolean`| 是否成功<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new CheckInEventReq()
|
||||||
|
client.CheckInEvent(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取会话列表
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/chat/conversations`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<Conversation>`| 会话列表<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Conversation**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string`| 会话ID|
|
||||||
|
|friendId|`string\|number`| 对方用户ID|
|
||||||
|
|nickname|`string`| 对方昵称|
|
||||||
|
|avatar|`string`| 对方头像URL(可为空)|
|
||||||
|
|lastMessage|`string`| 最后一条消息摘要|
|
||||||
|
|lastTime|`string`| 最后消息时间(ISO 8601)|
|
||||||
|
|unread|`number`| 未读消息数|
|
||||||
|
|online|`boolean`| 对方是否在线|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetConversationsReq()
|
||||||
|
client.GetConversations(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取历史消息
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/chat/messages`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|conversationId|`string`|required| 会话ID|
|
||||||
|
|lastID|`string\|number`|| 游标分页|
|
||||||
|
|pageSize|`number`|gt=0,lte=100| 每页数量,默认20|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|list|`Array<Message>`| 消息列表<br>|
|
||||||
|
|hasMore|`boolean`| 是否还有更多消息<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Message**
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-|:-|
|
||||||
|
|id|`string\|number`| 消息ID|
|
||||||
|
|conversationId|`string`| 会话ID|
|
||||||
|
|senderId|`string\|number`| 发送者ID|
|
||||||
|
|receiverId|`string\|number`| 接收者ID|
|
||||||
|
|type|`MessageType`| 消息类型|
|
||||||
|
|content|`string`| 消息内容|
|
||||||
|
|timestamp|`number`| 发送时间(毫秒时间戳)|
|
||||||
|
|status|`MessageStatus`| 消息状态|
|
||||||
|
|
||||||
|
|
||||||
|
**MessageType**
|
||||||
|
|名称|值|类型|说明|
|
||||||
|
|:-|:-:|:-|:-|
|
||||||
|
|Text|"text"|`MessageType`|文本消息|
|
||||||
|
|Image|"image"|`MessageType`|图片消息|
|
||||||
|
|
||||||
|
|
||||||
|
**MessageStatus**
|
||||||
|
|名称|值|类型|说明|
|
||||||
|
|:-|:-:|:-|:-|
|
||||||
|
|Sending|"sending"|`MessageStatus`|发送中|
|
||||||
|
|Sent|"sent"|`MessageStatus`|已发送|
|
||||||
|
|Delivered|"delivered"|`MessageStatus`|已送达|
|
||||||
|
|Read|"read"|`MessageStatus`|已读|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetMessagesReq()
|
||||||
|
client.GetMessages(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 标记会话已读
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">POST</font> `/api/have_a_drink/v1/chat/conversations/read`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|id|`string`|required| 会话ID(路径参数)|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|success|`boolean`| 是否成功<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new MarkReadReq()
|
||||||
|
client.MarkRead(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取未读消息总数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">GET</font> `/api/have_a_drink/v1/chat/unread-count`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|total|`number`| 未读总数<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new GetUnreadCountReq()
|
||||||
|
client.GetUnreadCount(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注意:连接建立后,服务端会先验证 token,然后推送 connected 事件
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<font color="green">WS</font> `/api/have_a_drink/v1/chat/chat`
|
||||||
|
|
||||||
|
#### 请求参数
|
||||||
|
|名称|类型|校验规则|说明|
|
||||||
|
|:-|:-|:-|:-|
|
||||||
|
|mesgType|`ClientMesgType`|发送消息: ClientMesgType.Chat = "chat"<br>输入状态: ClientMesgType.Typing = "typing"<br>已读回执: ClientMesgType.Read = "read"<br>心跳: ClientMesgType.Ping = "ping"| 消息类型|
|
||||||
|
|data|`ClientMessage`|| 消息数据|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 返回值
|
||||||
|
|名称|类型|说明|
|
||||||
|
|:-|:-:|:-|
|
||||||
|
|mesgType|`ServerMesgType`| 消息类型<br>推送新消息: ServerMesgType.Chat = "chat"<br>对方输入状态: ServerMesgType.Typing = "typing"<br>对方已读回执: ServerMesgType.Read = "read"<br>消息送达确认: ServerMesgType.Ack = "ack"<br>心跳响应: ServerMesgType.Pong = "pong"<br>连接成功: ServerMesgType.Connected = "connected"<br>被踢下线: ServerMesgType.Kicked = "kicked"|
|
||||||
|
|data|`ServerMessage`| 消息数据<br>|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const req = new ChatWebSocket()
|
||||||
|
client.ChatWebSocket(req).then(...).catch(...)
|
||||||
|
```
|
||||||
|
|||||||
+2284
-63
File diff suppressed because it is too large
Load Diff
+2266
-100
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "HaveADrink",
|
"name": "HaveADrink",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "v1.0.4",
|
"version": "v1.0.8",
|
||||||
"description": "喝酒了么 API 服务",
|
"description": "喝酒了么 API 服务",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Generated
+4
-4
@@ -5,13 +5,13 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"HaveADrink": "^1.0.4"
|
"HaveADrink": "^1.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/HaveADrink": {
|
"node_modules/HaveADrink": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.8",
|
||||||
"resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.4.tgz",
|
"resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.8.tgz",
|
||||||
"integrity": "sha512-kSSpTZrQGjRueftSAopurZcwXGMS3oKQ0mvP3/sePJh7odO5ev9FT31isXX0LLmL1HojzB4nrIeDb/PTdGhYuA==",
|
"integrity": "sha512-YOaqEZg+T1MKz1OtRJc09niyxEwm+52g91Yj0L2aGEy6bK6pTLJYlmREdneIStiniUGHVjL2/U5Qk57lTBAuFw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"HaveADrink": "^1.0.4"
|
"HaveADrink": "^1.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -34,7 +34,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import DrinkCard from '../../components/DrinkCard.vue'
|
import DrinkCard from '../../components/DrinkCard.vue'
|
||||||
import client from '../../common/api'
|
import client from '../../common/api'
|
||||||
import { getRecords } from '../../common/mock-data'
|
|
||||||
import { calcStandardCups, unitToMl } from '../../common/utils'
|
import { calcStandardCups, unitToMl } from '../../common/utils'
|
||||||
import { DRINK_CATEGORIES, FOOD_CATEGORIES, DRINK_QUOTES, FEELINGS } from '../../common/constants'
|
import { DRINK_CATEGORIES, FOOD_CATEGORIES, DRINK_QUOTES, FEELINGS } from '../../common/constants'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
@@ -86,7 +85,8 @@ export default {
|
|||||||
loadLocalRecord(id) {
|
loadLocalRecord(id) {
|
||||||
if (!id) return null
|
if (!id) return null
|
||||||
try {
|
try {
|
||||||
const records = getRecords()
|
let records = []
|
||||||
|
try { records = JSON.parse(uni.getStorageSync('drink_records') || '[]') } catch (e) { /* */ }
|
||||||
const found = records.find(r => r.id === id)
|
const found = records.find(r => r.id === id)
|
||||||
if (!found) return null
|
if (!found) return null
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import EmptyState from '../../components/EmptyState.vue'
|
import EmptyState from '../../components/EmptyState.vue'
|
||||||
import { GetConversations } from '../../common/api'
|
import { GetConversations } from '../../common/api'
|
||||||
|
import wsManager from '../../common/websocket'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -83,6 +84,13 @@ export default {
|
|||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
this.loadConversations()
|
this.loadConversations()
|
||||||
|
this.bindWsEvents()
|
||||||
|
},
|
||||||
|
onHide() {
|
||||||
|
this.unbindWsEvents()
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.unbindWsEvents()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadConversations() {
|
async loadConversations() {
|
||||||
@@ -110,6 +118,31 @@ export default {
|
|||||||
},
|
},
|
||||||
goCircle() {
|
goCircle() {
|
||||||
uni.switchTab({ url: '/pages/circle/circle' })
|
uni.switchTab({ url: '/pages/circle/circle' })
|
||||||
|
},
|
||||||
|
|
||||||
|
// === WebSocket 实时更新 ===
|
||||||
|
bindWsEvents() {
|
||||||
|
wsManager.on('message', this.onWsMessage)
|
||||||
|
},
|
||||||
|
unbindWsEvents() {
|
||||||
|
wsManager.off('message', this.onWsMessage)
|
||||||
|
},
|
||||||
|
/** 收到新消息时更新会话列表 */
|
||||||
|
onWsMessage(data) {
|
||||||
|
const convId = data.conversationId
|
||||||
|
const idx = this.conversations.findIndex(c => String(c.id) === String(convId))
|
||||||
|
if (idx >= 0) {
|
||||||
|
const conv = this.conversations[idx]
|
||||||
|
conv.lastMessage = data.content || '[新消息]'
|
||||||
|
conv.lastTime = '刚刚'
|
||||||
|
conv.unread = (conv.unread || 0) + 1
|
||||||
|
// 移动到顶部
|
||||||
|
this.conversations.splice(idx, 1)
|
||||||
|
this.conversations.unshift(conv)
|
||||||
|
} else {
|
||||||
|
// 新会话,重新加载列表
|
||||||
|
this.loadConversations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-27
@@ -119,7 +119,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { GetMessages, MarkRead } from '../../common/api'
|
import { GetMessages, MarkRead, GetConversations } from '../../common/api'
|
||||||
import wsManager from '../../common/websocket'
|
import wsManager from '../../common/websocket'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ export default {
|
|||||||
isOnline: false,
|
isOnline: false,
|
||||||
peerTyping: false,
|
peerTyping: false,
|
||||||
typingTimer: null,
|
typingTimer: null,
|
||||||
page: 1,
|
lastID: 0,
|
||||||
hasMore: true
|
hasMore: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -148,9 +148,7 @@ export default {
|
|||||||
this.friendId = options.friendId || ''
|
this.friendId = options.friendId || ''
|
||||||
this.nickname = decodeURIComponent(options.nickname || '')
|
this.nickname = decodeURIComponent(options.nickname || '')
|
||||||
this.conversationId = options.conversationId || ''
|
this.conversationId = options.conversationId || ''
|
||||||
this.loadMessages()
|
this.initChat()
|
||||||
this.markRead()
|
|
||||||
this.bindWsEvents()
|
|
||||||
// 监听键盘高度变化,动态调整输入栏位置(避免键盘顶起整个页面)
|
// 监听键盘高度变化,动态调整输入栏位置(避免键盘顶起整个页面)
|
||||||
uni.onKeyboardHeightChange(res => {
|
uni.onKeyboardHeightChange(res => {
|
||||||
this.keyboardHeight = res.height
|
this.keyboardHeight = res.height
|
||||||
@@ -176,12 +174,32 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// === 初始化聊天 ===
|
||||||
|
async initChat() {
|
||||||
|
// 如果没有传入 conversationId,通过会话列表查找
|
||||||
|
if (!this.conversationId && this.friendId) {
|
||||||
|
try {
|
||||||
|
const res = await GetConversations()
|
||||||
|
const conv = (res.data.list || []).find(c => String(c.friendId) === String(this.friendId))
|
||||||
|
if (conv) {
|
||||||
|
this.conversationId = conv.id
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
this.loadMessages()
|
||||||
|
this.markRead()
|
||||||
|
this.bindWsEvents()
|
||||||
|
},
|
||||||
// === 数据加载 ===
|
// === 数据加载 ===
|
||||||
async loadMessages() {
|
async loadMessages() {
|
||||||
try {
|
try {
|
||||||
const res = await GetMessages({ conversationId: this.conversationId, page: this.page })
|
const res = await GetMessages({ conversationId: this.conversationId, lastID: this.lastID, pageSize: 20 })
|
||||||
this.messages = res.data.list
|
this.messages = res.data.list || []
|
||||||
this.hasMore = res.data.hasMore
|
this.hasMore = res.data.hasMore
|
||||||
|
// 记录最早一条消息ID作为加载更多游标
|
||||||
|
if (this.messages.length) {
|
||||||
|
this.lastID = this.messages[0].id
|
||||||
|
}
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom()
|
this.scrollToBottom()
|
||||||
})
|
})
|
||||||
@@ -219,12 +237,12 @@ export default {
|
|||||||
/** 收到新消息 */
|
/** 收到新消息 */
|
||||||
onWsMessage(data) {
|
onWsMessage(data) {
|
||||||
// 只处理当前会话的消息
|
// 只处理当前会话的消息
|
||||||
if (data.senderId !== this.friendId) return
|
if (String(data.senderId) !== String(this.friendId)) return
|
||||||
const msg = {
|
const msg = {
|
||||||
id: data.msgId || 'msg_' + Date.now(),
|
id: data.msgId ? String(data.msgId) : 'msg_' + Date.now(),
|
||||||
conversationId: this.conversationId,
|
conversationId: data.conversationId || this.conversationId,
|
||||||
senderId: data.senderId,
|
senderId: data.senderId,
|
||||||
receiverId: 'user_001',
|
receiverId: data.receiverId,
|
||||||
type: data.msgType || 'text',
|
type: data.msgType || 'text',
|
||||||
content: data.content,
|
content: data.content,
|
||||||
timestamp: data.timestamp || Date.now(),
|
timestamp: data.timestamp || Date.now(),
|
||||||
@@ -242,7 +260,7 @@ export default {
|
|||||||
|
|
||||||
/** 对方正在输入 */
|
/** 对方正在输入 */
|
||||||
onWsTyping(data) {
|
onWsTyping(data) {
|
||||||
if (data.senderId !== this.friendId) return
|
if (String(data.senderId) !== String(this.friendId)) return
|
||||||
this.peerTyping = true
|
this.peerTyping = true
|
||||||
if (this.typingTimer) clearTimeout(this.typingTimer)
|
if (this.typingTimer) clearTimeout(this.typingTimer)
|
||||||
this.typingTimer = setTimeout(() => {
|
this.typingTimer = setTimeout(() => {
|
||||||
@@ -252,9 +270,9 @@ export default {
|
|||||||
|
|
||||||
/** 消息送达确认 */
|
/** 消息送达确认 */
|
||||||
onWsAck(data) {
|
onWsAck(data) {
|
||||||
const msg = this.messages.find(m => m.id === data.clientMsgId)
|
const msg = this.messages.find(m => m.id === data.clientMsgId || m.id === String(data.clientMsgId))
|
||||||
if (msg) {
|
if (msg) {
|
||||||
msg.id = data.msgId || msg.id
|
msg.id = data.msgId ? String(data.msgId) : msg.id
|
||||||
msg.status = data.status || 'sent'
|
msg.status = data.status || 'sent'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -268,7 +286,7 @@ export default {
|
|||||||
const msg = {
|
const msg = {
|
||||||
id: clientMsgId,
|
id: clientMsgId,
|
||||||
conversationId: this.conversationId,
|
conversationId: this.conversationId,
|
||||||
senderId: 'user_001',
|
senderId: wsManager.userId || 'self',
|
||||||
receiverId: this.friendId,
|
receiverId: this.friendId,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
content: text,
|
content: text,
|
||||||
@@ -288,13 +306,8 @@ export default {
|
|||||||
receiverId: this.friendId,
|
receiverId: this.friendId,
|
||||||
content: text,
|
content: text,
|
||||||
msgType: 'text',
|
msgType: 'text',
|
||||||
clientMsgId
|
clientMesgId: clientMsgId
|
||||||
})
|
})
|
||||||
|
|
||||||
// 模拟确认(Mock模式下直接标记为已发送)
|
|
||||||
setTimeout(() => {
|
|
||||||
msg.status = 'sent'
|
|
||||||
}, 500)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 重发失败消息 */
|
/** 重发失败消息 */
|
||||||
@@ -303,12 +316,9 @@ export default {
|
|||||||
wsManager.send('chat', {
|
wsManager.send('chat', {
|
||||||
receiverId: this.friendId,
|
receiverId: this.friendId,
|
||||||
content: msg.content,
|
content: msg.content,
|
||||||
msgType: msg.type,
|
msgType: msg.type || 'text',
|
||||||
clientMsgId: msg.id
|
clientMesgId: msg.id
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
|
||||||
msg.status = 'sent'
|
|
||||||
}, 500)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 点击发送按钮时记录时间戳 */
|
/** 点击发送按钮时记录时间戳 */
|
||||||
@@ -334,7 +344,7 @@ export default {
|
|||||||
|
|
||||||
// === 辅助方法 ===
|
// === 辅助方法 ===
|
||||||
isSelf(msg) {
|
isSelf(msg) {
|
||||||
return msg.senderId === 'user_001'
|
return String(msg.senderId) === String(wsManager.userId) || msg.senderId === 'self'
|
||||||
},
|
},
|
||||||
|
|
||||||
showTimeDivider(idx) {
|
showTimeDivider(idx) {
|
||||||
|
|||||||
@@ -62,8 +62,7 @@
|
|||||||
import FeedCard from '../../components/FeedCard.vue'
|
import FeedCard from '../../components/FeedCard.vue'
|
||||||
import CommentItem from '../../components/CommentItem.vue'
|
import CommentItem from '../../components/CommentItem.vue'
|
||||||
import EmptyState from '../../components/EmptyState.vue'
|
import EmptyState from '../../components/EmptyState.vue'
|
||||||
import { GetCircleFeeds, GetFeedComments, AddComment, LikeFeed, UnlikeFeed } from '../../common/api'
|
import { GetFeedComments, AddComment, LikeFeed, UnlikeFeed } from '../../common/api'
|
||||||
import { MOCK_FEEDS } from '../../common/mock-data'
|
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -89,8 +88,10 @@ export default {
|
|||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
},
|
},
|
||||||
async loadFeed() {
|
async loadFeed() {
|
||||||
// 从 Mock 数据中查找对应动态
|
// 从 globalData 中查找对应动态
|
||||||
this.feed = MOCK_FEEDS.find(f => f.id === this.feedId) || MOCK_FEEDS[0]
|
const app = getApp()
|
||||||
|
const feeds = (app.globalData && app.globalData.circleFeeds) || []
|
||||||
|
this.feed = feeds.find(f => String(f.id) === String(this.feedId)) || null
|
||||||
},
|
},
|
||||||
async loadComments() {
|
async loadComments() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view :class="themeClass" class="page-container publish-page">
|
<view :class="themeClass" class="page-container publish-page">
|
||||||
<!-- 顶部导航 -->
|
<!-- 顶部导航 -->
|
||||||
<view class="publish-nav" :style="{ paddingTop: navPaddingTop }">
|
<view class="publish-nav" :style="{ paddingTop: navPaddingTop, paddingRight: navPaddingRight }">
|
||||||
<view class="publish-nav-inner">
|
<view class="publish-nav-inner">
|
||||||
<view class="publish-back" @click="goBack">
|
<view class="publish-back" @click="goBack">
|
||||||
<text class="publish-back-icon">‹</text>
|
<text class="publish-back-icon">‹</text>
|
||||||
@@ -79,16 +79,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { PublishFeed } from '../../common/api'
|
import { PublishFeed, UploadImage } from '../../common/api'
|
||||||
import { getRecords } from '../../common/mock-data'
|
import client from '../../common/api'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [themeMixin],
|
mixins: [themeMixin],
|
||||||
data() {
|
data() {
|
||||||
const menuBtn = uni.getMenuButtonBoundingClientRect()
|
const menuBtn = uni.getMenuButtonBoundingClientRect()
|
||||||
|
const sysInfo = uni.getSystemInfoSync()
|
||||||
return {
|
return {
|
||||||
navPaddingTop: menuBtn.top + 'px',
|
navPaddingTop: menuBtn.top + 'px',
|
||||||
|
navPaddingRight: (sysInfo.windowWidth - menuBtn.left + 8) + 'px',
|
||||||
text: '',
|
text: '',
|
||||||
images: [],
|
images: [],
|
||||||
visibility: 'friends',
|
visibility: 'friends',
|
||||||
@@ -115,10 +117,11 @@ export default {
|
|||||||
goBack() {
|
goBack() {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
},
|
},
|
||||||
loadLastRecord() {
|
async loadLastRecord() {
|
||||||
try {
|
try {
|
||||||
const records = getRecords()
|
const res = await client.GetRecords({ page: 1, pageSize: 5, month: '', mode: '' })
|
||||||
const drank = records.find(r => r.mode === 'drank')
|
const list = res.list || []
|
||||||
|
const drank = list.find(r => String(r.mode) === 'drank' || String(r.mode) === 'Drank')
|
||||||
if (drank) this.lastRecord = drank
|
if (drank) this.lastRecord = drank
|
||||||
} catch (e) { /* ignore */ }
|
} catch (e) { /* ignore */ }
|
||||||
},
|
},
|
||||||
@@ -140,16 +143,29 @@ export default {
|
|||||||
if (!this.canSubmit) return
|
if (!this.canSubmit) return
|
||||||
this.publishing = true
|
this.publishing = true
|
||||||
try {
|
try {
|
||||||
|
// 先上传图片获取远程URL
|
||||||
|
let uploadedUrls = []
|
||||||
|
if (this.images.length) {
|
||||||
|
uni.showLoading({ title: '上传图片中...' })
|
||||||
|
for (const img of this.images) {
|
||||||
|
const upRes = await UploadImage({ filePath: img })
|
||||||
|
if (upRes.data && upRes.data.url) {
|
||||||
|
uploadedUrls.push(upRes.data.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
await PublishFeed({
|
await PublishFeed({
|
||||||
text: this.text,
|
text: this.text,
|
||||||
images: this.images,
|
images: uploadedUrls,
|
||||||
recordId: this.linkRecord && this.lastRecord ? this.lastRecord.id : null,
|
recordId: this.linkRecord && this.lastRecord ? this.lastRecord.id : null,
|
||||||
visibility: this.visibility
|
visibility: this.visibility
|
||||||
})
|
})
|
||||||
uni.showToast({ title: '发布成功 🍻', icon: 'none' })
|
uni.showToast({ title: '发布成功 🍻', icon: 'none' })
|
||||||
setTimeout(() => uni.navigateBack(), 800)
|
setTimeout(() => uni.navigateBack(), 800)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({ title: '发布失败', icon: 'none' })
|
uni.hideLoading()
|
||||||
|
// 全局拦截器已弹出错误提示
|
||||||
}
|
}
|
||||||
this.publishing = false
|
this.publishing = false
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-9
@@ -121,6 +121,7 @@ import FriendItem from '../../components/FriendItem.vue'
|
|||||||
import EventCard from '../../components/EventCard.vue'
|
import EventCard from '../../components/EventCard.vue'
|
||||||
import EmptyState from '../../components/EmptyState.vue'
|
import EmptyState from '../../components/EmptyState.vue'
|
||||||
import { GetCircleFeeds, LikeFeed, UnlikeFeed, GetFriends, GetFriendRequests, GetEvents, GetUnreadCount } from '../../common/api'
|
import { GetCircleFeeds, LikeFeed, UnlikeFeed, GetFriends, GetFriendRequests, GetEvents, GetUnreadCount } from '../../common/api'
|
||||||
|
import wsManager from '../../common/websocket'
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -136,7 +137,7 @@ export default {
|
|||||||
headerPaddingRight: (sysInfo.windowWidth - menuBtn.left + 8) + 'px',
|
headerPaddingRight: (sysInfo.windowWidth - menuBtn.left + 8) + 'px',
|
||||||
// 动态
|
// 动态
|
||||||
feeds: [],
|
feeds: [],
|
||||||
page: 1,
|
lastId: 0,
|
||||||
hasMore: true,
|
hasMore: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
@@ -154,6 +155,14 @@ export default {
|
|||||||
this.loadFriends()
|
this.loadFriends()
|
||||||
this.loadEvents()
|
this.loadEvents()
|
||||||
this.loadUnread()
|
this.loadUnread()
|
||||||
|
this._wsHandler = () => { this.unreadTotal++ }
|
||||||
|
wsManager.on('message', this._wsHandler)
|
||||||
|
},
|
||||||
|
onHide() {
|
||||||
|
if (this._wsHandler) {
|
||||||
|
wsManager.off('message', this._wsHandler)
|
||||||
|
this._wsHandler = null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
switchTab(i) {
|
switchTab(i) {
|
||||||
@@ -163,20 +172,26 @@ export default {
|
|||||||
async loadFeeds(reset = false) {
|
async loadFeeds(reset = false) {
|
||||||
if (this.loading) return
|
if (this.loading) return
|
||||||
if (reset) {
|
if (reset) {
|
||||||
this.page = 1
|
this.lastId = 0
|
||||||
this.hasMore = true
|
this.hasMore = true
|
||||||
}
|
}
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
const res = await GetCircleFeeds({ page: this.page, pageSize: 10 })
|
const res = await GetCircleFeeds({ lastId: this.lastId, pageSize: 10 })
|
||||||
const data = res.data
|
const data = res.data
|
||||||
if (reset) {
|
if (reset) {
|
||||||
this.feeds = data.list
|
this.feeds = data.list || []
|
||||||
} else {
|
} else {
|
||||||
this.feeds = [...this.feeds, ...data.list]
|
this.feeds = [...this.feeds, ...(data.list || [])]
|
||||||
}
|
}
|
||||||
this.hasMore = data.hasMore
|
this.hasMore = data.hasMore
|
||||||
this.page++
|
// 记录最后一条ID作为下次请求的游标
|
||||||
|
if (data.list && data.list.length) {
|
||||||
|
this.lastId = data.list[data.list.length - 1].id
|
||||||
|
}
|
||||||
|
// 存入 globalData 供详情页查找
|
||||||
|
getApp().globalData = getApp().globalData || {}
|
||||||
|
getApp().globalData.circleFeeds = this.feeds
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('加载动态失败', e)
|
console.warn('加载动态失败', e)
|
||||||
}
|
}
|
||||||
@@ -230,10 +245,8 @@ export default {
|
|||||||
if (this._navLock) return
|
if (this._navLock) return
|
||||||
this._navLock = true
|
this._navLock = true
|
||||||
setTimeout(() => { this._navLock = false }, 600)
|
setTimeout(() => { this._navLock = false }, 600)
|
||||||
// 根据 friendId 查找对应会话 (f001 -> conv_001)
|
|
||||||
const convId = 'conv_' + friend.id.replace('f', '')
|
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/chat/chat?friendId=${friend.id}&nickname=${encodeURIComponent(friend.nickname)}&conversationId=${convId}`
|
url: `/pages/chat/chat?friendId=${friend.id}&nickname=${encodeURIComponent(friend.nickname)}`
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
goChatList() {
|
goChatList() {
|
||||||
|
|||||||
@@ -115,12 +115,16 @@ export default {
|
|||||||
mixins: [themeMixin],
|
mixins: [themeMixin],
|
||||||
data() {
|
data() {
|
||||||
const menuBtn = uni.getMenuButtonBoundingClientRect()
|
const menuBtn = uni.getMenuButtonBoundingClientRect()
|
||||||
|
const now = new Date()
|
||||||
|
const pad = n => String(n).padStart(2, '0')
|
||||||
|
const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
|
||||||
|
const curTime = `${pad(now.getHours())}:${pad(now.getMinutes())}`
|
||||||
return {
|
return {
|
||||||
navPaddingTop: menuBtn.top + 'px',
|
navPaddingTop: menuBtn.top + 'px',
|
||||||
form: {
|
form: {
|
||||||
title: '',
|
title: '',
|
||||||
date: '',
|
date: today,
|
||||||
time: '',
|
time: curTime,
|
||||||
location: '',
|
location: '',
|
||||||
address: '',
|
address: '',
|
||||||
latitude: null,
|
latitude: null,
|
||||||
@@ -200,7 +204,7 @@ export default {
|
|||||||
try {
|
try {
|
||||||
await CreateEvent({
|
await CreateEvent({
|
||||||
title: this.form.title,
|
title: this.form.title,
|
||||||
time: `${this.form.date} ${this.form.time}`,
|
time: `${this.form.date} ${this.form.time}:00`,
|
||||||
location: this.form.location,
|
location: this.form.location,
|
||||||
address: this.form.address,
|
address: this.form.address,
|
||||||
latitude: this.form.latitude,
|
latitude: this.form.latitude,
|
||||||
@@ -211,7 +215,7 @@ export default {
|
|||||||
uni.showToast({ title: '酒局已发布 🎉', icon: 'none' })
|
uni.showToast({ title: '酒局已发布 🎉', icon: 'none' })
|
||||||
setTimeout(() => uni.navigateBack(), 800)
|
setTimeout(() => uni.navigateBack(), 800)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({ title: '发布失败', icon: 'none' })
|
// 全局拦截器已弹出错误提示
|
||||||
}
|
}
|
||||||
this.submitting = false
|
this.submitting = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,14 +98,14 @@
|
|||||||
<!-- 底部操作栏 -->
|
<!-- 底部操作栏 -->
|
||||||
<view class="evt-action-bar" v-if="event">
|
<view class="evt-action-bar" v-if="event">
|
||||||
<button
|
<button
|
||||||
v-if="event.status === 'open' && !event.isJoined && !event.isOrganizer"
|
v-if="(event.status === 'open' || event.status === 'upcoming') && !event.isJoined && !event.isOrganizer"
|
||||||
class="evt-btn evt-btn-primary"
|
class="evt-btn evt-btn-primary"
|
||||||
@click="handleJoin"
|
@click="handleJoin"
|
||||||
>
|
>
|
||||||
报名参加
|
报名参加
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="event.status === 'open' && event.isJoined && !event.isOrganizer"
|
v-if="(event.status === 'open' || event.status === 'upcoming') && event.isJoined && !event.isOrganizer"
|
||||||
class="evt-btn evt-btn-ghost"
|
class="evt-btn evt-btn-ghost"
|
||||||
@click="handleQuit"
|
@click="handleQuit"
|
||||||
>
|
>
|
||||||
@@ -145,8 +145,9 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
statusLabel() {
|
statusLabel() {
|
||||||
if (!this.event) return ''
|
if (!this.event) return ''
|
||||||
const map = { open: '报名中', ongoing: '进行中', ended: '已结束' }
|
const val = String(this.event.status).toLowerCase()
|
||||||
return map[this.event.status] || '报名中'
|
const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' }
|
||||||
|
return map[val] || '报名中'
|
||||||
},
|
},
|
||||||
eventMarkers() {
|
eventMarkers() {
|
||||||
if (!this.event || !this.event.latitude) return []
|
if (!this.event || !this.event.latitude) return []
|
||||||
@@ -296,6 +297,7 @@ export default {
|
|||||||
margin-bottom: $sp-lg;
|
margin-bottom: $sp-lg;
|
||||||
|
|
||||||
&.status-open { background: rgba(94,198,160,0.12); }
|
&.status-open { background: rgba(94,198,160,0.12); }
|
||||||
|
&.status-upcoming { background: rgba(139,133,184,0.12); }
|
||||||
&.status-ongoing { background: rgba(232,168,56,0.12); }
|
&.status-ongoing { background: rgba(232,168,56,0.12); }
|
||||||
&.status-ended { background: $bg-elevated; }
|
&.status-ended { background: $bg-elevated; }
|
||||||
}
|
}
|
||||||
@@ -305,6 +307,7 @@ export default {
|
|||||||
font-weight: $fw-medium;
|
font-weight: $fw-medium;
|
||||||
|
|
||||||
.status-open & { color: $mint; }
|
.status-open & { color: $mint; }
|
||||||
|
.status-upcoming & { color: $lavender; }
|
||||||
.status-ongoing & { color: $amber; }
|
.status-ongoing & { color: $amber; }
|
||||||
.status-ended & { color: $text-tertiary; }
|
.status-ended & { color: $text-tertiary; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,7 +316,6 @@ import {
|
|||||||
} from '../../common/constants'
|
} from '../../common/constants'
|
||||||
import { calcStandardCups, unitToMl, formatDate, getCatIcon, isIconPath } from '../../common/utils'
|
import { calcStandardCups, unitToMl, formatDate, getCatIcon, isIconPath } from '../../common/utils'
|
||||||
import client from '../../common/api'
|
import client from '../../common/api'
|
||||||
import { addRecord } from '../../common/mock-data'
|
|
||||||
import themeMixin from '../../common/theme-mixin'
|
import themeMixin from '../../common/theme-mixin'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -562,7 +561,13 @@ export default {
|
|||||||
console.warn('创建记录失败,降级本地保存:', e)
|
console.warn('创建记录失败,降级本地保存:', e)
|
||||||
uni.showToast({ title: e.msg || '保存失败,已本地保存', icon: 'none', duration: 2000 })
|
uni.showToast({ title: e.msg || '保存失败,已本地保存', icon: 'none', duration: 2000 })
|
||||||
// 降级:本地保存
|
// 降级:本地保存
|
||||||
const saved = addRecord(recordData)
|
recordData.id = `record_${Date.now()}`
|
||||||
|
recordData.createdAt = new Date().toISOString()
|
||||||
|
let localRecords = []
|
||||||
|
try { localRecords = JSON.parse(uni.getStorageSync('drink_records') || '[]') } catch (e) { /* */ }
|
||||||
|
localRecords.unshift(recordData)
|
||||||
|
uni.setStorageSync('drink_records', JSON.stringify(localRecords))
|
||||||
|
const saved = recordData
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages/card/card?recordId=${saved.id}`
|
url: `/pages/card/card?recordId=${saved.id}`
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user