forked from tools/tape-springboot-sysadmin
init
This commit is contained in:
429
src/views/setting/IndexSetting.vue
Normal file
429
src/views/setting/IndexSetting.vue
Normal file
@@ -0,0 +1,429 @@
|
||||
<template>
|
||||
<div class="setting-config">
|
||||
<a-spin :spinning="loading">
|
||||
<div class="config-box" v-if="settingList">
|
||||
<template v-if="settingList && settingList.length > 0">
|
||||
<div class="config-wrap">
|
||||
<!-- 左侧设置菜单 -->
|
||||
<div class="config-left">
|
||||
<a-menu
|
||||
mode="inline"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
class="setting-menu"
|
||||
>
|
||||
<a-menu-item
|
||||
v-for="item in settingList"
|
||||
:key="item.key"
|
||||
@click="handleMenuClick(item.key)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
|
||||
<!-- 右侧配置表单 -->
|
||||
<div class="config-right">
|
||||
<a-spin :spinning="detailLoading">
|
||||
<div v-if="currentSetting && currentSetting.settingInfo">
|
||||
<div class="config-title">
|
||||
<span>
|
||||
{{ currentSetting.settingInfo.title }}
|
||||
<small v-if="currentSetting.settingInfo.desc">
|
||||
({{ currentSetting.settingInfo.desc }})
|
||||
</small>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!!currentSetting.settingInfo.readme">
|
||||
<a-alert :message="currentSetting.settingInfo.readme" banner />
|
||||
</div>
|
||||
<div class="config-form">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="editModel"
|
||||
@submit.prevent
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item>
|
||||
<IViewForm
|
||||
ref="configFormRef"
|
||||
v-model="editModel.settingValue"
|
||||
model-format="json"
|
||||
:config="configFormJson"
|
||||
:read-only="false"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
:disabled="submitLoading || detailLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
保存
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</div>
|
||||
<a-empty
|
||||
v-else-if="!detailLoading"
|
||||
:description="detailErrorMsg || '请选择左侧配置项'"
|
||||
style="margin-top: 60px"
|
||||
>
|
||||
<!-- <a-button
|
||||
v-if="detailErrorMsg && currentSettingKey"
|
||||
@click="fetchSettingDetail"
|
||||
size="small"
|
||||
>
|
||||
重新加载
|
||||
</a-button> -->
|
||||
</a-empty>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-empty v-else description="暂无可配置选项">
|
||||
<a-button @click="refresh" size="small"> 刷新列表 </a-button>
|
||||
</a-empty>
|
||||
</div>
|
||||
<a-empty v-else-if="!loading" :description="errorMsg" style="margin-top: 60px">
|
||||
<a-button v-if="errorMsg" @click="refresh"> 重新加载 </a-button>
|
||||
</a-empty>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { showErrorTips, showSuccessTips } from "@/iview/utils/messageTips";
|
||||
import { apiRequest } from "@/iview/utils/request";
|
||||
import IViewForm from "@/iview/IViewForm.vue";
|
||||
|
||||
// 响应式变量
|
||||
const loading = ref(false);
|
||||
const errorMsg = ref(null);
|
||||
const settingList = ref(null);
|
||||
const detailLoading = ref(false);
|
||||
const detailErrorMsg = ref(null);
|
||||
const currentSetting = ref(null);
|
||||
const currentSettingKey = ref("");
|
||||
const selectedKeys = ref([]);
|
||||
const submitLoading = ref(false);
|
||||
|
||||
// 编辑表单模型
|
||||
const editModel = reactive({
|
||||
settingKey: "",
|
||||
settingValue: "{}",
|
||||
});
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref(null);
|
||||
const configFormRef = ref(null);
|
||||
|
||||
// 表单配置字段列表
|
||||
const configFormFields = computed(() => {
|
||||
if (
|
||||
!currentSetting.value ||
|
||||
!currentSetting.value.settingInfo ||
|
||||
!currentSetting.value.settingInfo.fields
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 处理特殊的条件显示逻辑
|
||||
const processedFields = currentSetting.value.settingInfo.fields.map((field) => {
|
||||
// 克隆字段以避免修改原始数据
|
||||
const processedField = { ...field };
|
||||
// 处理条件逻辑 - 保留原始的条件表达式
|
||||
// CustomFormEditor组件会根据表达式动态处理显示/隐藏逻辑
|
||||
return processedField;
|
||||
});
|
||||
|
||||
return processedFields;
|
||||
});
|
||||
|
||||
// 表单配置JSON
|
||||
const configFormJson = computed(() => {
|
||||
return JSON.stringify({
|
||||
fields: configFormFields.value,
|
||||
});
|
||||
});
|
||||
|
||||
// 处理菜单点击事件
|
||||
const handleMenuClick = (key) => {
|
||||
currentSettingKey.value = key;
|
||||
selectedKeys.value = [key];
|
||||
fetchSettingDetail();
|
||||
};
|
||||
|
||||
// 获取配置详情
|
||||
const fetchSettingDetail = async () => {
|
||||
if (!currentSettingKey.value) return;
|
||||
|
||||
detailErrorMsg.value = null;
|
||||
detailLoading.value = true;
|
||||
|
||||
try {
|
||||
// 调用真实接口获取配置详情
|
||||
const res = await apiRequest("api.system.global.setting.detail", {
|
||||
settingKey: currentSettingKey.value,
|
||||
});
|
||||
currentSetting.value = res;
|
||||
// 初始化编辑表单
|
||||
editModel.settingKey = currentSettingKey.value;
|
||||
editModel.settingValue = res.settingValue;
|
||||
} catch (err) {
|
||||
detailErrorMsg.value =
|
||||
(err && err.msg) || (err && err.message) || "连接服务器失败,请检查网络";
|
||||
currentSetting.value = null;
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取配置列表
|
||||
const refresh = async () => {
|
||||
errorMsg.value = null;
|
||||
loading.value = true;
|
||||
try {
|
||||
// 调用真实接口获取配置列表
|
||||
const res = await apiRequest("api.system.global.setting.list", {});
|
||||
// 过滤掉隐藏的设置项
|
||||
settingList.value = (res || []).filter((item) => !item.hidden);
|
||||
if (settingList.value.length > 0) {
|
||||
// 默认选中第一项
|
||||
currentSettingKey.value = settingList.value[0].key;
|
||||
selectedKeys.value = [currentSettingKey.value];
|
||||
fetchSettingDetail();
|
||||
}
|
||||
} catch (err) {
|
||||
errorMsg.value =
|
||||
(err && err.msg) || (err && err.message) || "连接服务器失败,请检查网络";
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 如果有表单校验,进行校验
|
||||
if (configFormRef.value) {
|
||||
await configFormRef.value.validate();
|
||||
}
|
||||
submitLoading.value = true;
|
||||
// 调用真实接口保存配置
|
||||
await apiRequest("api.system.global.setting.edit", {
|
||||
settingKey: editModel.settingKey,
|
||||
settingValue: editModel.settingValue,
|
||||
});
|
||||
showSuccessTips("保存成功");
|
||||
// 重新获取配置详情,确保数据最新
|
||||
await fetchSettingDetail();
|
||||
} catch (err) {
|
||||
showErrorTips(err);
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.setting-config {
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.config-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.config-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 18px;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.config-left {
|
||||
width: 210px;
|
||||
flex: 0 0 210px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #eef6ff 0%, #ffffff 72%);
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.05);
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.config-left-header {
|
||||
padding: 18px 18px 12px;
|
||||
/* background: linear-gradient(135deg, #eef6ff 0%, #ffffff 72%); */
|
||||
}
|
||||
|
||||
.config-left-title {
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.config-left-desc {
|
||||
margin-top: 4px;
|
||||
color: #8c96a8;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.setting-menu {
|
||||
padding: 8px;
|
||||
border-right: none;
|
||||
background: transparent;
|
||||
border-inline-end: none !important;
|
||||
}
|
||||
|
||||
.config-right {
|
||||
flex: 1;
|
||||
min-height: 300px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.config-panel {
|
||||
overflow: hidden;
|
||||
min-height: 300px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.config-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 22px 24px 18px;
|
||||
background: linear-gradient(135deg, #f7fbff 0%, #ffffff 70%);
|
||||
}
|
||||
|
||||
.config-title-text {
|
||||
color: #1f2937;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.config-title-desc {
|
||||
margin-top: 6px;
|
||||
color: #8c96a8;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.config-readme {
|
||||
margin: 0 24px 4px;
|
||||
}
|
||||
|
||||
.config-form {
|
||||
padding: 20px 24px 6px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.config-btns {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.config-empty {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 769px) {
|
||||
.setting-config,
|
||||
.config-box,
|
||||
.config-wrap,
|
||||
.config-left,
|
||||
.config-right,
|
||||
.config-panel {
|
||||
min-height: calc(100vh - 220px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 修复表单叠加问题 */
|
||||
:deep(.ant-form) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.custom-form-editor) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-control) {
|
||||
display: block;
|
||||
flex: 1 1 auto !important;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item-control-input) {
|
||||
min-height: unset !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-textarea-wrapper) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-input-textarea) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:deep(.setting-menu .ant-menu-item) {
|
||||
height: 40px;
|
||||
margin: 4px 0;
|
||||
border-radius: 10px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
:deep(.setting-menu .ant-menu-item-selected) {
|
||||
color: #1677ff;
|
||||
font-weight: 600;
|
||||
background: rgba(22, 119, 255, 0.1);
|
||||
}
|
||||
|
||||
:deep(.config-readme .ant-alert) {
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
background: #f6faff;
|
||||
}
|
||||
|
||||
/* 响应式布局 */
|
||||
@media screen and (max-width: 768px) {
|
||||
.config-wrap {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.config-left {
|
||||
width: 100%;
|
||||
flex: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.config-right {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.config-btns {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user