feat(system): implement dynamic platform configs for customer service, safety notice, and service agreement

这个提交包含在:
xcbyy
2026-10-10 09:47:53 +08:00
父节点 d9c8388711
当前提交 dc19189bc6
共有 10 个文件被更改,包括 613 次插入 和 10 次删除

查看文件

@@ -48,6 +48,7 @@ export default {
'menu.safety': 'Safety & Risk Control',
'menu.safety.places': 'Approved Venues',
'menu.safety.auditLog': 'Risk Intercept Log',
'menu.safety.platformConfig': 'Platform & Support Config',
'menu.banner': 'Banner & Operations',
'menu.banner.manage': 'Banner Management',
'menu.banner.navCategory': 'Category Navigation',

查看文件

@@ -48,6 +48,7 @@ export default {
'menu.safety': '履约安全与风控',
'menu.safety.places': '线下商圈场所库',
'menu.safety.auditLog': '场所与内容拦截日志',
'menu.safety.platformConfig': '客服与公告协议配置',
'menu.banner': '首页轮播与运营',
'menu.banner.manage': '轮播图管理',
'menu.banner.navCategory': '金刚区导航品类',

查看文件

@@ -32,6 +32,16 @@ const SAFETY: AppRouteRecordRaw = {
roles: ['*'],
},
},
{
path: 'platform-config',
name: 'PlatformConfig',
component: () => import('@/views/safety/platform-config/index.vue'),
meta: {
locale: 'menu.safety.platformConfig',
requiresAuth: true,
roles: ['*'],
},
},
],
};

查看文件

@@ -0,0 +1,259 @@
<template>
<div class="container">
<Breadcrumb :items="['menu.safety', 'menu.safety.platformConfig']" />
<a-card class="general-card" :title="'平台客服与官方指引/协议运营配置'">
<template #extra>
<a-button type="primary" :loading="loading" @click="fetchConfigs">
<template #icon><icon-refresh /></template>
刷新配置
</a-button>
</template>
<a-tabs default-active-key="customer_service" type="rounded">
<!-- Tab 1: 官方客服配置 -->
<a-tab-pane key="customer_service" title="官方客服与热线配置">
<div class="tab-pane-content">
<a-alert type="info" style="margin-bottom: 20px">
在此配置小程序个人中心“官方在线客服”弹层的联系电话与专属微信号,支持即时保存生效。
</a-alert>
<a-form :model="csForm" layout="vertical" style="max-width: 650px">
<a-form-item label="配置名称">
<a-input v-model="csForm.name" placeholder="如:官方在线客服与服务热线" />
</a-form-item>
<a-form-item label="7x24h 官方客服服务热线">
<a-input v-model="csForm.valueJson.hotline" placeholder="例如:400-888-6666" />
</a-form-item>
<a-form-item label="热线展示说明">
<a-input v-model="csForm.valueJson.hotlineDesc" placeholder="例如:7x24h 官方客服服务热线" />
</a-form-item>
<a-form-item label="官方 VIP 客服微信号">
<a-input v-model="csForm.valueJson.wechat" placeholder="例如:peiwan_vip_kefu" />
</a-form-item>
<a-form-item label="微信号展示说明">
<a-input v-model="csForm.valueJson.wechatDesc" placeholder="例如:官方 VIP 客服微信号" />
</a-form-item>
<a-form-item label="客服服务时间">
<a-input v-model="csForm.valueJson.workHours" placeholder="例如:周一至周日 00:00 - 24:00" />
</a-form-item>
<a-form-item label="启用状态">
<a-switch
:checked-value="1"
:unchecked-value="0"
v-model="csForm.status"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="saving" @click="saveConfig('customer_service', csForm)">
保存客服配置
</a-button>
</a-form-item>
</a-form>
</div>
</a-tab-pane>
<!-- Tab 2: 官方安全履约公告 -->
<a-tab-pane key="safety_notice" title="安全履约公告指引">
<div class="tab-pane-content">
<a-alert type="warning" style="margin-bottom: 20px">
在此配置小程序个人中心“平台消息与通知”弹层的履约安全指引与风险防范须知。
</a-alert>
<a-form :model="noticeForm" layout="vertical" style="max-width: 750px">
<a-form-item label="公告标题">
<a-input v-model="noticeForm.valueJson.title" placeholder="如:平台官方公告与指引" />
</a-form-item>
<a-form-item label="安全须知要点 (每行一条)">
<a-textarea
v-model="noticePointsText"
placeholder="每行一条须知要点"
:auto-size="{ minRows: 5, maxRows: 10 }"
/>
</a-form-item>
<a-form-item label="启用状态">
<a-switch
:checked-value="1"
:unchecked-value="0"
v-model="noticeForm.status"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="saving" @click="saveNoticeConfig">
保存公告指引
</a-button>
</a-form-item>
</a-form>
</div>
</a-tab-pane>
<!-- Tab 3: 用户服务协议与规范 -->
<a-tab-pane key="platform_protocol" title="平台规范与服务协议">
<div class="tab-pane-content">
<a-alert type="success" style="margin-bottom: 20px">
在此配置“平台规范与服务协议”弹层的协议条款与合规内容,严格遵循微信内容安全风控标准。
</a-alert>
<a-form :model="protoForm" layout="vertical" style="max-width: 800px">
<a-form-item label="协议标题">
<a-input v-model="protoForm.valueJson.title" placeholder="如:用户服务协议与隐私规范" />
</a-form-item>
<a-form-item label="协议摘要概要">
<a-input v-model="protoForm.valueJson.summary" placeholder="协议概要,一两句话总结" />
</a-form-item>
<a-form-item label="服务协议全文内容">
<a-textarea
v-model="protoForm.valueJson.content"
placeholder="请输入完整协议条款"
:auto-size="{ minRows: 8, maxRows: 15 }"
/>
</a-form-item>
<a-form-item label="启用状态">
<a-switch
:checked-value="1"
:unchecked-value="0"
v-model="protoForm.status"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" :loading="saving" @click="saveConfig('platform_protocol', protoForm)">
保存协议规范
</a-button>
</a-form-item>
</a-form>
</div>
</a-tab-pane>
</a-tabs>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue';
import axios from 'axios';
const loading = ref(false);
const saving = ref(false);
const csForm = reactive({
name: '官方在线客服与服务热线',
description: '小程序端个人中心客服联系方式',
status: 1,
valueJson: {
hotline: '400-888-6666',
hotlineDesc: '7x24h 官方客服服务热线',
wechat: 'peiwan_vip_kefu',
wechatDesc: '官方 VIP 客服微信号',
workHours: '周一至周日 00:00 - 24:00'
}
});
const noticeForm = reactive({
name: '安全履约须知与官方公告',
description: '小程序端官方指引与安全红线弹窗',
status: 1,
valueJson: {
title: '平台官方公告与指引',
points: [
'【安全履约须知】为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。',
'【现场核销】现场碰面后请出示 4 位数字核销码进行签到确认。',
'【平台兜底】平台提供 7x24 小时争议仲裁与资金托管保障服务,严禁脱离平台私下交易。'
]
}
});
const noticePointsText = ref('');
const protoForm = reactive({
name: '平台服务协议与合规规范',
description: '小程序端服务协议与隐私政策详情',
status: 1,
valueJson: {
title: '用户服务协议与隐私规范',
summary: '严禁发布任何违规言论及低俗诈骗行为,平台全面遵循微信内容安全风控与用户隐私保护标准。',
content: ''
}
});
const fetchConfigs = async () => {
loading.value = true;
try {
const { data: res } = await axios.get('/api/admin/system/configs');
if (res && res.code === 20000 && Array.isArray(res.data)) {
res.data.forEach((item: any) => {
if (item.configKey === 'customer_service') {
csForm.name = item.name;
csForm.description = item.description;
csForm.status = item.status;
csForm.valueJson = { ...csForm.valueJson, ...(item.valueJson || {}) };
} else if (item.configKey === 'safety_notice') {
noticeForm.name = item.name;
noticeForm.description = item.description;
noticeForm.status = item.status;
noticeForm.valueJson = { ...noticeForm.valueJson, ...(item.valueJson || {}) };
if (Array.isArray(noticeForm.valueJson.points)) {
noticePointsText.value = noticeForm.valueJson.points.join('\n');
}
} else if (item.configKey === 'platform_protocol') {
protoForm.name = item.name;
protoForm.description = item.description;
protoForm.status = item.status;
protoForm.valueJson = { ...protoForm.valueJson, ...(item.valueJson || {}) };
}
});
}
} catch (err: any) {
Message.error(err.response?.data?.message || '获取系统配置失败');
} finally {
loading.value = false;
}
};
const saveConfig = async (key: string, form: any) => {
saving.value = true;
try {
const payload = {
name: form.name,
description: form.description,
status: form.status,
valueJson: form.valueJson
};
const { data: res } = await axios.put(`/api/admin/system/configs/${key}`, payload);
if (res && res.code === 20000) {
Message.success('配置已保存并同步至小程序端');
fetchConfigs();
} else {
Message.error(res?.message || '保存配置失败');
}
} catch (err: any) {
Message.error(err.response?.data?.message || '保存配置异常,请检查内容是否含敏感词');
} finally {
saving.value = false;
}
};
const saveNoticeConfig = () => {
const points = noticePointsText.value
.split('\n')
.map((p) => p.trim())
.filter((p) => Boolean(p));
noticeForm.valueJson.points = points;
saveConfig('safety_notice', noticeForm);
};
onMounted(() => {
fetchConfigs();
});
</script>
<style scoped lang="less">
.container {
padding: 0 20px 20px 20px;
}
.general-card {
border-radius: 4px;
}
.tab-pane-content {
padding: 16px 8px;
}
</style>