refactor(activity): 移除活动品类与合规商圈的本地兜底数据,完全由后台配置驱动
- 发起组局页 games 初始改为空,仅从 nav_categories(status=1) 加载;加载失败/为空显示重试提示,禁止提交空品类 - 合规商圈初始改为空,仅从 safety_places(status=正常营业) 加载;无数据时提示手动填写地点 - 活动列表品类导航移除默认 3 个兜底品类 - 117 套件/764 用例全绿
这个提交包含在:
@@ -49,7 +49,7 @@
|
||||
<!-- 品类选择 -->
|
||||
<view class="form-card">
|
||||
<view class="card-label">选择活动品类</view>
|
||||
<view class="game-pills">
|
||||
<view class="game-pills" v-if="games.length > 0">
|
||||
<view
|
||||
v-for="g in games"
|
||||
:key="g"
|
||||
@@ -60,6 +60,9 @@
|
||||
{{ g }}
|
||||
</view>
|
||||
</view>
|
||||
<text v-else class="config-empty-tip" @click="loadLivePlaces">
|
||||
{{ configLoading ? '品类加载中…' : '暂无可选品类,点击重试' }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 基础信息录入 -->
|
||||
@@ -135,7 +138,7 @@
|
||||
<!-- 合规示范商圈一键点选 (防风控与合规保障) -->
|
||||
<view class="quick-chip-row">
|
||||
<text class="chip-label">合规商圈:</text>
|
||||
<view class="quick-chips">
|
||||
<view class="quick-chips" v-if="recommendPublicZones.length > 0">
|
||||
<view
|
||||
v-for="zoneItem in recommendPublicZones.slice(0, 4)"
|
||||
:key="zoneItem"
|
||||
@@ -146,6 +149,9 @@
|
||||
{{ zoneItem.split(' ')[0] }}
|
||||
</view>
|
||||
</view>
|
||||
<text v-else class="config-empty-tip" @click="loadLivePlaces">
|
||||
{{ configLoading ? '商圈加载中…' : '暂无合规商圈,请手动填写地点(点击重试)' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -367,8 +373,9 @@
|
||||
<text class="risk-desc">
|
||||
平台严格保障人身安全,严禁将私人住宅、酒店客房、封闭公寓等非公开场所作为履约地点。
|
||||
</text>
|
||||
<text class="risk-sub-tip">请选择以下平台认证的示范公开商圈:</text>
|
||||
<view class="recommend-zones">
|
||||
<text class="risk-sub-tip" v-if="recommendPublicZones.length > 0">请选择以下平台认证的示范公开商圈:</text>
|
||||
<text class="risk-sub-tip" v-else>暂无可推荐的合规商圈,请手动填写公开商业场所。</text>
|
||||
<view class="recommend-zones" v-if="recommendPublicZones.length > 0">
|
||||
<view
|
||||
v-for="zone in recommendPublicZones"
|
||||
:key="zone"
|
||||
@@ -396,7 +403,8 @@ import { createActivity, createOrder, payOrder as payOrderApi, requestWechatPaym
|
||||
import { getCurrentThemeKey, applyThemeToNativeUI } from '@/common/theme.js';
|
||||
import { ensureLoginOrRedirect } from '@/common/role.js';
|
||||
|
||||
const games = ref(['美食陪吃', '同城陪唱', '下午茶陪喝', '同城微醺', '同城桌游', '热门电竞']);
|
||||
const games = ref([]);
|
||||
const configLoading = ref(true);
|
||||
const currentThemeKey = ref(getCurrentThemeKey());
|
||||
|
||||
const onThemeChanged = (key) => {
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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'),
|
||||
|
||||
在新工单中引用
屏蔽一个用户