|
@@ -0,0 +1,807 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="menu-settings-container">
|
|
|
|
|
+ <el-tabs v-model="activeTab" class="custom-tabs">
|
|
|
|
|
+ <!-- 顶部菜单 (树形二级结构) -->
|
|
|
|
|
+ <el-tab-pane label="顶部菜单" name="top">
|
|
|
|
|
+ <div class="tab-content">
|
|
|
|
|
+ <div class="action-bar">
|
|
|
|
|
+ <el-button type="primary" icon="Plus" @click="handleAdd('top')" class="btn-primary-blue">新增顶部一级菜单</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ :data="topMenuList"
|
|
|
|
|
+ border
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ header-cell-class-name="table-header-custom"
|
|
|
|
|
+ class="standard-table"
|
|
|
|
|
+ row-key="id"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table-column label="序号" type="index" width="70" align="center" />
|
|
|
|
|
+ <el-table-column prop="name" label="菜单名称" min-width="200" />
|
|
|
|
|
+ <el-table-column prop="sort" label="排序" width="100" align="center" sortable />
|
|
|
|
|
+ <el-table-column prop="link" label="跳转地址" min-width="250" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="状态" width="100" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="row.status"
|
|
|
|
|
+ :active-value="1"
|
|
|
|
|
+ :inactive-value="0"
|
|
|
|
|
+ active-color="#1890ff"
|
|
|
|
|
+ @change="changeTopMenuStatus(row.id, row.status)"
|
|
|
|
|
+ />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="操作" width="220" align="center">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button v-if="!scope.row.parentId" link type="primary" class="text-blue" @click="handleAddSubTop(scope.row)">添加子菜单</el-button>
|
|
|
|
|
+ <el-button link type="primary" class="text-blue" @click="handleEdit('top', scope.row, scope.$index)">编辑</el-button>
|
|
|
|
|
+ <el-button link type="danger" @click="handleDelete('top', scope.$index, scope.row)">删除</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 底部菜单 (保持树形二级结构) -->
|
|
|
|
|
+ <el-tab-pane label="底部菜单" name="bottom">
|
|
|
|
|
+ <div class="tab-content">
|
|
|
|
|
+ <div class="action-bar">
|
|
|
|
|
+ <el-button type="primary" icon="Plus" @click="handleAdd('bottom')" class="btn-primary-blue">新增底部一级菜单</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ :data="bottomMenuList"
|
|
|
|
|
+ border
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ header-cell-class-name="table-header-custom"
|
|
|
|
|
+ class="standard-table"
|
|
|
|
|
+ row-key="id"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table-column label="序号" type="index" width="70" align="center" />
|
|
|
|
|
+ <el-table-column prop="name" label="菜单名称" min-width="200" />
|
|
|
|
|
+ <el-table-column prop="sort" label="排序" width="100" align="center" sortable />
|
|
|
|
|
+ <el-table-column prop="link" label="跳转地址" min-width="250" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="状态" width="100" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="row.status"
|
|
|
|
|
+ :active-value="1"
|
|
|
|
|
+ :inactive-value="0"
|
|
|
|
|
+ active-color="#1890ff"
|
|
|
|
|
+ @change="changeBottomMenuStatus(row.id, row.status)"
|
|
|
|
|
+ />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="操作" width="220" align="center">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button v-if="!scope.row.parentId" link type="primary" class="text-blue" @click="handleAddSub(scope.row)">添加子菜单</el-button>
|
|
|
|
|
+ <el-button link type="primary" class="text-blue" @click="handleEdit('bottom', scope.row, scope.$index)">编辑</el-button>
|
|
|
|
|
+ <el-button link type="danger" @click="handleDelete('bottom', scope.$index, scope.row)">删除</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 手机慧采 -->
|
|
|
|
|
+ <el-tab-pane label="手机慧采" name="phone">
|
|
|
|
|
+ <div class="tab-content">
|
|
|
|
|
+ <div class="copyright-container">
|
|
|
|
|
+ <el-form :model="phoneForm" label-width="120px" class="copyright-form">
|
|
|
|
|
+ <div class="form-section">
|
|
|
|
|
+ <div class="section-title">基本信息</div>
|
|
|
|
|
+ <div class="grid-form-fields">
|
|
|
|
|
+ <el-form-item label="菜单名称:">
|
|
|
|
|
+ <el-input v-model="phoneForm.menuName" placeholder="如:手机慧采" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="form-section">
|
|
|
|
|
+ <div class="section-title">移动资产矩阵</div>
|
|
|
|
|
+ <div class="social-config-row">
|
|
|
|
|
+ <!-- 小程序配置 -->
|
|
|
|
|
+ <div class="social-item">
|
|
|
|
|
+ <div class="social-label">小程序端配置</div>
|
|
|
|
|
+ <el-form-item label="主标题:" label-width="80px">
|
|
|
|
|
+ <el-input v-model="phoneForm.miniProgramTitle" placeholder="如:体验小程序" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="副标题:" label-width="80px">
|
|
|
|
|
+ <el-input v-model="phoneForm.miniProgramSubTitle" placeholder="如:随时随地 快捷采购" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="二维码:" label-width="80px">
|
|
|
|
|
+ <UploadImage v-model="phoneForm.miniProgramQR" :limit="1" />
|
|
|
|
|
+ <div class="field-tip" style="margin-top: 0">上传小程序太阳码</div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 微信公众号服务配置 -->
|
|
|
|
|
+ <div class="social-item">
|
|
|
|
|
+ <div class="social-label">微信服务号配置</div>
|
|
|
|
|
+ <el-form-item label="主标题:" label-width="80px">
|
|
|
|
|
+ <el-input v-model="phoneForm.wechatServiceTitle" placeholder="如:关注公众号" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="副标题:" label-width="80px">
|
|
|
|
|
+ <el-input v-model="phoneForm.wechatServiceSubTitle" placeholder="如:获取最新折扣与会员权益" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="二维码:" label-width="80px">
|
|
|
|
|
+ <UploadImage v-model="phoneForm.wechatServiceQR" :limit="1" />
|
|
|
|
|
+ <div class="field-tip" style="margin-top: 0">上传微信公众号服务二维码</div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="form-footer">
|
|
|
|
|
+ <el-button type="primary" @click="savePhoneSettings" class="btn-primary-blue" size="large">保存手机页配置</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 版权信息 -->
|
|
|
|
|
+ <el-tab-pane label="版权信息" name="copyright">
|
|
|
|
|
+ <div class="tab-content">
|
|
|
|
|
+ <div class="copyright-container">
|
|
|
|
|
+ <el-form :model="copyrightForm" label-width="120px" class="copyright-form">
|
|
|
|
|
+ <!-- 基本信息 (一排放2个字段,网格化重构) -->
|
|
|
|
|
+ <div class="form-section">
|
|
|
|
|
+ <div class="section-title">基本信息</div>
|
|
|
|
|
+ <div class="grid-form-fields">
|
|
|
|
|
+ <el-form-item label="版权所有:">
|
|
|
|
|
+ <el-input v-model="copyrightForm.owner" placeholder="如:© 2024 优易企业购 版权所有" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="备案号:">
|
|
|
|
|
+ <el-input v-model="copyrightForm.icp" placeholder="如:苏ICP备12345678号" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="公安备案:">
|
|
|
|
|
+ <el-input v-model="copyrightForm.police" placeholder="如:苏公网安备 1234567890号" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="联系电话:">
|
|
|
|
|
+ <el-input v-model="copyrightForm.phone" placeholder="如:400-123-4567" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="客服电话:">
|
|
|
|
|
+ <el-input v-model="copyrightForm.customerPhone" placeholder="如:400-666-8888" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="客服二维码:">
|
|
|
|
|
+ <UploadImage v-model="copyrightForm.customerQR" :limit="1" />
|
|
|
|
|
+ <div class="field-tip" style="margin-top: 0">上传客服微信二维码,支持用户扫码添加专属客服</div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 品牌资产 (一排放2个字段,Logo与上传对齐) -->
|
|
|
|
|
+ <div class="form-section">
|
|
|
|
|
+ <div class="section-title">品牌资产</div>
|
|
|
|
|
+ <div class="grid-form-fields" style="align-items: center">
|
|
|
|
|
+ <el-form-item label="底部Logo:">
|
|
|
|
|
+ <UploadImage v-model="copyrightForm.logo" :limit="1" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <div class="field-tip" style="margin-top: 0; padding-left: 20px">
|
|
|
|
|
+ 建议尺寸:160 * 78,格式:PNG/SVG。大白底或透明背景,完美契合页脚视觉资产
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 社交媒体 (二维码不要换行显示调大至 80px) -->
|
|
|
|
|
+ <div class="form-section">
|
|
|
|
|
+ <div class="section-title">社交媒体</div>
|
|
|
|
|
+ <div class="social-config-row">
|
|
|
|
|
+ <div class="social-item">
|
|
|
|
|
+ <div class="social-label">公众号配置</div>
|
|
|
|
|
+ <el-form-item label="名称:" label-width="80px">
|
|
|
|
|
+ <el-input v-model="copyrightForm.wechatTitle" placeholder="如:关注公众号" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="二维码:" label-width="80px">
|
|
|
|
|
+ <UploadImage v-model="copyrightForm.wechatQR" :limit="1" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="social-item">
|
|
|
|
|
+ <div class="social-label">微博配置</div>
|
|
|
|
|
+ <el-form-item label="名称:" label-width="80px">
|
|
|
|
|
+ <el-input v-model="copyrightForm.weiboTitle" placeholder="如:关注微博" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="二维码:" label-width="80px">
|
|
|
|
|
+ <UploadImage v-model="copyrightForm.weiboQR" :limit="1" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="form-footer">
|
|
|
|
|
+ <el-button type="primary" @click="saveCopyright" class="btn-primary-blue" size="large">保存版权页配置</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+ </el-tabs>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 编辑弹窗 -->
|
|
|
|
|
+ <el-dialog v-model="dialogVisible" :title="dialogType === 'add' ? '新增菜单' : '编辑菜单'" width="550px" class="custom-dialog">
|
|
|
|
|
+ <el-form :model="menuForm" label-width="100px" class="p-t-10">
|
|
|
|
|
+ <el-form-item label="上级菜单:">
|
|
|
|
|
+ <el-select v-model="menuForm.parentId" placeholder="请选择上级菜单" clearable style="width: 100%">
|
|
|
|
|
+ <el-option label="无 (作为一级菜单)" :value="null" />
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in currentMenuType === 'top' ? topMenuList : bottomMenuList"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ :label="item.name"
|
|
|
|
|
+ :value="item.id"
|
|
|
|
|
+ :disabled="item.id === menuForm.id"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="菜单名称:">
|
|
|
|
|
+ <el-input v-model="menuForm.name" placeholder="请输入菜单名称" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="跳转地址:">
|
|
|
|
|
+ <WebLinkInput v-model="menuForm.link" placeholder="请输入链接或点击选择" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="菜单排序:">
|
|
|
|
|
+ <el-input-number v-model="menuForm.sort" :min="1" placeholder="数字越小越靠前" style="width: 100%" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="启用状态:">
|
|
|
|
|
+ <el-switch v-model="menuForm.status" :active-value="1" :inactive-value="0" active-color="#1890ff" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="confirmSubmit" class="btn-primary-blue">确定</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+import { ref, reactive, onMounted } from 'vue';
|
|
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
|
|
+import { globalHeaders } from '@/utils/request';
|
|
|
|
|
+import { listTopMenu, addTopMenu, updateTopMenu, delTopMenu, changeTopMenuStatus } from '@/api/settings/topMenu';
|
|
|
|
|
+import { addMobileHuicaiConfig, updateMobileHuicaiConfig, getCurrentMobileHuicaiConfig } from '@/api/settings/mobileHuicaiConfig';
|
|
|
|
|
+import { addCopyrightInfo, updateCopyrightInfo, getCurrentCopyrightInfo } from '@/api/settings/copyrightInfo';
|
|
|
|
|
+import { listBottomMenu, addBottomMenu, updateBottomMenu, delBottomMenu, changeBottomMenuStatus } from '@/api/settings/bottomMenu';
|
|
|
|
|
+
|
|
|
|
|
+const activeTab = ref('top');
|
|
|
|
|
+
|
|
|
|
|
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
|
|
|
|
|
+const uploadHeaders = ref(globalHeaders());
|
|
|
|
|
+
|
|
|
|
|
+const beforeCellUpload = (file: any) => {
|
|
|
|
|
+ const isImg = file.type.indexOf('image') > -1;
|
|
|
|
|
+ if (!isImg) {
|
|
|
|
|
+ ElMessage.error('只能上传图片文件');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ const isLt5M = file.size / 1024 / 1024 < 5;
|
|
|
|
|
+ if (!isLt5M) {
|
|
|
|
|
+ ElMessage.error('图片大小不能超过 5MB');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// === 顶部菜单 ===
|
|
|
|
|
+const topMenuList = ref<any[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+const buildTree = (list: any[]) => {
|
|
|
|
|
+ const tree: any[] = [];
|
|
|
|
|
+ const map = new Map<string, any>();
|
|
|
|
|
+ list.forEach((item) => {
|
|
|
|
|
+ map.set(String(item.id), { ...item, children: [] });
|
|
|
|
|
+ });
|
|
|
|
|
+ map.forEach((item) => {
|
|
|
|
|
+ if (item.parentId && map.has(String(item.parentId))) {
|
|
|
|
|
+ map.get(String(item.parentId))!.children.push(item);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tree.push(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ const cleanEmpties = (nodes: any[]) => {
|
|
|
|
|
+ nodes.forEach((n) => {
|
|
|
|
|
+ if (n.children && n.children.length === 0) delete n.children;
|
|
|
|
|
+ else if (n.children) cleanEmpties(n.children);
|
|
|
|
|
+ });
|
|
|
|
|
+ };
|
|
|
|
|
+ cleanEmpties(tree);
|
|
|
|
|
+ return tree;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const loadTopMenus = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listTopMenu({ pageNum: 1, pageSize: 500 } as any);
|
|
|
|
|
+ topMenuList.value = buildTree(res.rows || []);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载顶部菜单失败', error);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const loadBottomMenus = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listBottomMenu({ pageNum: 1, pageSize: 500 } as any);
|
|
|
|
|
+ bottomMenuList.value = buildTree(res.rows || []);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载底部菜单失败', error);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// === 底部菜单 ===
|
|
|
|
|
+const bottomMenuList = ref<any[]>([]);
|
|
|
|
|
+
|
|
|
|
|
+// === 手机慧采 ===
|
|
|
|
|
+const phoneConfigId = ref<number | string>('');
|
|
|
|
|
+const phoneForm = reactive({
|
|
|
|
|
+ menuName: '手机慧采',
|
|
|
|
|
+ miniProgramQR: '',
|
|
|
|
|
+ miniProgramTitle: '体验小程序',
|
|
|
|
|
+ miniProgramSubTitle: '随时随地 快捷采购',
|
|
|
|
|
+ wechatServiceQR: '',
|
|
|
|
|
+ wechatServiceTitle: '关注公众号',
|
|
|
|
|
+ wechatServiceSubTitle: '获取最新折扣与会员权益'
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const loadPhoneConfig = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = (await getCurrentMobileHuicaiConfig()) as any;
|
|
|
|
|
+ if (res.data) {
|
|
|
|
|
+ const d = res.data;
|
|
|
|
|
+ phoneConfigId.value = d.id || '';
|
|
|
|
|
+ phoneForm.menuName = d.menuName || '手机慧采';
|
|
|
|
|
+ phoneForm.miniProgramQR = d.miniProgramQr || '';
|
|
|
|
|
+ phoneForm.miniProgramTitle = d.miniProgramTitle || '体验小程序';
|
|
|
|
|
+ phoneForm.miniProgramSubTitle = d.miniProgramSubTitle || '随时随地 快捷采购';
|
|
|
|
|
+ phoneForm.wechatServiceQR = d.wechatServiceQr || '';
|
|
|
|
|
+ phoneForm.wechatServiceTitle = d.wechatServiceTitle || '关注公众号';
|
|
|
|
|
+ phoneForm.wechatServiceSubTitle = d.wechatServiceSubTitle || '获取最新折扣与会员权益';
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载手机慧采配置失败', error);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const savePhoneSettings = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data: any = {
|
|
|
|
|
+ menuName: phoneForm.menuName,
|
|
|
|
|
+ miniProgramTitle: phoneForm.miniProgramTitle,
|
|
|
|
|
+ miniProgramSubTitle: phoneForm.miniProgramSubTitle,
|
|
|
|
|
+ miniProgramQr: phoneForm.miniProgramQR,
|
|
|
|
|
+ wechatServiceTitle: phoneForm.wechatServiceTitle,
|
|
|
|
|
+ wechatServiceSubTitle: phoneForm.wechatServiceSubTitle,
|
|
|
|
|
+ wechatServiceQr: phoneForm.wechatServiceQR
|
|
|
|
|
+ };
|
|
|
|
|
+ if (phoneConfigId.value) {
|
|
|
|
|
+ data.id = phoneConfigId.value;
|
|
|
|
|
+ await updateMobileHuicaiConfig(data);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const res = (await addMobileHuicaiConfig(data)) as any;
|
|
|
|
|
+ if (res.data?.id) phoneConfigId.value = res.data.id;
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success('手机页配置保存成功');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ ElMessage.error('保存失败');
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// === 版权信息 ===
|
|
|
|
|
+const copyrightConfigId = ref<number | string>('');
|
|
|
|
|
+const copyrightForm = reactive({
|
|
|
|
|
+ owner: '© 2024 优易企业购 版权所有',
|
|
|
|
|
+ icp: '苏ICP备12345678号',
|
|
|
|
|
+ police: '苏公网安备 1234567890号',
|
|
|
|
|
+ phone: '400-123-4567',
|
|
|
|
|
+ customerPhone: '400-666-8888',
|
|
|
|
|
+ customerQR: '',
|
|
|
|
|
+ logo: '',
|
|
|
|
|
+ wechatTitle: '关注公众号',
|
|
|
|
|
+ wechatQR: '',
|
|
|
|
|
+ weiboTitle: '关注微博',
|
|
|
|
|
+ weiboQR: ''
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const loadCopyright = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = (await getCurrentCopyrightInfo()) as any;
|
|
|
|
|
+ if (res.data) {
|
|
|
|
|
+ const d = res.data;
|
|
|
|
|
+ copyrightConfigId.value = d.id || '';
|
|
|
|
|
+ copyrightForm.owner = d.owner || '';
|
|
|
|
|
+ copyrightForm.icp = d.icp || '';
|
|
|
|
|
+ copyrightForm.police = d.police || '';
|
|
|
|
|
+ copyrightForm.phone = d.phone || '';
|
|
|
|
|
+ copyrightForm.customerPhone = d.customerPhone || '';
|
|
|
|
|
+ copyrightForm.customerQR = d.customerQr || '';
|
|
|
|
|
+ copyrightForm.logo = d.logo || '';
|
|
|
|
|
+ copyrightForm.wechatTitle = d.wechatTitle || '';
|
|
|
|
|
+ copyrightForm.wechatQR = d.wechatQr || '';
|
|
|
|
|
+ copyrightForm.weiboTitle = d.weiboTitle || '';
|
|
|
|
|
+ copyrightForm.weiboQR = d.weiboQr || '';
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载版权信息失败', error);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const saveCopyright = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data: any = {
|
|
|
|
|
+ owner: copyrightForm.owner,
|
|
|
|
|
+ icp: copyrightForm.icp,
|
|
|
|
|
+ police: copyrightForm.police,
|
|
|
|
|
+ phone: copyrightForm.phone,
|
|
|
|
|
+ customerPhone: copyrightForm.customerPhone,
|
|
|
|
|
+ customerQr: copyrightForm.customerQR,
|
|
|
|
|
+ logo: copyrightForm.logo,
|
|
|
|
|
+ wechatTitle: copyrightForm.wechatTitle,
|
|
|
|
|
+ wechatQr: copyrightForm.wechatQR,
|
|
|
|
|
+ weiboTitle: copyrightForm.weiboTitle,
|
|
|
|
|
+ weiboQr: copyrightForm.weiboQR
|
|
|
|
|
+ };
|
|
|
|
|
+ if (copyrightConfigId.value) {
|
|
|
|
|
+ data.id = copyrightConfigId.value;
|
|
|
|
|
+ await updateCopyrightInfo(data);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const res = (await addCopyrightInfo(data)) as any;
|
|
|
|
|
+ if (res.data?.id) copyrightConfigId.value = res.data.id;
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success('版权信息保存成功');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ ElMessage.error('保存失败');
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// === 弹窗逻辑 ===
|
|
|
|
|
+const dialogVisible = ref(false);
|
|
|
|
|
+const dialogType = ref('add');
|
|
|
|
|
+const currentMenuType = ref('top');
|
|
|
|
|
+const menuForm = reactive({
|
|
|
|
|
+ id: null as number | string | null,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ link: '',
|
|
|
|
|
+ target: '_self',
|
|
|
|
|
+ status: 1,
|
|
|
|
|
+ sort: 100,
|
|
|
|
|
+ parentId: null as number | string | null
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const handleAdd = (type: string) => {
|
|
|
|
|
+ currentMenuType.value = type;
|
|
|
|
|
+ dialogType.value = 'add';
|
|
|
|
|
+ Object.assign(menuForm, { id: null, name: '', link: '', target: '_self', status: 1, sort: 100, parentId: null });
|
|
|
|
|
+ dialogVisible.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleAddSub = (parentRow: any) => {
|
|
|
|
|
+ currentMenuType.value = 'bottom';
|
|
|
|
|
+ dialogType.value = 'add';
|
|
|
|
|
+ Object.assign(menuForm, { id: null, name: '', link: '', target: '_self', status: 1, sort: 100, parentId: parentRow.id });
|
|
|
|
|
+ dialogVisible.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleAddSubTop = (parentRow: any) => {
|
|
|
|
|
+ currentMenuType.value = 'top';
|
|
|
|
|
+ dialogType.value = 'add';
|
|
|
|
|
+ Object.assign(menuForm, { id: null, name: '', link: '', target: '_self', status: 1, sort: 100, parentId: parentRow.id });
|
|
|
|
|
+ dialogVisible.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleEdit = (type: string, row: any, _index: number) => {
|
|
|
|
|
+ currentMenuType.value = type;
|
|
|
|
|
+ dialogType.value = 'edit';
|
|
|
|
|
+ Object.assign(menuForm, JSON.parse(JSON.stringify(row)));
|
|
|
|
|
+ dialogVisible.value = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const confirmSubmit = async () => {
|
|
|
|
|
+ if (!menuForm.name) return ElMessage.warning('请输入菜单名称');
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data: any = {
|
|
|
|
|
+ name: menuForm.name,
|
|
|
|
|
+ link: menuForm.link,
|
|
|
|
|
+ target: menuForm.target,
|
|
|
|
|
+ status: menuForm.status,
|
|
|
|
|
+ sort: menuForm.sort,
|
|
|
|
|
+ parentId: menuForm.parentId || undefined
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (currentMenuType.value === 'top') {
|
|
|
|
|
+ if (menuForm.id) {
|
|
|
|
|
+ data.id = menuForm.id;
|
|
|
|
|
+ await updateTopMenu(data);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await addTopMenu(data);
|
|
|
|
|
+ }
|
|
|
|
|
+ await loadTopMenus();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (menuForm.id) {
|
|
|
|
|
+ data.id = menuForm.id;
|
|
|
|
|
+ await updateBottomMenu(data);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await addBottomMenu(data);
|
|
|
|
|
+ }
|
|
|
|
|
+ await loadBottomMenus();
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success(dialogType.value === 'add' ? '新增成功' : '修改成功');
|
|
|
|
|
+ dialogVisible.value = false;
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ ElMessage.error('保存失败');
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const handleDelete = (type: string, _index: number, row: any) => {
|
|
|
|
|
+ ElMessageBox.confirm('确定要删除该菜单吗?', '提示', { type: 'warning' }).then(async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (type === 'top') {
|
|
|
|
|
+ await delTopMenu(row.id);
|
|
|
|
|
+ await loadTopMenus();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await delBottomMenu(row.id);
|
|
|
|
|
+ await loadBottomMenus();
|
|
|
|
|
+ }
|
|
|
|
|
+ ElMessage.success('删除成功');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ ElMessage.error('删除失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ loadTopMenus();
|
|
|
|
|
+ loadBottomMenus();
|
|
|
|
|
+ loadPhoneConfig();
|
|
|
|
|
+ loadCopyright();
|
|
|
|
|
+});
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.menu-settings-container {
|
|
|
|
|
+ padding: 30px;
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ min-height: calc(100vh - 120px);
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.custom-tabs :deep(.el-tabs__nav-wrap::after) {
|
|
|
|
|
+ height: 1px;
|
|
|
|
|
+ background-color: #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.custom-tabs :deep(.el-tabs__item) {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ height: 50px;
|
|
|
|
|
+ line-height: 50px;
|
|
|
|
|
+ padding: 0 30px !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.custom-tabs :deep(.el-tabs__item.is-active) {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.custom-tabs :deep(.el-tabs__active-bar) {
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ height: 3px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 各 Tab 内容容器:加入最大高度和优雅的滚动条,确保内容超过区域均可在区域内上下滑动 */
|
|
|
|
|
+.tab-content {
|
|
|
|
|
+ padding-top: 25px;
|
|
|
|
|
+ max-height: calc(100vh - 210px); /* 动态视窗高度自适应减扣 */
|
|
|
|
|
+ overflow-y: auto; /* 开启上下滑动 */
|
|
|
|
|
+ padding-right: 12px; /* 预留滚动条物理宽度防止排版挤压抖动 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 高雅隐形纤细滚动条设计 */
|
|
|
|
|
+.tab-content::-webkit-scrollbar {
|
|
|
|
|
+ width: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-content::-webkit-scrollbar-thumb {
|
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.12);
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-content::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.24);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.tab-content::-webkit-scrollbar-track {
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.action-bar {
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: flex-start;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.btn-primary-blue {
|
|
|
|
|
+ background-color: #1890ff;
|
|
|
|
|
+ border-color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.btn-primary-blue:hover {
|
|
|
|
|
+ background-color: #40a9ff;
|
|
|
|
|
+ border-color: #40a9ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.text-blue {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 表格样式 */
|
|
|
|
|
+.standard-table {
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.table-header-custom) {
|
|
|
|
|
+ background-color: #fafafa !important;
|
|
|
|
|
+ color: #333 !important;
|
|
|
|
|
+ font-weight: bold !important;
|
|
|
|
|
+ height: 54px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.copyright-container {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.copyright-form {
|
|
|
|
|
+ max-width: 960px; /* 适当拉大表单最大宽度以容纳双列排布 */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-section {
|
|
|
|
|
+ margin-bottom: 30px;
|
|
|
|
|
+ background: #fcfcfc;
|
|
|
|
|
+ padding: 24px;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ border: 1px solid #f0f0f0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.section-title {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ padding-left: 12px;
|
|
|
|
|
+ border-left: 4px solid #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 网格布局:一排放2个字段 */
|
|
|
|
|
+.grid-form-fields {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 1fr 1fr; /* 完美平分两列 */
|
|
|
|
|
+ column-gap: 40px; /* 两列横向空隙极其通透 */
|
|
|
|
|
+ row-gap: 15px; /* 行间隙 */
|
|
|
|
|
+ align-items: center; /* 确保左右单元格在垂直方向上绝对居中对齐,杜绝高度拉伸错位! */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 确保网格内的表单项自身不带底边距,并强制水平横排 */
|
|
|
|
|
+.grid-form-fields :deep(.el-form-item) {
|
|
|
|
|
+ margin-bottom: 0 !important; /* 清除默认底边距对 Grid 单元格高度计算的物理干扰 */
|
|
|
|
|
+ display: flex !important;
|
|
|
|
|
+ align-items: center !important; /* 强制 label 与 input 纵向绝对水平线居中对齐! */
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 确保所有表单标签横排 white-space nowrap 彻底防换行 */
|
|
|
|
|
+:deep(.el-form-item__label) {
|
|
|
|
|
+ white-space: nowrap !important;
|
|
|
|
|
+ word-break: keep-all !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 上传框样式 */
|
|
|
|
|
+.upload-box-rect {
|
|
|
|
|
+ width: 160px;
|
|
|
|
|
+ height: 78px;
|
|
|
|
|
+ border: 1px dashed #d9d9d9;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ transition: border-color 0.3s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.upload-box-square {
|
|
|
|
|
+ width: 86px;
|
|
|
|
|
+ height: 86px;
|
|
|
|
|
+ border: 1px dashed #d9d9d9;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ transition: border-color 0.3s;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.upload-box-rect:hover,
|
|
|
|
|
+.upload-box-square:hover {
|
|
|
|
|
+ border-color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.preview-img-rect {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ object-fit: contain;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.preview-img-square {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ object-fit: contain;
|
|
|
|
|
+ padding: 5px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.upload-icon {
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.upload-text {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ margin-top: 5px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.field-tip {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ line-height: 1.5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 社交媒体布局 */
|
|
|
|
|
+.social-config-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 30px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.social-item {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ padding: 24px;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ border: 1px solid #eee;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.social-label {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-footer {
|
|
|
|
|
+ padding: 20px 0;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 弹窗样式 */
|
|
|
|
|
+.custom-dialog :deep(.el-dialog__header) {
|
|
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
+ margin-right: 0;
|
|
|
|
|
+ padding-bottom: 15px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.custom-dialog :deep(.el-dialog__title) {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.p-t-10 {
|
|
|
|
|
+ padding-top: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|