| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- <template>
- <div class="app-container sys-config-page">
- <el-card class="config-card" shadow="hover">
- <!-- 主配置选项卡 -->
- <el-tabs v-model="activeMainTab" class="main-tabs">
- <el-tab-pane label="网站设置" name="website" />
- <el-tab-pane label="平台配置" name="platform" />
- <el-tab-pane label="文件存储配置" name="storage">
- <div class="storage-container animated fadeIn">
- <storage-config v-if="activeMainTab === 'storage'" />
- </div>
- </el-tab-pane>
- <el-tab-pane label="短信配置" name="sms" />
- <el-tab-pane label="支付配置" name="payment">
- <div class="agreement-container animated fadeIn">
- <wxpay-config v-if="activeMainTab === 'payment'" />
- </div>
- </el-tab-pane>
- <el-tab-pane label="协议配置" name="agreement">
- <div class="agreement-container animated fadeIn">
- <!-- 子配置选项卡:协议类型 -->
- <div class="sub-tabs-wrapper">
- <div
- v-for="tab in agreementSubTabs"
- :key="tab.name"
- :class="['sub-tab-item', activeSubTab === tab.name ? 'active' : '']"
- @click="activeSubTab = tab.name"
- >
- {{ tab.label }}
- </div>
- </div>
- <!-- 协议编辑表单 -->
- <el-form :model="form" label-width="100px" class="config-form">
- <el-form-item label="协议标题" required>
- <el-input
- v-model="form.title"
- placeholder="请输入协议标题"
- class="title-input"
- />
- </el-form-item>
- <el-form-item label="协议内容" required>
- <Editor v-model="form.content" :min-height="400" />
- </el-form-item>
-
- <el-form-item class="form-actions">
- <el-button type="primary" size="large" icon="Check" @click="handleSave">
- 保存并应用
- </el-button>
- </el-form-item>
- </el-form>
- </div>
- </el-tab-pane>
- </el-tabs>
- </el-card>
- <!-- 其他Tab的占位信息 (非协议配置、支付配置和文件存储配置时显示) -->
- <el-empty v-if="activeMainTab !== 'agreement' && activeMainTab !== 'payment' && activeMainTab !== 'storage'" description="该模块配置开发中..." />
- </div>
- </template>
- <script setup name="SysConfig">
- import { ref, reactive, watch, onMounted } from 'vue';
- import Editor from '@/components/Editor/index.vue';
- import WxpayConfig from './wxpay.vue';
- import StorageConfig from './storage.vue';
- import { ElMessage } from 'element-plus';
- import request from '@/utils/request';
- // 主选项卡
- const activeMainTab = ref('agreement');
- // 表单数据
- const form = reactive({
- title: '用户服务协议',
- content: ''
- });
- // 当前协议对应的数据库 ID
- const currentAgreementId = ref(null);
- // 协议子选项卡
- const activeSubTab = ref('user');
- const agreementSubTabs = [
- { label: '用户协议', name: 'user' },
- { label: '隐私政策', name: 'privacy' },
- { label: 'Offer政策', name: 'offer' }
- ];
- const activeSubTabsMap = {
- user: '用户协议',
- privacy: '隐私政策',
- offer: 'Offer政策'
- };
- const agreementTypeMap = {
- user: 'service',
- privacy: 'privacy',
- offer: 'offer'
- };
- // 获取协议内容
- const fetchAgreement = async () => {
- try {
- const typeKey = agreementTypeMap[activeSubTab.value];
- const res = await request({
- url: `/miniapp/auth/agreement?type=${typeKey}`,
- method: 'get'
- });
- if (res && res.code === 200 && res.data) {
- currentAgreementId.value = res.data.id;
- form.title = res.data.title || activeSubTabsMap[activeSubTab.value];
- form.content = res.data.content || '';
- } else {
- currentAgreementId.value = null;
- form.title = activeSubTabsMap[activeSubTab.value];
- form.content = '';
- }
- } catch (error) {
- console.error('获取协议失败', error);
- }
- };
- // 切换 Tab 时重新抓取对应数据
- watch(activeSubTab, () => {
- fetchAgreement();
- });
- // 挂载时抓取初始数据
- onMounted(() => {
- fetchAgreement();
- });
- /** 保存配置 */
- const handleSave = async () => {
- if (!form.title) {
- return ElMessage.warning('请输入协议标题');
- }
- if (!form.content || form.content === '<p></p>') {
- return ElMessage.warning('请输入协议内容');
- }
-
- try {
- const typeKey = agreementTypeMap[activeSubTab.value];
- const payload = {
- id: currentAgreementId.value,
- type: typeKey,
- title: form.title,
- content: form.content
- };
-
- // 调用后端的 /edit 接口
- const res = await request({
- url: '/miniapp/auth/edit',
- method: 'post',
- data: payload
- });
-
- if (res.code === 200) {
- ElMessage.success({
- message: `${activeSubTabsMap[activeSubTab.value]} 保存成功`,
- type: 'success',
- duration: 2000
- });
- fetchAgreement(); // 重新拉取
- } else {
- ElMessage.error(res.msg || '保存失败');
- }
- } catch (error) {
- console.error('保存协议失败', error);
- }
- };
- </script>
- <style scoped lang="scss">
- .sys-config-page {
- padding: 20px;
- background-color: #f5f7fa;
- min-height: calc(100vh - 84px);
- .config-card {
- border-radius: 12px;
- border: none;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
- :deep(.el-card__body) {
- padding: 0;
- }
- }
- /* 主选项卡样式定制 */
- .main-tabs {
- :deep(.el-tabs__header) {
- margin-bottom: 0;
- padding: 10px 20px 0;
- background: #fff;
- border-radius: 12px 12px 0 0;
- }
- :deep(.el-tabs__nav-wrap::after) {
- height: 1px;
- background-color: #ebeef5;
- }
- :deep(.el-tabs__item) {
- font-size: 15px;
- font-weight: 500;
- height: 50px;
- line-height: 50px;
- transition: all 0.3s;
-
- &:hover {
- color: #409eff;
- }
-
- &.is-active {
- font-weight: 700;
- }
- }
- :deep(.el-tabs__content) {
- padding: 24px;
- background: #fff;
- border-radius: 0 0 12px 12px;
- }
- }
- .agreement-container {
- .sub-tabs-wrapper {
- display: flex;
- gap: 12px;
- margin-bottom: 30px;
-
- .sub-tab-item {
- padding: 8px 24px;
- border-radius: 6px;
- background: #f4f4f5;
- color: #606266;
- cursor: pointer;
- font-size: 14px;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- border: 1px solid transparent;
- &:hover {
- color: #409eff;
- background: #ecf5ff;
- }
- &.active {
- background: #409eff;
- color: #fff;
- box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
- transform: translateY(-1px);
- }
- }
- }
- .config-form {
- max-width: 1000px;
- .title-input {
- :deep(.el-input__wrapper) {
- box-shadow: 0 0 0 1px #dcdfe6 inset;
-
- &:hover {
- box-shadow: 0 0 0 1px #409eff inset;
- }
-
- &.is-focus {
- box-shadow: 0 0 0 1px #409eff inset;
- }
- }
- }
- :deep(.el-form-item__label) {
- font-weight: 600;
- color: #303133;
-
- &::before {
- margin-right: 4px;
- }
- }
- .form-actions {
- margin-top: 40px;
-
- .el-button {
- padding: 12px 40px;
- font-weight: 600;
- letter-spacing: 1px;
- border-radius: 8px;
- }
- }
- }
- }
- }
- /* 动画 */
- .fadeIn {
- animation: fadeIn 0.5s ease-in-out;
- }
- @keyframes fadeIn {
- from {
- opacity: 0;
- transform: translateY(10px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
- }
- </style>
|