1、系统配置修改上线 2、 pinia取代vuex

This commit is contained in:
zhangtao
2025-01-10 10:39:15 +08:00
parent 8d9a64aed3
commit 1ae731eafe
21 changed files with 379 additions and 568 deletions
+4 -20
View File
@@ -1,32 +1,16 @@
import request from "@/utils/request";
export function getConfigList(params) {
export function getConfigInfo() {
return request({
url: "/api/v1/system/config/list",
url: "/api/v1/system/config/info",
method: "get",
params: params,
});
}
export function getConfigDetail(params) {
return request({
url: "/api/v1/system/config/detail",
method: "get",
params: params,
});
}
export function createConfig(body) {
export function updateConfig(body) {
return request({
url: "/api/v1/system/config/create",
method: "post",
data: body,
});
}
export function batchConfig(body) {
return request({
url: "/api/v1/system/config/batch",
url: "/api/v1/system/config/update",
method: "put",
data: body,
});
+12 -29
View File
@@ -150,7 +150,7 @@ import type { MenuProps, ItemType } from "ant-design-vue";
import { message } from 'ant-design-vue';
import { useRouter, useRoute } from "vue-router";
import storage from 'store';
import store from '@/store';
import { useUserStore } from "@/store/index";
import * as icons from '@ant-design/icons-vue';
import { h } from 'vue';
import { listToTree } from '@/utils/util';
@@ -168,6 +168,9 @@ import {
import { logout } from '@/api/system/auth';
import { getNoticeList } from '@/api/system/notice'
import { getInitConfig } from "@/api/system/config"
import { useConfigStore } from "@/store/index";
const userStore = useUserStore();
// 通知公告获取
const dataSource = reactive({
@@ -194,7 +197,7 @@ const router = useRouter();
const route = useRoute();
// 计算属性
const userInfo = computed(() => store.state.user.basicInfo);
const userInfo = computed(() => userStore.basicInfo);
const unreadCount = computed(() => dataSource.total); // 通知条数
// 菜单状态
@@ -229,7 +232,7 @@ const handleLogout = async () => {
await logout({ token: storage.get('Access-Token') });
storage.remove('Access-Token');
storage.remove('Refresh-Token');
await store.dispatch('clearUserInfo');
await userStore.clearUserInfo;
router.push('/login');
} catch (error) {
console.error('退出登录失败:', error);
@@ -244,7 +247,7 @@ const handleMenuClick: MenuProps['onClick'] = (menuInfo) => {
// 监听路由变化
watch(() => route.path, (newPath) => {
const routePath = store.state.user.routeList.map(item => item.route_path);
const routePath = userStore.routeList.map(item => item.route_path);
if (!routePath.includes(newPath)) {
menuState.openKeys = [];
menuState.selectedKeys = [];
@@ -274,11 +277,11 @@ const generateMenuItem = (item: any): ItemType => {
// 初始化菜单
const initMenu = () => {
// 确保路由列表存在
if (!store.state.user.routeList?.length) {
if (!userStore.routeList?.length) {
return;
}
const menuTree = listToTree(store.state.user.routeList);
const menuTree = listToTree(userStore.routeList);
menuState.menus = menuTree
.filter(item => !item.hidden)
.map(generateMenuItem)
@@ -328,38 +331,18 @@ const noticeState = reactive({
loading: false
});
const configStore = useConfigStore();
const initConfigState = reactive({
web_title: '',
web_favicon: '',
web_title: configStore.getConfigValue("title"),
web_favicon: configStore.getConfigValue("logo"),
});
const initConfig = () => {
getInitConfig()
.then(response => {
const { status_code, data } = response.data;
if (status_code === 200) {
const configData = JSON.parse(data);
configData.forEach(item => {
if (item.fied_key === 'web_title') {
initConfigState.web_title = item.fied_value;
} else if (item.fied_key === 'web_favicon') {
initConfigState.web_favicon = item.fied_value;
}
});
}
})
.catch(error => {
message.error('获取系统配置失败');
console.error(error); // 打印错误信息以便调试
});
};
// 在页面加载时获取通知列表
onMounted(() => {
initMenu();
handleNoticeList();
initConfig();
});
</script>
+27 -29
View File
@@ -6,39 +6,37 @@ import "ant-design-vue/dist/reset.css";
import VChart from "vue-echarts";
import "echarts";
import './style.css'
import { getInitConfig } from "@/api/system/config"
import { createPinia } from 'pinia';
import { useConfigStore } from "@/store/index";
const app = createApp(App);
const pinia = createPinia();
const initConfig = () => {
return getInitConfig()
.then((response) => {
const { status_code, data } = response.data;
if (status_code === 200) {
const configData = JSON.parse(data);
configData.forEach((item) => {
if (item.fied_key === "login_title") {
document.title = item.fied_value || "fastapi vue admin";
} else if (item.fied_key === "login_logo") {
const favicon = document.querySelector('link[rel="icon"]');
if (favicon) {
favicon.href = item.fied_value || "/logo.png";
}
}
});
}
})
.catch((error) => {
message.error("获取系统配置失败");
console.error(error); // 打印错误信息以便调试
});
app.use(pinia);
const initConfig = async () => {
const configStore = useConfigStore();
await configStore.fetchConfig();
const loginTitle = configStore.getConfigValue("title");
const loginLogo = configStore.getConfigValue("favicon");
if (loginTitle) {
document.title = loginTitle;
}
if (loginLogo) {
const favicon = document.querySelector('link[rel="icon"]');
if (favicon) {
favicon.href = loginLogo;
}
}
};
// 初始化配置并挂载应用
initConfig().then(() => {
app.use(router);
app.use(Antd);
app.component("VChart", VChart);
app.mount("#app");
});
app.use(router);
app.use(Antd);
app.component("VChart", VChart);
app.mount("#app");
});
-25
View File
@@ -1,25 +0,0 @@
const modules = import.meta.glob("../views/**/**.vue");
export const generator = (routers) => {
return routers.map((item) => {
const currentRouter = {
path: item.route_path,
name: item.route_name,
component: item.component_path
? modules[`../views/${item.component_path}.vue`]
: null,
redirect: item.redirect,
meta: {
title: item.name,
icon: item.icon || undefined,
keepAlive: item.cache,
hidden: item.hidden,
order: item.order,
},
};
if (item.children && item.children.length > 0) {
currentRouter.children = generator(item.children);
}
return currentRouter;
});
};
+39 -12
View File
@@ -1,10 +1,37 @@
import { createRouter, createWebHistory } from "vue-router";
import storage from "store";
import BasicLayout from "@/layouts/basicLayout.vue";
import Login from "@/views/system/auth/login.vue";
import storage from "store";
import store from "@/store";
import { generator } from "./generateRouter";
import { listToTree } from "@/utils/util";
import { useUserStore } from "@/store/index";
const modules = import.meta.glob("../views/**/**.vue");
export const generator = (routers) => {
return routers.map((item) => {
const currentRouter = {
path: item.route_path,
name: item.route_name,
component: item.component_path
? modules[`../views/${item.component_path}.vue`]
: null,
redirect: item.redirect,
meta: {
title: item.name,
icon: item.icon || undefined,
keepAlive: item.cache,
hidden: item.hidden,
order: item.order,
},
};
if (item.children && item.children.length > 0) {
currentRouter.children = generator(item.children);
}
return currentRouter;
});
};
const routes = [
{ path: "/login", name: "Login", component: Login },
@@ -39,21 +66,21 @@ const router = createRouter({
routes,
});
router.beforeEach((to) => {
router.beforeEach(async (to) => {
const token = storage.get("Access-Token");
const userStore = useUserStore();
if (!token && to.name !== "Login") {
return { name: "Login" };
} else if (token && to.name === "Login") {
return { name: "Index" };
} else if (token && !store.state.user.hasGetRoute) {
return store.dispatch("getUserInfo").then(() => {
const routersTree = listToTree(store.state.user.routeList);
const routerMap = generator(routersTree);
rootRouter.children = [...rootRouter.children, ...routerMap];
router.addRoute(rootRouter);
return to.fullPath;
});
} else if (token && !userStore.hasGetRoute) {
await userStore.getUserInfo();
const routersTree = listToTree(userStore.routeList);
const routerMap = generator(routersTree);
rootRouter.children = [...rootRouter.children, ...routerMap];
router.addRoute(rootRouter);
return to.fullPath;
}
});
export default router;
-26
View File
@@ -1,26 +0,0 @@
import { defineStore } from 'pinia';
import { getInitConfig } from "@/api/system/config";
export const useConfigStore = defineStore('config', {
state: () => ({
systemConfig: {},
}),
actions: {
async getConfigInfo() {
try {
const response = await getInitConfig();
const result = response.data;
const configData = JSON.parse(result.data);
this.systemConfig = configData;
} catch (error) {
console.error(error);
}
},
setSystemConfig(config) {
this.systemConfig = config;
},
clearConfigInfo() {
this.systemConfig = {};
},
},
});
+59 -10
View File
@@ -1,14 +1,63 @@
import { createStore } from "vuex";
import user from "./modules/user";
import { defineStore } from "pinia";
import { getCurrentUserInfo } from "@/api/system/user";
import { getInitConfig } from "@/api/system/config";
const store = createStore({
modules: {
user,
export const useUserStore = defineStore("user", {
state: () => ({
basicInfo: {},
routeList: [],
hasGetRoute: false,
}),
actions: {
async getUserInfo() {
try {
const response = await getCurrentUserInfo();
const result = response.data;
const routers = result.data.menus;
delete result.data.menus;
this.setRoute(routers);
this.basicInfo = { ...this.basicInfo, ...result.data };
} catch (error) {
console.error(error);
}
},
setRoute(routers) {
this.routeList = routers;
this.hasGetRoute = true;
},
setAvatar(avatar) {
this.basicInfo = { ...this.basicInfo, avatar };
},
clearUserInfo() {
this.basicInfo = {};
this.routeList = [];
this.hasGetRoute = false;
},
},
state: {},
mutations: {},
actions: {},
getters: {},
});
export default store;
export const useConfigStore = defineStore("config", {
state: () => ({
configData: {}, // 存储系统配置
isConfigLoaded: false, // 标记配置是否已加载
}),
actions: {
async fetchConfig() {
try {
const response = await getInitConfig();
const { status_code, data } = response.data;
if (status_code === 200) {
this.configData = JSON.parse(data);
console.log(this.configData);
this.isConfigLoaded = true;
}
} catch (error) {
console.error("获取系统配置失败", error);
}
},
getConfigValue(key) {
return this.configData[key] || null;
},
},
});
-44
View File
@@ -1,44 +0,0 @@
import { getCurrentUserInfo } from "@/api/system/user";
const user = {
state: {
basicInfo: {},
routeList: [],
hasGetRoute: false,
},
mutations: {
setRoute(state, routers) {
state.routeList = routers;
state.hasGetRoute = true;
},
setAvatar(state, avatar) {
state.basicInfo.avatar = avatar;
},
},
actions: {
getUserInfo({ commit, state }) {
return new Promise((resolve) => {
getCurrentUserInfo()
.then((response) => {
const result = response.data;
const routers = result.data.menus;
delete result.data.menus;
commit("setRoute", routers);
state.basicInfo = Object.assign(state.basicInfo || {}, result.data);
resolve();
})
.catch((error) => {
console.log(error);
});
});
},
clearUserInfo({ state }) {
state.basicInfo = {};
state.routeList = [];
state.hasGetRoute = false;
},
},
};
export default user;
-36
View File
@@ -1,36 +0,0 @@
import { defineStore } from 'pinia';
import { getCurrentUserInfo } from "@/api/system/user";
export const useUserStore = defineStore('user', {
state: () => ({
basicInfo: {},
routeList: [],
hasGetRoute: false,
}),
actions: {
async getUserInfo() {
try {
const response = await getCurrentUserInfo();
const result = response.data;
const routers = result.data.menus;
delete result.data.menus;
this.setRoute(routers);
this.basicInfo = Object.assign(this.basicInfo || {}, result.data);
} catch (error) {
console.error(error);
}
},
setRoute(routers) {
this.routeList = routers;
this.hasGetRoute = true;
},
setAvatar(avatar) {
this.basicInfo.avatar = avatar;
},
clearUserInfo() {
this.basicInfo = {};
this.routeList = [];
this.hasGetRoute = false;
},
},
});
+5 -3
View File
@@ -122,15 +122,17 @@ import { ref, reactive } from 'vue';
import PageHeader from '@/components/PageHeader.vue'
import { timeFix } from '@/utils/util';
import { PlusOutlined, UserOutlined } from '@ant-design/icons-vue';
import store from '@/store';
import { useUserStore } from "@/store/index";
const userStore = useUserStore();
const loading = ref(true);
let timefix = timeFix();
const userInfo = store.state.user.basicInfo;
const userInfo = userStore.basicInfo;
console.log("store.state.user.basicInfo",store.state.user.basicInfo)
console.log("store.state.user.basicInfo",userStore.basicInfo)
const welcome = '祝你开心每一天!';
const projectItems = [
+20 -65
View File
@@ -1,15 +1,15 @@
<template>
<a-layout>
<!-- 页面主体 -->
<div class="container" :style="{ backgroundImage: `url(${initConfigState.login_background})` }">
<div class="container" :style="{ backgroundImage: `url(${initConfigState.background})` }">
<a-layout-content :style="contentStyle">
<div class="header">
<div class="logo">
<a-image :src="initConfigState.login_logo" :preview="false" />
<a-image :src="initConfigState.logo" :preview="false" />
</div>
<div class="title">{{ initConfigState.login_title }}</div>
<div class="title">{{ initConfigState.title }}</div>
</div>
<div class="desc">{{ initConfigState.login_description }}</div>
<div class="desc">{{ initConfigState.description }}</div>
<div class="login-main" style="width: 330px; margin: 0 auto;">
<a-tabs centered>
@@ -69,13 +69,13 @@
{{ initConfigState.copyright }} |
</a-button>
<a-button type="link" :href="initConfigState.help_url">
{{ initConfigState.help_name }} |
帮助 |
</a-button>
<a-button type="link" :href="initConfigState.privacy_url">
{{ initConfigState.privacy_name }} |
隐私 |
</a-button>
<a-button type="link" :href="initConfigState.clause_url">
{{ initConfigState.clause_name }}
条款
</a-button>
</div>
<div class="footer-record">
@@ -161,8 +161,8 @@ import { save_token } from "@/utils/util"
import { message } from 'ant-design-vue';
import { login, getCaptcha } from "@/api/system/auth"
import { registerUser, forgetPassword } from "@/api/system/user"
import { getInitConfig } from "@/api/system/config"
import type { LoginForm, CaptchaState, ForgetPasswordForm, RegisterForm } from './types'
import { useConfigStore } from "@/store/index";
const router = useRouter();
const loginFlag = ref(false);
@@ -287,69 +287,24 @@ const requestCaptcha = () => {
.catch(() => captchaState.enable = false);
};
const configStore = useConfigStore();
const initConfigState = reactive({
login_title: '',
login_description: '/logo.png',
login_logo: '',
login_background: '/background.png',
copyright: '',
copyright_name: '',
keep_record: '',
keep_record_name: '',
help_url: '',
help_name: '',
privacy_url: '',
privacy_name: '',
clause_url: '',
clause_name: '',
code_url: '',
title: configStore.getConfigValue("title"),
description: configStore.getConfigValue("description"),
logo: configStore.getConfigValue("logo"),
background: configStore.getConfigValue("background"),
copyright: configStore.getConfigValue("copyright"),
keep_record: configStore.getConfigValue("keep_record"),
help_url: configStore.getConfigValue("help_url"),
privacy_url: configStore.getConfigValue("privacy_url"),
clause_url: configStore.getConfigValue("clause_url"),
code_url: configStore.getConfigValue("code_url"),
});
const initConfig = () => {
getInitConfig()
.then(response => {
const { status_code, data } = response.data;
if (status_code === 200) {
const configData = JSON.parse(data);
configData.forEach(item => {
if (item.fied_key === 'login_title') {
initConfigState.login_title = item.fied_value;
} else if (item.fied_key === 'login_description') {
initConfigState.login_description = item.fied_value;
} else if (item.fied_key === 'login_logo') {
initConfigState.login_logo = item.fied_value;
} else if (item.fied_key === 'login_background') {
initConfigState.login_background = item.fied_value;
} else if (item.fied_key === 'copyright') {
initConfigState.copyright = item.fied_value;
initConfigState.copyright_name = item.name;
} else if (item.fied_key === 'keep_record') {
initConfigState.keep_record = item.fied_value;
initConfigState.keep_record_name = item.name;
} else if (item.fied_key === 'help_url') {
initConfigState.help_url = item.fied_value;
initConfigState.help_name = item.name;
} else if (item.fied_key === 'privacy_url') {
initConfigState.privacy_url = item.fied_value;
initConfigState.privacy_name = item.name;
} else if (item.fied_key === 'clause_url') {
initConfigState.clause_url = item.fied_value;
initConfigState.clause_name = item.name;
} else if (item.fied_key === 'code_url') {
initConfigState.code_url = item.fied_value;
}
});
}
})
.catch(error => {
message.error('获取系统配置失败');
console.error(error); // 打印错误信息以便调试
});
};
onMounted(() => {
requestCaptcha();
initConfig();
});
</script>
+138 -84
View File
@@ -7,35 +7,94 @@
<div class="config-edit-wrapper">
<a-card title="系统配置" :bordered="false">
<!-- 动态生成配置项 -->
<template v-for="group in updateState" :key="group.id">
<a-card :title="group.name" :bordered="false" style="margin-bottom: 24px;">
<a-row :gutter="16">
<template v-for="config in group.children" :key="config.id">
<a-col :span="12">
<!-- 图片上传类型 -->
<a-form-item v-if="config.fied_key === 'web_favicon' || config.fied_key === 'login_logo' || config.fied_key === 'login_background'" :label="config.name">
<a-upload
v-model:file-list="config.fileList"
list-type="picture-card"
:before-upload="beforeUpload"
:custom-request="(options) => handleUpload(options, config)"
>
<div v-if="!config.fileList || config.fileList.length === 0">
<plus-outlined />
<div style="margin-top: 8px">上传图片</div>
</div>
</a-upload>
</a-form-item>
<a-row :gutter="16">
<a-col :span="12">
<!-- 网站标题 -->
<a-form-item label="网站标题">
<a-input v-model:value="configData.title" placeholder="请输入网站标题" allowClear />
</a-form-item>
<!-- 输入框类型 -->
<a-form-item v-else :label="config.name">
<a-input v-model:value="config.fied_value" :placeholder="`请输入${config.name}`" allowClear />
</a-form-item>
</a-col>
</template>
</a-row>
</a-card>
</template>
<!-- 网站图标 -->
<a-form-item label="网站图标">
<a-upload
v-model:file-list="faviconFileList"
list-type="picture-card"
:before-upload="beforeUpload"
:custom-request="(options) => handleUpload(options, 'favicon')"
>
<div v-if="!faviconFileList || faviconFileList.length === 0">
<plus-outlined />
<div style="margin-top: 8px">上传图标</div>
</div>
</a-upload>
</a-form-item>
<!-- 登录页Logo -->
<a-form-item label="登录页Logo">
<a-upload
v-model:file-list="logoFileList"
list-type="picture-card"
:before-upload="beforeUpload"
:custom-request="(options) => handleUpload(options, 'logo')"
>
<div v-if="!logoFileList || logoFileList.length === 0">
<plus-outlined />
<div style="margin-top: 8px">上传Logo</div>
</div>
</a-upload>
</a-form-item>
<!-- 登录页背景图 -->
<a-form-item label="登录页背景图">
<a-upload
v-model:file-list="backgroundFileList"
list-type="picture-card"
:before-upload="beforeUpload"
:custom-request="(options) => handleUpload(options, 'background')"
>
<div v-if="!backgroundFileList || backgroundFileList.length === 0">
<plus-outlined />
<div style="margin-top: 8px">上传背景图</div>
</div>
</a-upload>
</a-form-item>
<!-- 版权信息 -->
<a-form-item label="网站描述">
<a-input v-model:value="configData.description" placeholder="请输入版权信息" allowClear />
</a-form-item>
<!-- 版权信息 -->
<a-form-item label="版权信息">
<a-input v-model:value="configData.copyright" placeholder="请输入版权信息" allowClear />
</a-form-item>
<!-- 备案号 -->
<a-form-item label="备案号">
<a-input v-model:value="configData.keep_record" placeholder="请输入备案号" allowClear />
</a-form-item>
<!-- 帮助链接 -->
<a-form-item label="帮助链接">
<a-input v-model:value="configData.help_url" placeholder="请输入帮助链接" allowClear />
</a-form-item>
<!-- 隐私条款链接 -->
<a-form-item label="隐私条款链接">
<a-input v-model:value="configData.privacy_url" placeholder="请输入隐私条款链接" allowClear />
</a-form-item>
<!-- 服务条款链接 -->
<a-form-item label="服务条款链接">
<a-input v-model:value="configData.clause_url" placeholder="请输入服务条款链接" allowClear />
</a-form-item>
<!-- 代码仓库链接 -->
<a-form-item label="代码仓库链接">
<a-input v-model:value="configData.code_url" placeholder="请输入代码仓库链接" allowClear />
</a-form-item>
</a-col>
</a-row>
<!-- 保存按钮 -->
<div style="text-align: right; margin-top: 24px;">
@@ -47,46 +106,53 @@
</template>
<script lang="ts" setup>
import { reactive, onMounted } from 'vue';
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { getConfigList, batchConfig, uploadFile } from '@/api/system/config';
import { getConfigInfo, updateConfig, uploadFile } from '@/api/system/config';
import PageHeader from '@/components/PageHeader.vue';
import type { tableDataType } from './types'
import type { tableDataType } from './types';
interface ConfigGroup extends tableDataType {
children: tableDataType[];
fileList?: any[];
}
// 配置数据
const configData = reactive<tableDataType>({
id: 1,
title: 'FastAPI Vue Admin',
favicon: 'http://example.com/favicon.png',
logo: 'http://example.com/logo.png',
background: 'http://example.com/background.png',
description: 'FastAPI Vue Admin 是完全开源的权限管理系统',
copyright: 'Copyright © 2021-2025 fastapi-vue-admin.com',
keep_record: '晋ICP备18005113号-3',
help_url: 'https://django-vue-admin.com',
privacy_url: '/api/system/clause/privacy.html',
clause_url: '/api/system/clause/terms_service.html',
code_url: 'https://gitee.com/tao__tao/fastapi_vue_admin.git',
});
const updateState = reactive<ConfigGroup[]>([]);
// 文件上传列表
const faviconFileList = ref<any[]>([]);
const logoFileList = ref<any[]>([]);
const backgroundFileList = ref<any[]>([]);
// 加载配置数据
const loadConfigData = async () => {
try {
const response = await getConfigList({});
const items = response.data.data.items;
const response = await getConfigInfo({});
const data = response.data.data;
// 将配置项按父级分组
const groups = items
.filter(item => item.parent_id === null) // 获取顶级配置项
.map(group => ({
...group,
children: items.filter(item => item.parent_id === group.id) // 获取子配置项
}));
// 填充配置数据
Object.assign(configData, data);
// 初始化图片上传列表
groups.forEach(group => {
group.children.forEach(config => {
if (config.fied_key === 'web_favicon' || config.fied_key === 'login_logo' || config.fied_key === 'login_background') {
config.fileList = config.fied_value ? [{ url: config.fied_value }] : [];
}
});
});
// 避免直接修改 reactive 对象,使用重新赋值的方式
updateState.length = 0; // 清空数组
updateState.push(...groups); // 添加新数据
// 初始化文件上传列表
if (data.favicon) {
faviconFileList.value = [{ url: data.favicon }];
}
if (data.logo) {
logoFileList.value = [{ url: data.logo }];
}
if (data.background) {
backgroundFileList.value = [{ url: data.background }];
}
} catch (error) {
console.error('加载配置数据失败:', error);
message.error('加载配置数据失败');
@@ -103,7 +169,7 @@ const beforeUpload = (file: File) => {
};
// 自定义上传逻辑
const handleUpload = async (options: any, config: any) => {
const handleUpload = async (options: any, type: string) => {
const { file, onSuccess, onError } = options;
try {
@@ -111,11 +177,19 @@ const handleUpload = async (options: any, config: any) => {
formData.append('file', file);
const response = await uploadFile(formData);
const fileUrl = response.data.data.file_url; // 使用 file_url 更新配置项的值
const fileUrl = response.data.data.file_url;
// 更新配置项的值
config.fied_value = fileUrl;
config.fileList = [{ url: fileUrl }]; // 更新文件列表显示
// 更新配置数据
if (type === 'favicon') {
configData.favicon = fileUrl;
faviconFileList.value = [{ url: fileUrl }];
} else if (type === 'logo') {
configData.logo = fileUrl;
logoFileList.value = [{ url: fileUrl }];
} else if (type === 'background') {
configData.background = fileUrl;
backgroundFileList.value = [{ url: fileUrl }];
}
onSuccess(response, file);
message.success('上传成功');
@@ -129,28 +203,8 @@ const handleUpload = async (options: any, config: any) => {
// 保存配置
const handleSave = async () => {
try {
// 提取所有配置项(包括父级和子级)
const configs = updateState.flatMap(group => [
{
id: group.id,
name: group.name,
order: group.order,
fied_key: group.fied_key,
fied_value: group.fied_value,
parent_id: group.parent_id,
},
...group.children.map(config => ({
id: config.id,
name: config.name,
order: config.order,
fied_key: config.fied_key,
fied_value: config.fied_value,
parent_id: config.parent_id,
})),
]);
// 调用批量保存接口,直接发送数组
await batchConfig(configs); // 修改为直接发送数组
// 调用更新配置接口
await updateConfig(configData);
message.success('配置保存成功');
} catch (error) {
console.error('保存配置失败:', error);
+13 -9
View File
@@ -1,10 +1,14 @@
export interface tableDataType {
id?: number;
name?: string;
order?: number;
fied_key?: string;
fied_value?: string;
parent_id?: number;
fileList?: any[];
children?: tableDataType[];
}
id?: number; // id 是可选的,类型为 number
title: string; // 网站标题
favicon: string; // 网站图标
logo: string; // 登录页Logo
background: string; // 登录页背景图
description: string; // 网站描述
copyright: string; // 版权信息
keep_record: string; // 备案号
help_url: string; // 帮助链接
privacy_url: string; // 隐私条款链接
clause_url: string; // 服务条款链接
code_url: string; // 代码仓库链接
}