fix(core): comprehensive fix for state loss across activity, ticket, workspace, favorites and community
这个提交包含在:
@@ -169,9 +169,10 @@ export async function leaveActivity(activityId, userId = 2001) {
|
||||
/**
|
||||
* 获取“我的组局”(发起的 + 报名的)
|
||||
*/
|
||||
export async function getMyActivities(userId = 1001) {
|
||||
export async function getMyActivities(userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.get(`/users/${userId}/my-activities`);
|
||||
const res = await http.get(`/users/${uid}/my-activities`);
|
||||
if (res) {
|
||||
return {
|
||||
created: (res.created || []).map(formatActivityData),
|
||||
@@ -477,7 +478,13 @@ export async function likePost(id, action, userId) {
|
||||
* 规范化用户ID,自动兼容历史测试账号映射 (将虚拟陪伴师 101 映射至真实用户 2006)
|
||||
*/
|
||||
function normalizeUserId(uid) {
|
||||
const n = Number(uid);
|
||||
let target = uid;
|
||||
if (!target && target !== 0) {
|
||||
try {
|
||||
target = uni.getStorageSync('userId');
|
||||
} catch (_) {}
|
||||
}
|
||||
const n = Number(target);
|
||||
if (n === 101) return 2006;
|
||||
return (!isNaN(n) && n > 0) ? n : 1001;
|
||||
}
|
||||
@@ -952,9 +959,10 @@ export async function updateExpertServiceStatus(expertId = 101, serviceId, statu
|
||||
/**
|
||||
* 获取用户收藏的陪玩列表
|
||||
*/
|
||||
export async function getFavorites(userId = 1001) {
|
||||
export async function getFavorites(userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.get(`/favorites?userId=${userId}`);
|
||||
const res = await http.get(`/favorites?userId=${uid}`);
|
||||
return res || [];
|
||||
} catch (err) {
|
||||
console.warn('[API] 获取收藏列表异常:', err.message);
|
||||
@@ -965,9 +973,10 @@ export async function getFavorites(userId = 1001) {
|
||||
/**
|
||||
* 探测是否已收藏特定陪玩
|
||||
*/
|
||||
export async function checkFavorite(expertId, userId = 1001) {
|
||||
export async function checkFavorite(expertId, userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.get(`/favorites/check?userId=${userId}&expertId=${expertId}`);
|
||||
const res = await http.get(`/favorites/check?userId=${uid}&expertId=${expertId}`);
|
||||
return res && res.isFavorite;
|
||||
} catch (err) {
|
||||
console.warn('[API] 探测收藏状态异常:', err.message);
|
||||
@@ -978,9 +987,10 @@ export async function checkFavorite(expertId, userId = 1001) {
|
||||
/**
|
||||
* 添加收藏
|
||||
*/
|
||||
export async function addFavorite(expertId, userId = 1001) {
|
||||
export async function addFavorite(expertId, userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.post('/favorites', { userId, expertId });
|
||||
const res = await http.post('/favorites', { userId: uid, expertId });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.warn('[API] 添加收藏异常:', err.message);
|
||||
@@ -991,9 +1001,10 @@ export async function addFavorite(expertId, userId = 1001) {
|
||||
/**
|
||||
* 取消收藏
|
||||
*/
|
||||
export async function removeFavorite(expertId, userId = 1001) {
|
||||
export async function removeFavorite(expertId, userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.delete(`/favorites/${expertId}?userId=${userId}`);
|
||||
const res = await http.delete(`/favorites/${expertId}?userId=${uid}`);
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.warn('[API] 取消收藏异常:', err.message);
|
||||
@@ -1004,9 +1015,10 @@ export async function removeFavorite(expertId, userId = 1001) {
|
||||
/**
|
||||
* 获取分销团队列表 (一级与二级成员)
|
||||
*/
|
||||
export async function getDistributionTeam(userId = 1001) {
|
||||
export async function getDistributionTeam(userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.get(`/distribution/team?userId=${userId}`);
|
||||
const res = await http.get(`/distribution/team?userId=${uid}`);
|
||||
return res || { directCount: 0, indirectCount: 0, totalTeamCount: 0, directList: [], indirectList: [] };
|
||||
} catch (err) {
|
||||
console.warn('[API] 获取分销团队异常:', err.message);
|
||||
@@ -1017,9 +1029,10 @@ export async function getDistributionTeam(userId = 1001) {
|
||||
/**
|
||||
* 获取分销返佣订单记录
|
||||
*/
|
||||
export async function getDistributionRecords(userId = 1001) {
|
||||
export async function getDistributionRecords(userId) {
|
||||
const uid = normalizeUserId(userId);
|
||||
try {
|
||||
const res = await http.get(`/distribution/records?userId=${userId}`);
|
||||
const res = await http.get(`/distribution/records?userId=${uid}`);
|
||||
return res || [];
|
||||
} catch (err) {
|
||||
console.warn('[API] 获取分销返佣记录异常:', err.message);
|
||||
|
||||
@@ -137,12 +137,12 @@
|
||||
<text class="sec-sub">满员即成局</text>
|
||||
</view>
|
||||
<view class="member-list" v-if="activity.participants && activity.participants.length > 0">
|
||||
<view v-for="(p, index) in activity.participants" :key="p.id" class="member-item" :class="{ 'my-member-item': Number(p.userId) === CURRENT_USER_ID }">
|
||||
<view v-for="(p, index) in activity.participants" :key="p.id" class="member-item" :class="{ 'my-member-item': Number(p.userId) === currentUserId }">
|
||||
<image class="member-avatar" :src="p.avatar" mode="aspectFill" />
|
||||
<view class="member-info">
|
||||
<view class="member-top-line">
|
||||
<text class="member-name">{{ p.nickname }}</text>
|
||||
<text class="my-badge" v-if="Number(p.userId) === CURRENT_USER_ID">我的席位</text>
|
||||
<text class="my-badge" v-if="Number(p.userId) === currentUserId">我的席位</text>
|
||||
<text class="member-price-tag" v-if="p.priceAtJoin">
|
||||
出价: ¥{{ p.priceAtJoin }}
|
||||
</text>
|
||||
@@ -158,7 +158,7 @@
|
||||
<view class="checkin-badge" :class="{ checked: p.checkedIn }">
|
||||
{{ p.checkedIn ? '已核销到场' : '待现场核销' }}
|
||||
</view>
|
||||
<text class="btn-micro-leave btn-active" v-if="Number(p.userId) === CURRENT_USER_ID && !p.checkedIn" @click.stop="handleLeave">
|
||||
<text class="btn-micro-leave btn-active" v-if="Number(p.userId) === currentUserId && !p.checkedIn" @click.stop="handleLeave">
|
||||
退席
|
||||
</text>
|
||||
</view>
|
||||
@@ -384,18 +384,26 @@ import {
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||||
|
||||
const CURRENT_USER_ID = 1001;
|
||||
const currentUserId = computed(() => {
|
||||
try {
|
||||
return Number(uni.getStorageSync('userId')) || 1001;
|
||||
} catch (_) {
|
||||
return 1001;
|
||||
}
|
||||
});
|
||||
const currentActId = ref('1');
|
||||
const verifyCodeInput = ref('');
|
||||
const currentThemeKey = ref(getCurrentThemeKey());
|
||||
|
||||
const isHost = computed(() => {
|
||||
if (!activity.value) return false;
|
||||
return Number(activity.value.creatorId) === CURRENT_USER_ID;
|
||||
return Number(activity.value.creatorId) === currentUserId.value;
|
||||
});
|
||||
|
||||
const myParticipantInfo = computed(() => {
|
||||
if (!activity.value || !activity.value.participants) return null;
|
||||
return activity.value.participants.find(p => Number(p.userId) === CURRENT_USER_ID || Number(p.id) === CURRENT_USER_ID);
|
||||
const uid = currentUserId.value;
|
||||
return activity.value.participants.find(p => Number(p.userId) === uid || Number(p.id) === uid);
|
||||
});
|
||||
|
||||
const isJoined = computed(() => !!myParticipantInfo.value);
|
||||
@@ -421,6 +429,9 @@ onMounted(() => {
|
||||
onShow(() => {
|
||||
currentThemeKey.value = getCurrentThemeKey();
|
||||
applyThemeToNativeUI(currentThemeKey.value);
|
||||
if (currentActId.value) {
|
||||
loadLiveDetail(currentActId.value);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -458,6 +469,7 @@ const loadLiveDetail = async (id) => {
|
||||
|
||||
onLoad((query) => {
|
||||
const targetId = (query && query.id) ? query.id : '1';
|
||||
currentActId.value = targetId;
|
||||
loadLiveDetail(targetId);
|
||||
});
|
||||
|
||||
@@ -639,7 +651,7 @@ const handleHostVerify = async () => {
|
||||
}
|
||||
try {
|
||||
uni.showLoading({ title: '正在核销签到...' });
|
||||
await verifyActivityCheckin(activity.value.id, code, CURRENT_USER_ID);
|
||||
await verifyActivityCheckin(activity.value.id, code, currentUserId.value);
|
||||
uni.hideLoading();
|
||||
verifyCodeInput.value = '';
|
||||
uni.showToast({ title: '核销成功!搭子已签到', icon: 'success' });
|
||||
@@ -675,7 +687,7 @@ const handleHostCancel = () => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({ title: '正在解散并全额退款...' });
|
||||
await cancelActivity(activity.value.id, CURRENT_USER_ID, '局长主动解散组局');
|
||||
await cancelActivity(activity.value.id, currentUserId.value, '局长主动解散组局');
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '组局已成功解散', icon: 'success' });
|
||||
activity.value.status = 'CANCELLED';
|
||||
@@ -703,7 +715,7 @@ const handleLeave = () => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({ title: '正在办理退席...' });
|
||||
await leaveActivity(activity.value.id, CURRENT_USER_ID);
|
||||
await leaveActivity(activity.value.id, currentUserId.value);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '已退席,款项已退回钱包', icon: 'success' });
|
||||
loadLiveDetail(activity.value.id);
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import { getActivityDetail, verifyActivityCheckin, getMyActivities, leaveActivity } from '@/common/api.js';
|
||||
import { encodeCode128B } from '@/common/barcode.js';
|
||||
import { generateQRCodeMatrix } from '@/common/qrcode.js';
|
||||
@@ -140,6 +140,14 @@ const isCheckedIn = ref(false);
|
||||
const currentTime = ref('');
|
||||
const hasTicket = ref(true);
|
||||
|
||||
const currentUserId = computed(() => {
|
||||
try {
|
||||
return Number(uni.getStorageSync('userId')) || 1001;
|
||||
} catch (_) {
|
||||
return 1001;
|
||||
}
|
||||
});
|
||||
|
||||
const ticketSn = computed(() => {
|
||||
const actId = ticketData.value && ticketData.value.id ? ticketData.value.id : '101';
|
||||
return `TICKET-${actId}-${verifyCode.value}`;
|
||||
@@ -170,7 +178,9 @@ const updateTime = () => {
|
||||
currentTime.value = `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
|
||||
};
|
||||
|
||||
onLoad(async (query) => {
|
||||
let savedQuery = null;
|
||||
|
||||
const loadTicketData = async (query = savedQuery) => {
|
||||
if (query && query.code) {
|
||||
verifyCode.value = String(query.code);
|
||||
}
|
||||
@@ -199,17 +209,18 @@ onLoad(async (query) => {
|
||||
|
||||
// 2. 若无传参,真实拉取当前用户的已报名组局列表
|
||||
try {
|
||||
const myActs = await getMyActivities(1001);
|
||||
const uid = currentUserId.value;
|
||||
const myActs = await getMyActivities(uid);
|
||||
const joinedList = myActs.joined || [];
|
||||
if (joinedList.length > 0) {
|
||||
let targetAct = joinedList.find(act => {
|
||||
const myP = (act.participants || []).find(p => p.userId === 1001 || p.nickname === '同城局长' || p.verifyCode);
|
||||
const myP = (act.participants || []).find(p => Number(p.userId) === uid || p.nickname === '同城局长' || p.verifyCode);
|
||||
return myP && !myP.checkedIn;
|
||||
}) || joinedList[0];
|
||||
|
||||
ticketData.value = targetAct;
|
||||
if (targetAct.participants && targetAct.participants.length > 0) {
|
||||
const myP = targetAct.participants.find(p => p.userId === 1001 || p.verifyCode);
|
||||
const myP = targetAct.participants.find(p => Number(p.userId) === uid || p.verifyCode);
|
||||
if (myP && myP.verifyCode) {
|
||||
verifyCode.value = String(myP.verifyCode);
|
||||
isCheckedIn.value = Boolean(myP.checkedIn);
|
||||
@@ -235,8 +246,16 @@ onLoad(async (query) => {
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
};
|
||||
|
||||
onLoad(async (query) => {
|
||||
savedQuery = query;
|
||||
await loadTicketData(query);
|
||||
});
|
||||
|
||||
onShow(async () => {
|
||||
await loadTicketData();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
updateTime();
|
||||
@@ -251,7 +270,7 @@ const toggleCheckIn = async () => {
|
||||
if (!isCheckedIn.value) {
|
||||
try {
|
||||
uni.showLoading({ title: '正在核销签到...' });
|
||||
await verifyActivityCheckin(ticketData.value.id, verifyCode.value, 1001);
|
||||
await verifyActivityCheckin(ticketData.value.id, verifyCode.value, currentUserId.value);
|
||||
uni.hideLoading();
|
||||
isCheckedIn.value = true;
|
||||
uni.showToast({
|
||||
@@ -291,7 +310,7 @@ const handleCancelTicket = () => {
|
||||
try {
|
||||
uni.showLoading({ title: '正在办理退票...' });
|
||||
const actId = ticketData.value && ticketData.value.id ? ticketData.value.id : 'act_101';
|
||||
await leaveActivity(actId, 1001);
|
||||
await leaveActivity(actId, currentUserId.value);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: '退票成功,款项已退回钱包', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -80,8 +80,14 @@
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
<button v-else class="join-btn btn-active" @click="handleJoinParty(post)">
|
||||
{{ post.actionText || '聊一聊' }}
|
||||
<button
|
||||
v-else
|
||||
class="join-btn btn-active"
|
||||
:class="{ 'btn-applied': post.hasApplied }"
|
||||
:disabled="post.hasApplied"
|
||||
@click="handleJoinParty(post)"
|
||||
>
|
||||
{{ post.hasApplied ? '已申请' : (post.actionText || '聊一聊') }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -380,10 +386,18 @@ const loadRemotePosts = async () => {
|
||||
if (list && list.length > 0) {
|
||||
let localLikedIds = uni.getStorageSync('COMMUNITY_LIKED_POST_IDS') || [];
|
||||
if (!Array.isArray(localLikedIds)) localLikedIds = [];
|
||||
posts.value = list.map(item => ({
|
||||
...item,
|
||||
liked: item.liked !== undefined ? item.liked : localLikedIds.includes(String(item.id))
|
||||
}));
|
||||
let localAppliedIds = uni.getStorageSync('COMMUNITY_APPLIED_POST_IDS') || [];
|
||||
if (!Array.isArray(localAppliedIds)) localAppliedIds = [];
|
||||
|
||||
posts.value = list.map(item => {
|
||||
const isApplied = localAppliedIds.includes(String(item.id));
|
||||
return {
|
||||
...item,
|
||||
liked: item.liked !== undefined ? item.liked : localLikedIds.includes(String(item.id)),
|
||||
hasApplied: isApplied,
|
||||
actionText: isApplied ? '已申请' : (item.actionText || '加入车队')
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[Community] 获取动态列表失败,保持本地列表:', err.message);
|
||||
@@ -416,20 +430,29 @@ const feedTabs = ref([
|
||||
|
||||
const currentTab = ref('all');
|
||||
|
||||
const syncInitialLikedState = (rawPosts) => {
|
||||
const syncInitialPostStates = (rawPosts) => {
|
||||
try {
|
||||
let localLikedIds = uni.getStorageSync('COMMUNITY_LIKED_POST_IDS') || [];
|
||||
if (!Array.isArray(localLikedIds) || localLikedIds.length === 0) return rawPosts;
|
||||
return rawPosts.map(p => ({
|
||||
...p,
|
||||
liked: localLikedIds.includes(String(p.id)) ? true : p.liked
|
||||
}));
|
||||
if (!Array.isArray(localLikedIds)) localLikedIds = [];
|
||||
let localAppliedIds = uni.getStorageSync('COMMUNITY_APPLIED_POST_IDS') || [];
|
||||
if (!Array.isArray(localAppliedIds)) localAppliedIds = [];
|
||||
|
||||
return rawPosts.map(p => {
|
||||
const isApplied = localAppliedIds.includes(String(p.id));
|
||||
return {
|
||||
...p,
|
||||
liked: localLikedIds.includes(String(p.id)) ? true : p.liked,
|
||||
hasApplied: isApplied,
|
||||
actionText: isApplied ? '已申请' : p.actionText
|
||||
};
|
||||
});
|
||||
} catch (_) {
|
||||
return rawPosts;
|
||||
}
|
||||
};
|
||||
const syncInitialLikedState = syncInitialPostStates;
|
||||
|
||||
const posts = ref(syncInitialLikedState([
|
||||
const posts = ref(syncInitialPostStates([
|
||||
{
|
||||
id: 1,
|
||||
author: '小甜橙',
|
||||
@@ -658,6 +681,13 @@ const handleDeletePost = (post) => {
|
||||
};
|
||||
|
||||
const handleJoinParty = (post) => {
|
||||
if (post.hasApplied) {
|
||||
uni.showToast({
|
||||
title: '已提交过约伴申请',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showModal({
|
||||
title: '搭子互动申请',
|
||||
content: `确定向【${post.author}】发起同城约伴 / 上车请求?`,
|
||||
@@ -666,24 +696,33 @@ const handleJoinParty = (post) => {
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({ title: '正在提交申请...' });
|
||||
const uid = Number(uni.getStorageSync('userId')) || 1001;
|
||||
const userInfo = uni.getStorageSync('userInfo') || {};
|
||||
const applicantName = userInfo.nickname || '搭子玩家';
|
||||
const applicantAvatar = userInfo.avatar || 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=120';
|
||||
try {
|
||||
await applyPostParty(post.id, {
|
||||
applicantId: 1001,
|
||||
applicantName: '电竞玩家 (我)',
|
||||
applicantAvatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=120'
|
||||
});
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '申请已送达,等待房主确认!',
|
||||
icon: 'success'
|
||||
});
|
||||
} catch (_) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '申请已送达,等待房主确认!',
|
||||
icon: 'success'
|
||||
applicantId: uid,
|
||||
applicantName,
|
||||
applicantAvatar
|
||||
});
|
||||
} catch (_) {}
|
||||
|
||||
post.hasApplied = true;
|
||||
post.actionText = '已申请';
|
||||
let localAppliedIds = uni.getStorageSync('COMMUNITY_APPLIED_POST_IDS') || [];
|
||||
if (!Array.isArray(localAppliedIds)) localAppliedIds = [];
|
||||
const strId = String(post.id);
|
||||
if (!localAppliedIds.includes(strId)) {
|
||||
localAppliedIds.push(strId);
|
||||
uni.setStorageSync('COMMUNITY_APPLIED_POST_IDS', localAppliedIds);
|
||||
}
|
||||
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '申请已送达,等待房主确认!',
|
||||
icon: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1212,6 +1251,13 @@ const submitPublishPost = async () => {
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
|
||||
&.btn-applied {
|
||||
background: rgba(148, 163, 184, 0.15);
|
||||
color: #94A3B8;
|
||||
border-color: rgba(148, 163, 184, 0.3);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1769,6 +1815,12 @@ const submitPublishPost = async () => {
|
||||
background: rgba(234, 88, 12, 0.1);
|
||||
color: #EA580C;
|
||||
border-color: rgba(234, 88, 12, 0.3);
|
||||
|
||||
&.btn-applied {
|
||||
background: #F1F5F9;
|
||||
color: #94A3B8;
|
||||
border-color: #CBD5E1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
import SkeletonScreen from '@/components/SkeletonScreen.vue';
|
||||
import { getCompanionDetail, getExpertReviews, replyOrderReview, submitReport as submitReportApi, checkFavorite, addFavorite as addFavApi, removeFavorite as removeFavApi } from '@/common/api.js';
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
@@ -268,10 +268,23 @@ const expert = ref({
|
||||
});
|
||||
const selectedServiceId = ref(null);
|
||||
const isPlayingVoice = ref(false);
|
||||
let currentExpertTargetId = null;
|
||||
|
||||
const refreshFavoriteState = async () => {
|
||||
if (!currentExpertTargetId) return;
|
||||
try {
|
||||
const favStatus = await checkFavorite(currentExpertTargetId);
|
||||
isFav.value = Boolean(favStatus);
|
||||
} catch (_) {}
|
||||
};
|
||||
|
||||
onShow(() => {
|
||||
refreshFavoriteState();
|
||||
});
|
||||
|
||||
const toggleFavorite = async () => {
|
||||
try {
|
||||
const targetId = Number(expert.value.id || 101);
|
||||
const targetId = Number(expert.value.id || currentExpertTargetId || 101);
|
||||
if (isFav.value) {
|
||||
await removeFavApi(targetId);
|
||||
isFav.value = false;
|
||||
@@ -315,6 +328,7 @@ const handleReplyReview = (rev) => {
|
||||
|
||||
onLoad(async (query) => {
|
||||
const targetId = (query && query.id) ? query.id : '1';
|
||||
currentExpertTargetId = targetId;
|
||||
try {
|
||||
const liveData = await getCompanionDetail(targetId);
|
||||
if (liveData) {
|
||||
|
||||
@@ -415,7 +415,7 @@ const expert = ref({
|
||||
nickname: '认证大神',
|
||||
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=300'
|
||||
});
|
||||
const isReceivingOrders = ref(true);
|
||||
const isReceivingOrders = ref(uni.getStorageSync('EXPERT_RECEIVING_STATUS') !== '0');
|
||||
const todayIncome = ref('0.00');
|
||||
const todayOrdersCount = ref(0);
|
||||
const waitingVerifyCount = ref(0);
|
||||
@@ -579,6 +579,7 @@ const activeOrders = ref([
|
||||
|
||||
const toggleReceivingStatus = () => {
|
||||
isReceivingOrders.value = !isReceivingOrders.value;
|
||||
uni.setStorageSync('EXPERT_RECEIVING_STATUS', isReceivingOrders.value ? '1' : '0');
|
||||
uni.showToast({
|
||||
title: isReceivingOrders.value ? '已开启在线接单通道!' : '已打烊停止接单',
|
||||
icon: 'none'
|
||||
@@ -677,6 +678,7 @@ onShow(() => {
|
||||
}, 400);
|
||||
return;
|
||||
}
|
||||
isReceivingOrders.value = uni.getStorageSync('EXPERT_RECEIVING_STATUS') !== '0';
|
||||
loadWorkspaceOrders();
|
||||
});
|
||||
|
||||
|
||||
@@ -999,6 +999,75 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安
|
||||
expect(emojiRegex.test(fs.readFileSync(communityPath, 'utf-8'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('23. 全模块状态持久化与跨页面状态同步健壮性规范验证 (组局/门票/工作台/收藏/搭子)', () => {
|
||||
it('API 层 normalizeUserId 与收藏/组局方法必须优先支持 Storage 用户ID', () => {
|
||||
const apiPath = path.join(rootDir, 'pwuniapp', 'common', 'api.js');
|
||||
const content = fs.readFileSync(apiPath, 'utf-8');
|
||||
expect(content).toContain("uni.getStorageSync('userId')");
|
||||
expect(content).toContain('function normalizeUserId');
|
||||
expect(content).toContain('export async function getFavorites(userId)');
|
||||
expect(content).toContain('export async function checkFavorite(expertId, userId)');
|
||||
});
|
||||
|
||||
it('组局详情页必须动态响应当前用户并在 onShow 中拉取最新数据', () => {
|
||||
const actDetailPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'detail.vue');
|
||||
const content = fs.readFileSync(actDetailPath, 'utf-8');
|
||||
expect(content).toContain('currentUserId');
|
||||
expect(content).toContain('currentActId');
|
||||
expect(content).toContain('loadLiveDetail(currentActId.value)');
|
||||
expect(content).not.toContain('const CURRENT_USER_ID = 1001;');
|
||||
});
|
||||
|
||||
it('我的门票页面必须注册 onShow 并在退票与核销时绑定动态用户', () => {
|
||||
const ticketPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'my-ticket.vue');
|
||||
const content = fs.readFileSync(ticketPath, 'utf-8');
|
||||
expect(content).toContain('onShow');
|
||||
expect(content).toContain('currentUserId');
|
||||
expect(content).toContain('loadTicketData()');
|
||||
expect(content).toContain('leaveActivity(actId, currentUserId.value)');
|
||||
});
|
||||
|
||||
it('大神工作台接单状态必须持久化 EXPERT_RECEIVING_STATUS 并在 onShow 恢复', () => {
|
||||
const wsPath = path.join(rootDir, 'pwuniapp', 'pages', 'expert', 'workspace.vue');
|
||||
const content = fs.readFileSync(wsPath, 'utf-8');
|
||||
expect(content).toContain('EXPERT_RECEIVING_STATUS');
|
||||
expect(content).toContain("uni.setStorageSync('EXPERT_RECEIVING_STATUS'");
|
||||
});
|
||||
|
||||
it('大神主页必须在 onShow 中刷新当前用户的收藏状态', () => {
|
||||
const expertDetailPath = path.join(rootDir, 'pwuniapp', 'pages', 'expert', 'detail.vue');
|
||||
const content = fs.readFileSync(expertDetailPath, 'utf-8');
|
||||
expect(content).toContain('onShow');
|
||||
expect(content).toContain('refreshFavoriteState');
|
||||
expect(content).toContain('currentExpertTargetId');
|
||||
});
|
||||
|
||||
it('友圈页面必须通过 COMMUNITY_APPLIED_POST_IDS 持久化申请上车状态并防重', () => {
|
||||
const communityPath = path.join(rootDir, 'pwuniapp', 'pages', 'community', 'index.vue');
|
||||
const content = fs.readFileSync(communityPath, 'utf-8');
|
||||
expect(content).toContain('COMMUNITY_APPLIED_POST_IDS');
|
||||
expect(content).toContain('btn-applied');
|
||||
expect(content).toContain('syncInitialPostStates');
|
||||
});
|
||||
|
||||
it('所有状态持久化修复代码与测试严禁包含任何系统 Emoji 违规字符', () => {
|
||||
const actDetailPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'detail.vue');
|
||||
const ticketPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'my-ticket.vue');
|
||||
const wsPath = path.join(rootDir, 'pwuniapp', 'pages', 'expert', 'workspace.vue');
|
||||
const expertDetailPath = path.join(rootDir, 'pwuniapp', 'pages', 'expert', 'detail.vue');
|
||||
const communityPath = path.join(rootDir, 'pwuniapp', 'pages', 'community', 'index.vue');
|
||||
const apiPath = path.join(rootDir, 'pwuniapp', 'common', 'api.js');
|
||||
|
||||
const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation}|[\u2600-\u27BF])/u;
|
||||
expect(emojiRegex.test(fs.readFileSync(actDetailPath, 'utf-8'))).toBe(false);
|
||||
expect(emojiRegex.test(fs.readFileSync(ticketPath, 'utf-8'))).toBe(false);
|
||||
expect(emojiRegex.test(fs.readFileSync(wsPath, 'utf-8'))).toBe(false);
|
||||
expect(emojiRegex.test(fs.readFileSync(expertDetailPath, 'utf-8'))).toBe(false);
|
||||
expect(emojiRegex.test(fs.readFileSync(communityPath, 'utf-8'))).toBe(false);
|
||||
expect(emojiRegex.test(fs.readFileSync(apiPath, 'utf-8'))).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户