diff --git a/pwuniapp/pages/activity/create.vue b/pwuniapp/pages/activity/create.vue index 08c5141..6e50647 100644 --- a/pwuniapp/pages/activity/create.vue +++ b/pwuniapp/pages/activity/create.vue @@ -49,7 +49,7 @@ 选择活动品类 - + + + {{ configLoading ? '品类加载中…' : '暂无可选品类,点击重试' }} + @@ -135,7 +138,7 @@ 合规商圈: - + + + {{ configLoading ? '商圈加载中…' : '暂无合规商圈,请手动填写地点(点击重试)' }} + @@ -367,8 +373,9 @@ 平台严格保障人身安全,严禁将私人住宅、酒店客房、封闭公寓等非公开场所作为履约地点。 - 请选择以下平台认证的示范公开商圈: - + 请选择以下平台认证的示范公开商圈: + 暂无可推荐的合规商圈,请手动填写公开商业场所。 + { @@ -426,7 +434,7 @@ onUnmounted(() => { const form = ref({ payType: 'PROJECT', - game: '美食陪吃', + game: '', title: '', startTime: '', location: '', @@ -467,12 +475,7 @@ const dispatchHours = ref(2); const changeDispatchHours = (delta) => { dispatchHours.value = Math.min(8, Math.max(1, Number(dispatchHours.value) + delta)); }; -const recommendPublicZones = ref([ - '北京市朝阳区三里屯太古里 (AAA公开商圈)', - '北京市朝阳区工体纯K量贩KTV (正规量贩)', - '北京市朝阳区国贸CBD商业街 (阳光示范商圈)', - '北京市海淀区五道口购物中心 (高校商圈)' -]); +const recommendPublicZones = ref([]); const presetTimeOptions = ref([ '今晚 19:30', @@ -565,30 +568,42 @@ const maxParticipants = ref(10); const presetPerPersonFees = ref([200, 300, 500, 800, 1000]); const presetTotalFees = ref([500, 1000, 2000, 3000, 5000]); +let placesRequestSeq = 0; + const loadLivePlaces = async () => { + const seq = ++placesRequestSeq; + configLoading.value = true; try { const [placesList, catList, configsRes] = await Promise.allSettled([ getSafetyPlaces(), getNavCategories(), getPlatformConfigs() ]); + if (seq !== placesRequestSeq) return; - if (placesList.status === 'fulfilled' && Array.isArray(placesList.value) && placesList.value.length > 0) { + // 商圈完全以后台「线下商圈场所库」为准,不再使用本地兜底数据 + if (placesList.status === 'fulfilled' && Array.isArray(placesList.value)) { recommendPublicZones.value = placesList.value .filter((p) => p.status === '正常营业') .map((p) => `${p.area} · ${p.name}`); + } else { + recommendPublicZones.value = []; } - if (catList.status === 'fulfilled' && Array.isArray(catList.value) && catList.value.length > 0) { + // 活动品类完全以后台「金刚区导航品类」为准,不再使用本地兜底数据 + if (catList.status === 'fulfilled' && Array.isArray(catList.value)) { const activeCats = catList.value .filter((c) => c.status === 1 && c.name && c.name !== '全部活动') .map((c) => c.name); - if (activeCats.length > 0) { - games.value = activeCats; - if (!games.value.includes(form.value.game)) { - form.value.game = games.value[0]; - } + games.value = activeCats; + if (activeCats.length === 0) { + form.value.game = ''; + } else if (!activeCats.includes(form.value.game)) { + form.value.game = activeCats[0]; } + } else { + games.value = []; + form.value.game = ''; } if (configsRes.status === 'fulfilled' && configsRes.value?.activity_rules) { @@ -603,7 +618,16 @@ const loadLivePlaces = async () => { } } } catch (e) { - console.warn('[ActivityCreate] 加载合规场所与品类异常降级:', e); + console.warn('[ActivityCreate] 加载合规场所与品类异常:', e); + if (seq === placesRequestSeq) { + recommendPublicZones.value = []; + games.value = []; + form.value.game = ''; + } + } finally { + if (seq === placesRequestSeq) { + configLoading.value = false; + } } }; @@ -957,6 +981,13 @@ const handleSubmit = async () => { } } + .config-empty-tip { + display: block; + padding: 12rpx 0; + font-size: 22rpx; + color: #94A3B8; + } + .input-row { display: flex; align-items: center; @@ -1449,6 +1480,10 @@ const handleSubmit = async () => { } .create-activity-container.theme-warm { + .form-card .config-empty-tip { + color: #78716C; + } + background-color: #FDFBF7; .mode-switch-wrap .mode-item { diff --git a/pwuniapp/pages/activity/list.vue b/pwuniapp/pages/activity/list.vue index 4b6d3cf..1289ef9 100644 --- a/pwuniapp/pages/activity/list.vue +++ b/pwuniapp/pages/activity/list.vue @@ -296,12 +296,8 @@ const navCapsules = computed(() => { }); } - // 默认兜底品类 - return [ - { code: 'MEAL', catKey: 'offline_meal', name: '美食陪吃', iconSvg: ICONS.utensils }, - { code: 'SING', catKey: 'offline_sing', name: '同城陪唱', iconSvg: ICONS.micVocal }, - { code: 'DRINK', catKey: 'offline_drink', name: '下午茶陪喝', iconSvg: ICONS.coffee } - ]; + // 品类完全以后台配置为准,不再使用本地兜底数据 + return []; }); onLoad((options) => { diff --git a/server/tests/content-security.spec.ts b/server/tests/content-security.spec.ts index a591db8..a781592 100644 --- a/server/tests/content-security.spec.ts +++ b/server/tests/content-security.spec.ts @@ -302,6 +302,21 @@ describe('ContentSecurityService & Compliance Architecture (TDD 微信内容安 expect(content).toContain('uni.chooseLocation'); }); + it('活动品类与合规商圈不得保留本地兜底数据,必须完全由后台配置驱动', () => { + const actCreatePath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'create.vue'); + const content = fs.readFileSync(actCreatePath, 'utf-8'); + expect(content).toContain('const games = ref([]);'); + expect(content).toContain('const recommendPublicZones = ref([]);'); + expect(content).toContain('game: \'\''); + expect(content).not.toContain('北京市朝阳区三里屯太古里'); + expect(content).not.toContain('北京市海淀区五道口购物中心'); + expect(content).not.toContain("['美食陪吃', '同城陪唱'"); + + const actListPath = path.join(rootDir, 'pwuniapp', 'pages', 'activity', 'list.vue'); + const listContent = fs.readFileSync(actListPath, 'utf-8'); + expect(listContent).not.toContain('默认兜底品类'); + }); + it('所有新增代码与测试套件严禁包含任何 Emoji 字符', () => { const pathsToCheck = [ path.join(rootDir, 'server', 'src', 'db', 'mysql.ts'),