feat: redesign system configuration workspace

This commit is contained in:
2026-08-04 15:07:01 +08:00
parent c4de4e8c56
commit 3b9215cc3d
8 changed files with 901 additions and 153 deletions

View File

@@ -6,7 +6,6 @@ import AdminLoginView from "./components/AdminLoginView.vue";
import TableRowActions from "./components/TableRowActions.vue";
import {
systemSettingDefinitions,
systemSettingSections,
type SystemSettingValue,
} from "./config/systemSettings";
import { api, clearToken, getToken, saveToken } from "./services/api";
@@ -47,6 +46,9 @@ const UserManagementView = defineAsyncComponent(
const SsoIntegrationView = defineAsyncComponent(
() => import("./components/SsoIntegrationView.vue"),
);
const SystemConfigView = defineAsyncComponent(
() => import("./components/SystemConfigView.vue"),
);
const admin = ref<AdminProfile | null>(null);
const activeMenu = ref("dashboard");
@@ -55,6 +57,8 @@ const loading = ref(false);
const entitlementPlans = ref<EntitlementPlan[]>([]);
const models = ref<ModelItem[]>([]);
const configs = ref<SystemConfigItem[]>([]);
const savingSystemSettings = ref(false);
const systemSettingsBaseline = ref<Record<string, string>>({});
const previewKnowledgeId = ref<number | null>(null);
const editingModelId = ref<number | null>(null);
@@ -174,6 +178,13 @@ const systemSettingValues = reactive<Record<string, SystemSettingValue>>({});
systemSettingDefinitions.forEach((setting) => {
systemSettingValues[setting.key] = setting.defaultValue;
});
const systemSettingsDirty = computed(() =>
systemSettingDefinitions.some(
(setting) =>
serializeSystemSettingValue(systemSettingValues[setting.key]) !==
systemSettingsBaseline.value[setting.key],
),
);
onMounted(async () => {
if (!getToken()) return;
@@ -490,20 +501,30 @@ function validateModelForm() {
}
async function saveSystemSettings() {
await Promise.all(
systemSettingDefinitions.map((setting) =>
api.saveConfig({
configKey: setting.key,
configValue: serializeSystemSettingValue(
systemSettingValues[setting.key],
),
description: setting.description,
}),
),
);
ElMessage.success("系统配置已保存");
configs.value = await api.configs();
hydrateSystemSettings();
if (savingSystemSettings.value || !systemSettingsDirty.value) return;
savingSystemSettings.value = true;
try {
await Promise.all(
systemSettingDefinitions.map((setting) =>
api.saveConfig({
configKey: setting.key,
configValue: serializeSystemSettingValue(
systemSettingValues[setting.key],
),
description: setting.description,
}),
),
);
configs.value = await api.configs();
hydrateSystemSettings();
ElMessage.success("系统配置已保存");
} catch (error) {
ElMessage.error(
error instanceof Error ? error.message : "系统配置保存失败,请重试",
);
} finally {
savingSystemSettings.value = false;
}
}
function hydrateSystemSettings() {
@@ -516,6 +537,17 @@ function hydrateSystemSettings() {
configMap.get(setting.key),
);
});
systemSettingsBaseline.value = Object.fromEntries(
systemSettingDefinitions.map((setting) => [
setting.key,
serializeSystemSettingValue(systemSettingValues[setting.key]),
]),
);
}
function updateSystemSetting(key: string, value: SystemSettingValue) {
if (systemSettingValues[key] === value) return;
systemSettingValues[key] = value;
}
function normalizeSystemSettingValue(
@@ -1014,81 +1046,14 @@ async function clearFeishuCache() {
<SsoIntegrationView v-if="activeMenu === 'sso'" />
<template v-if="activeMenu === 'configs'">
<div class="page-head inline">
<div>
<h2>系统配置</h2>
<p>
按业务分组维护运行时配置默认每日额度已接入学员新增和导入
</p>
</div>
<el-button type="primary" @click="saveSystemSettings"
>保存系统配置</el-button
>
</div>
<section class="settings-grid">
<div
v-for="section in systemSettingSections"
:key="section.title"
class="settings-section"
>
<div class="settings-section-head">
<h3>{{ section.title }}</h3>
<p>{{ section.description }}</p>
</div>
<div class="settings-fields">
<label
v-for="setting in section.settings"
:key="setting.key"
class="setting-field"
>
<span>{{ setting.label }}</span>
<el-input-number
v-if="setting.type === 'number'"
v-model="systemSettingValues[setting.key]"
:min="setting.min"
:max="setting.max"
/>
<el-input
v-else-if="setting.type === 'text'"
v-model="systemSettingValues[setting.key]"
:placeholder="setting.placeholder"
/>
<el-input
v-else-if="setting.type === 'password'"
v-model="systemSettingValues[setting.key]"
:placeholder="setting.placeholder"
type="password"
show-password
/>
<el-select
v-else-if="setting.type === 'select'"
v-model="systemSettingValues[setting.key]"
:placeholder="setting.placeholder"
>
<el-option
v-for="option in setting.options || []"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
<el-switch
v-else
v-model="systemSettingValues[setting.key]"
/>
<small>{{ setting.description }}</small>
<small
v-if="setting.valueHint"
class="setting-value-hint"
>
{{ setting.valueHint(systemSettingValues[setting.key]) }}
</small>
</label>
</div>
</div>
</section>
</template>
<SystemConfigView
v-if="activeMenu === 'configs'"
:values="systemSettingValues"
:saving="savingSystemSettings"
:dirty="systemSettingsDirty"
@update="updateSystemSetting"
@save="saveSystemSettings"
/>
<RetrievalLogView v-if="activeMenu === 'retrievals'" />
<AttentionManagementView v-if="activeMenu === 'attention'" />