This commit is contained in:
扶桑花间 2025-08-25 22:45:07 +08:00
parent 907eb67dc7
commit 5acd35774f
9 changed files with 567 additions and 453 deletions

View File

@ -16,9 +16,9 @@ class ConfigController extends BaseController
'group'=> $this->request->get('group/s','')
]);
$list = CurdService::getList($this->request, $model)->toArray();
$data = CurdService::getList($this->request, $model)->toArray();
$data = list_build_tree($list);
// $data = list_build_tree($list);
return $this->writeSuccess('success', $data);
}
@ -43,9 +43,9 @@ class ConfigController extends BaseController
'comments' => '',
'group'=> ''
]);
$data['item_bind'] = $this->request->post('itemBind');
$data['item_style'] = $this->request->post('itemStyle');
$data['item_class'] = $this->request->post('itemClass');
$data['item_bind'] = $this->request->post('itemBind','{}');
$data['item_style'] = $this->request->post('itemStyle','');
$data['item_class'] = $this->request->post('itemClass','');
$user = new SysConfig();

View File

@ -19,7 +19,7 @@ export async function configData(params: ConfigParam) {
/**
*
*/
export async function listConfig(params?: ConfigParam) {
export async function listConfigs(params?: ConfigParam) {
const res = await request.get<ApiResult<Config[]>>('/system/config', {
params
});
@ -29,7 +29,6 @@ export async function listConfig(params?: ConfigParam) {
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
@ -56,19 +55,23 @@ export async function updateConfig(data: Config) {
*
*/
export async function updateConfigStatus(id: number, status: number) {
const res = await request.put<ApiResult<unknown>>('/system/config/status', {id, status});
const res = await request.put<ApiResult<unknown>>('/system/config/status', {
id,
status
});
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}
/**
*
*/
export async function removeConfigs(ids: number[]) {
const res = await request.delete<ApiResult<unknown>>('/system/config/batch', {data: ids});
const res = await request.delete<ApiResult<unknown>>('/system/config/batch', {
data: ids
});
if (res.data.code === 0) {
return res.data.message;
}

View File

@ -4,12 +4,17 @@ import type { PageParam } from '@/api';
*
*/
export interface Config {
id?: number;
pid?: number;
title?: string;
type?: string;
comments?: string;
itemStyle?: string;
itemBind?: string;
itemClass?: string;
}
/**
*
*/
export interface ConfigParam extends PageParam {
}
export interface ConfigParam extends PageParam {}

View File

@ -2,50 +2,51 @@
*
*/
export enum ItemType {
SEPARATOR = "-",
ALERT = "alert",
CARD = "card",
COLLAPSE = "collapse",
TABS = "tabs",
TEXT = "text",
TEXTAREA = "textarea",
PASSWORD = "password",
CHECKBOX = "checkbox",
RADIO = "radio",
DATE = "date",
DATETIME = "datetime",
TIME = "time",
SWITCH = "switch",
HIDDEN = "hidden",
RANGE = "range",
DATE_RANGE = "date_range",
DATETIME_RANGE = "datetime_range",
TIME_RANGE = "time_range",
NUMBER = "number"
SEPARATOR = 'separator',
ALERT = 'alert',
CARD = 'card',
COLLAPSE = 'collapse',
TABS = 'tabs',
TABS_ITEM = 'tabs_item',
TEXT = 'text',
TEXTAREA = 'textarea',
PASSWORD = 'password',
CHECKBOX = 'checkbox',
RADIO = 'radio',
DATE = 'date',
DATETIME = 'datetime',
TIME = 'time',
SWITCH = 'switch',
HIDDEN = 'hidden',
RANGE = 'range',
DATE_RANGE = 'date_range',
DATETIME_RANGE = 'datetime_range',
TIME_RANGE = 'time_range',
NUMBER = 'number'
}
export const ItemLabels: Record<ItemType, string> = {
[ItemType.SEPARATOR]: "分割线",
[ItemType.ALERT]: "提示",
[ItemType.CARD]: "卡片",
[ItemType.COLLAPSE]: "折叠面板",
[ItemType.TABS]: "Tabs",
[ItemType.TEXT]: "单行文本",
[ItemType.TEXTAREA]: "多行文本",
[ItemType.PASSWORD]: "密码",
[ItemType.CHECKBOX]: "复选框",
[ItemType.RADIO]: "单选按钮",
[ItemType.DATE]: "日期",
[ItemType.DATETIME]: "日期+时间",
[ItemType.TIME]: "时间",
[ItemType.SWITCH]: "开关",
[ItemType.HIDDEN]: "隐藏",
[ItemType.TIME_SECOND]: "时间",
[ItemType.RANGE]: "范围",
[ItemType.DATE_RANGE]: "日期范围",
[ItemType.DATETIME_RANGE]: "日期时间范围",
[ItemType.TIME_RANGE]: "时间范围",
[ItemType.NUMBER]: "数字",
[ItemType.SEPARATOR]: '分割线',
[ItemType.ALERT]: '提示',
[ItemType.CARD]: '卡片',
[ItemType.COLLAPSE]: '折叠面板',
[ItemType.TABS]: 'Tabs',
[ItemType.TABS_ITEM]: 'Tabs子项',
[ItemType.TEXT]: '单行文本',
[ItemType.TEXTAREA]: '多行文本',
[ItemType.PASSWORD]: '密码',
[ItemType.CHECKBOX]: '复选框',
[ItemType.RADIO]: '单选按钮',
[ItemType.DATE]: '日期',
[ItemType.DATETIME]: '日期+时间',
[ItemType.TIME]: '时间',
[ItemType.SWITCH]: '开关',
[ItemType.HIDDEN]: '隐藏',
[ItemType.RANGE]: '范围',
[ItemType.DATE_RANGE]: '日期范围',
[ItemType.DATETIME_RANGE]: '日期时间范围',
[ItemType.TIME_RANGE]: '时间范围',
[ItemType.NUMBER]: '数字'
};
export function getItemLabel(type: ItemType): string {

View File

@ -1,34 +1,54 @@
<template>
<template v-for="config in configList">
<template v-if="config.type === '-'">
<el-divider v-bind="config.bind">{{config.title}}</el-divider>
<template v-if="config.type === 'separator'">
<el-divider v-bind="config.bind">{{ config.title }}</el-divider>
</template>
<template v-else-if="config.type === 'alert'">
<el-alert :title="config.title" v-bind="config.bind" />
</template>
<template v-else-if="config.type === 'card'">
<ele-card shadow="always" :header="config.title" v-bind="config.bind">
<config-form-list :config-list="config.children" v-if="config.children && config.children.length > 0" />
<config-form-list
:config-list="config.children"
v-if="config.children && config.children.length > 0"
/>
</ele-card>
</template>
<template v-else-if="config.type === 'collapse'">
<el-collapse v-bind="config.bind">
<el-collapse v-bind="config.bind">
<el-collapse-item :title="config.title">
<config-form-list :config-list="config.children" v-if="config.children && config.children.length > 0" />
<config-form-list
:config-list="config.children"
v-if="config.children && config.children.length > 0"
/>
</el-collapse-item>
</el-collapse>
</template>
<template v-else-if="config.type === 'tabs'">
<el-tabs
type="card"
:model-value="defaultTabs(config)"
>
<el-tab-pane v-for="(label, name) in config.option" :label="label" :name="name">{{label}}</el-tab-pane>
<el-tabs type="card" :model-value="defaultTabs(config)">
<el-tab-pane
v-for="tabPane in config.children"
:label="tabPane.title"
:name="tabPane.id"
:key="tabPane.id"
>
<config-form-list
:config-list="tabPane.children"
v-if="tabPane.children && tabPane.children.length > 0"
/>
</el-tab-pane>
</el-tabs>
</template>
<template v-else>
<config-form-item :name="config.name" :title="config.title" :type="config.type" :value="config.value"
:option="config.option" :item-bind="config.bind"/>
<config-form-item
:name="config.name"
:title="config.title"
:type="config.type"
:value="config.value"
:option="config.option"
:item-bind="config.bind"
:key="config.id"
/>
<el-form-item v-if="config.tips">
<ele-text type="placeholder" style="line-height: 1.6">
{{ config.tips }}
@ -39,15 +59,15 @@
</template>
<script setup lang="ts">
import ConfigFormItem from "../components/config-form-item.vue";
const props = defineProps<{
configList: any;
}>();
const defaultTabs = (cfg: any)=>{
let keys = Object.keys(cfg.option)
if(keys.length > 0) {
return keys[0];
}
return null;
}
import ConfigFormItem from '../components/config-form-item.vue';
const props = defineProps<{
configList: any;
}>();
const defaultTabs = (cfg: any) => {
let keys = Object.keys(cfg.option);
if (keys.length > 0) {
return keys[0];
}
return null;
};
</script>

View File

@ -9,14 +9,11 @@
>
刷新
</el-button>
<el-button
type="danger"
class="ele-btn-icon"
:icon="Refresh"
>
<el-button type="danger" class="ele-btn-icon" :icon="Refresh">
同步配置
</el-button>
</el-card>
<ele-card :body-style="{ paddingTop: '8px' }" v-loading="configLoading">
<template #header>
<ele-tabs
@ -32,7 +29,10 @@
label-position="top"
@submit.prevent=""
>
<config-form-list :config-list="configList" v-if="configList.length > 0" />
<config-form-list
:config-list="configList"
v-if="configList.length > 0"
/>
</el-form>
</ele-card>
</ele-page>
@ -40,12 +40,13 @@
<script lang="ts" setup>
import {onMounted, ref} from 'vue';
import {Refresh} from "@element-plus/icons-vue"
import {listConfig} from "@/api/system/config";
import {getSysConfig, strToBind, strToOption} from "@/utils/sys-config";
import {useFormData} from "@/utils/use-form-data";
import type {Config} from "@/api/system/config/model";
import ConfigFormList from "./components/config-form-list.vue";
import {Refresh} from '@element-plus/icons-vue';
import {listConfigs} from '@/api/system/config';
import {getSysConfig, strToBind, strToOption} from '@/utils/sys-config';
import {useFormData} from '@/utils/use-form-data';
import type {Config} from '@/api/system/config/model';
import ConfigFormList from './components/config-form-list.vue';
import {toTree} from 'ele-admin-plus';
defineOptions({name: 'SystemConfigSet'});
@ -56,129 +57,37 @@ const configLoading = ref(false);
/** 搜索 */
const reload = () => {
configLoading.value = true;
listConfig({group: configGroup.value, limit: 999}).then((data) => {
const configs = data.filter(d => d.type !== 'hidden');
listConfigs({group: configGroup.value, limit: 999}).then((data) => {
const configs = data.filter((d) => d.type !== 'hidden');
configList.value = configs.map(d=>{
const lists = configs.map((d) => {
// vBinditem
d.bind = strToBind(d.type, d.itemBind);
d.option = strToOption(d.type, d.option);
if(d.type == "tabs") {
d.children = [
{
"id": 6,
"pid": 0,
"name": "page1",
"title": "YEM1",
"group": "base",
"type": "tab-item",
"value": "123456",
"options": "",
"tips": "",
"sort": 100,
"status": 1,
"vBind": null,
"createTime": "2025-08-22 16:50:23",
"updateTime": "2025-08-22 16:50:23",
"deleteTime": null,
},
{
"id": 6,
"pid": 0,
"name": "page2",
"title": "YEM2",
"group": "base",
"type": "tab-item",
"value": "123456",
"options": "",
"tips": "",
"sort": 100,
"status": 1,
"vBind": null,
"createTime": "2025-08-22 16:50:23",
"updateTime": "2025-08-22 16:50:23",
"deleteTime": null
},
];
}
if(d.type =='card' || d.type == 'collapse'){
d.children = [
{
"id": 6,
"pid": 0,
"name": "test_password",
"title": "系统密码",
"group": "base",
"type": "password",
"value": "123456",
"options": "",
"tips": "",
"sort": 100,
"status": 1,
"vBind": null,
"createTime": "2025-08-22 16:50:23",
"updateTime": "2025-08-22 16:50:23",
"deleteTime": null
},
{
"id": 4,
"pid": 0,
"name": "test_textarea",
"title": "测试配置",
"group": "base",
"type": "textarea",
"value": "测试配置",
"options": "",
"tips": "",
"sort": 100,
"status": 1,
"vBind": null,
"createTime": "2025-08-22 15:40:19",
"updateTime": "2025-08-22 16:40:31",
"deleteTime": null
},
{
"id": 3,
"pid": 0,
"name": "test_text",
"title": "站点名称",
"group": "base",
"type": "text",
"value": "Ele Admin Plus",
"options": "",
"tips": "站点名称显示在<header>的title上",
"sort": 100,
"status": 1,
"vBind": null,
"createTime": "2025-08-22 14:11:22",
"updateTime": "2025-08-22 16:50:32",
"deleteTime": null
}
];
for(let a in d.children){
d.children[a].bind = strToBind(d.children[a].type, d.children[a].itemBind);
d.children[a].option = strToOption(d.children[a].type, d.children[a].option);
}
}
return d;
});
configList.value = toTree({
data: lists,
idField: 'id',
parentIdField: 'pid'
});
configLoading.value = false;
});
};
const groupItems = ref([]);
const configGroup = ref("");
const configGroup = ref('');
onMounted(async () => {
//
const group = [];
const {valueData} = await getSysConfig("config_group");
const {valueData} = await getSysConfig('config_group');
for (const name in valueData) {
group.push({name: name, label: valueData[name]})
group.push({name: name, label: valueData[name]});
}
groupItems.value = group
groupItems.value = group;
if (group.length > 0) {
configGroup.value = group[0].name
configGroup.value = group[0].name;
}
reload();
})
});
</script>

View File

@ -3,7 +3,7 @@
<ele-modal
form
destroy-on-close
:width="460"
:width="960"
v-model="visible"
:title="isUpdate ? '修改配置' : '添加配置'"
>
@ -14,93 +14,108 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="类型" prop="type">
<el-select
clearable
class="ele-fluid"
placeholder="请选择类型"
v-model="form.type"
>
<el-option value="-" label="分割线" />
<el-option value="alert" label="提示" />
<el-option value="card" label="卡片" />
<el-option value="collapse" label="折叠面板" />
<el-option value="tabs" label="Tabs" />
<el-option value="tabs_item" label="Tabs子页" />
<el-option value="text" label="单行文本" />
<el-option value="textarea" label="多行文本" />
<el-option value="password" label="密码" />
<el-option value="checkbox" label="复选框" />
<el-option value="radio" label="单选按钮" />
<el-option value="date" label="日期" />
<el-option value="datetime" label="日期+时间" />
<el-option value="time" label="时间" />
<el-option value="switch" label="开关" />
<el-option value="hidden" label="隐藏" />
<el-option value="range" label="范围" />
<el-option value="date_range" label="日期范围" />
<el-option value="datetime_range" label="日期时间范围" />
<el-option value="time_range" label="时间范围" />
<el-option value="number" label="数字" />
</el-select>
</el-form-item>
<el-form-item label="配置PID" prop="pid">
<el-input-number
v-model="form.pid"
/>
</el-form-item>
<el-form-item label="配置标题" prop="title">
<el-input
clearable
:maxlength="20"
v-model="form.title"
placeholder="请输入配置标题(一般由中文组成,仅用于显示)"
/>
</el-form-item>
<el-form-item label="配置名称" prop="name">
<el-input
clearable
:maxlength="20"
v-model="form.name"
placeholder="请输入配置名称"
/>
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="上级" prop="pid">
<config-select :model-value="form.pid" :group="form.group" disabled />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select
clearable
class="ele-fluid"
placeholder="请选择类型"
v-model="form.type"
:disabled="form._type_readonly"
>
<el-option
v-for="label in configItemType"
:value="label"
:label="getItemLabel(label)"
:key="label"
/>
</el-select>
</el-form-item>
<el-form-item label="配置标题" prop="title">
<el-input
clearable
:maxlength="20"
v-model="form.title"
placeholder="请输入配置标题(一般由中文组成,仅用于显示)"
/>
</el-form-item>
<template v-if="form.type">
<el-form-item
label="配置名称"
prop="name"
v-if="HIDE_FORM_ITEM_WHERE.name()"
>
<el-input
clearable
:maxlength="20"
v-model="form.name"
placeholder="请输入配置名称"
/>
</el-form-item>
<el-form-item
label="配置值"
v-if="HIDE_FORM_ITEM_WHERE.value()"
>
<el-input
:rows="4"
type="textarea"
v-model="form.value"
placeholder="请输入配置值"
/>
</el-form-item>
<el-form-item
label="配置项"
v-if="HIDE_FORM_ITEM_WHERE.option()"
>
<el-input
:rows="4"
type="textarea"
v-model="form.option"
placeholder="请输入配置项"
/>
</el-form-item>
<el-form-item label="配置说明">
<el-input
:rows="4"
type="textarea"
v-model="form.tips"
placeholder="请输入配置说明"
/>
</el-form-item>
</template>
</el-col>
<el-col :span="12">
<el-form-item label="BIND">
<el-input
:rows="4"
type="textarea"
v-model="form.itemBind"
placeholder=""
/>
</el-form-item>
<el-form-item label="Class">
<el-input
type="text"
v-model="form.itemClass"
placeholder="请输入渲染框CSS样式类"
/>
</el-form-item>
<el-form-item label="Style">
<el-input
:rows="4"
type="textarea"
v-model="form.itemStyle"
placeholder="请输入渲染框Style样式"
/>
</el-form-item>
</el-col>
</el-row>
<!-- //线 web_site_titleconfig('web_site_title')-->
<el-form-item label="配置值">
<el-input
:rows="4"
type="textarea"
v-model="form.value"
placeholder="请输入配置值"
/>
</el-form-item>
<el-form-item label="配置项">
<el-input
:rows="4"
type="textarea"
v-model="form.option"
placeholder="请输入配置项"
/>
</el-form-item>
<el-form-item label="Bind属性">
<el-input
:rows="4"
type="textarea"
v-model="form.bind"
placeholder="请输入Bind属性"
/>
</el-form-item>
<el-form-item label="配置说明">
<el-input
:rows="4"
type="textarea"
v-model="form.tips"
placeholder="请输入配置说明"
/>
</el-form-item>
<el-form-item label="备注">
<el-form-item label="备注" v-if="false">
<el-input
:rows="4"
type="textarea"
@ -119,16 +134,51 @@
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { ref, reactive, watch, computed } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { EleMessage } from 'ele-admin-plus';
import { useFormData } from '@/utils/use-form-data';
import type { Config } from '@/api/system/config/model';
import {addConfig, updateConfig} from "@/api/system/config";
import { addConfig, updateConfig } from '@/api/system/config';
import ConfigSelect from './config-select.vue';
import { ItemType, getItemLabel } from '@/enum/config-item-type.ts';
const HIDE_FORM_ITEM_WHERE: any = {
name: () => {
return ['tabs', 'tabs_item','alert','card','separator'].indexOf(<string>form.type) == -1;
},
value: ()=>{
return ['tabs', 'tabs_item'].indexOf(<string>form.type) == -1
},
option: ()=>{
return ['tabs', 'tabs_item'].indexOf(<string>form.type) == -1
}
};
const configItemType: any = computed(() => {
let list = {};
if (props.parent) {
if (props.parent.type == 'tabs') {
list[ItemType.TABS_ITEM] = ItemType.TABS_ITEM;
} else if (props.parent.type == 'tabs_item') {
for (const name in ItemType) {
if (
[ItemType.TABS_ITEM, ItemType.TABS].indexOf(ItemType[name]) == -1
) {
list[name] = ItemType[name];
}
}
}
}
if (Object.keys(list).length == 0) {
return ItemType;
} else {
return list;
}
});
const props = defineProps<{
/** 修改回显的数据 */
data?: Config | null;
/** 父级元素 */
parent?: Config | null;
/** 配置所属组 */
group?: string | null;
}>();
@ -149,18 +199,26 @@
/** 表单实例 */
const formRef = ref<FormInstance | null>(null);
interface ConfigEdit extends Config {
_type_readonly: boolean;
}
/** 表单数据 */
const [form, resetFields, assignFields] = useFormData<Config>({
const [form, resetFields, assignFields] = useFormData<ConfigEdit>({
id: void 0,
pid: 0,
_type_readonly: false,
type: '',
comments: '',
group: '',
title: '',
name: '',
type: '',
tips: '',
option: '',
value: '',
itemBind: '',
itemStyle: '',
itemClass: ''
});
/** 表单验证规则 */
@ -219,6 +277,17 @@
} else {
resetFields();
isUpdate.value = false;
if (props.parent) {
let data: any = { pid: props.parent.id };
if (props.parent.type == 'tabs') {
data.type = 'tabs_item';
data._type_readonly = true;
}
assignFields(data);
}
}
if (form.type == 'tabs_item') {
form._type_readonly = true;
}
form.group = props.group;
}

View File

@ -0,0 +1,72 @@
<!-- 上级配置选择下拉框 -->
<template>
<el-tree-select
clearable
filterable
:data="configData"
check-strictly
default-expand-all
node-key="id"
:props="{ label: 'title', disabled: 'disabled' }"
:placeholder="placeholder"
:model-value="modelValue || void 0"
class="ele-fluid"
:popper-options="{ strategy: 'fixed' }"
@update:modelValue="updateValue"
>
<template #default="{ data }">
<span>{{ data.title }}</span>
</template>
</el-tree-select>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { EleMessage, toTree } from 'ele-admin-plus';
import { listConfigs } from '@/api/system/config';
import type { Config } from '@/api/system/config/model';
const props = withDefaults(
defineProps<{
/** 选中的配置 */
modelValue?: number | string;
/** 提示信息 */
placeholder?: string;
/** 配置所属组 */
group?: string | null;
}>(),
{
placeholder: '请选择上级配置'
}
);
const emit = defineEmits<{
(e: 'update:modelValue', value: number | string): void;
}>();
/** 配置数据 */
const configData = ref<Config[]>([]);
/** 更新选中数据 */
const updateValue = (value: number | string) => {
emit('update:modelValue', value || 0);
};
/** 获取配置数据 */
listConfigs({ group: props.group, limit: 999 })
.then((list) => {
const data: any = list.map((d: Config) => {
d.disabled = ['tabs', 'tabs_item'].indexOf(d.type) === -1;
return d;
});
configData.value = toTree({
data: data.filter(d => !d.disabled),
idField: 'id',
parentIdField: 'pid'
});
})
.catch((e) => {
EleMessage.error({ message: e.message, plain: true });
});
</script>

View File

@ -1,10 +1,10 @@
<template>
<ele-page>
<!-- 搜索表单 -->
<config-search @search="reload"/>
<config-search @search="reload" />
<ele-tabs
type="card"
type="tag"
v-model="configGroup"
:items="groupItems"
@tabChange="reload()"
@ -23,6 +23,7 @@
:pagination="false"
cache-key="systemRoleTable"
:load-on-created="false"
height="66vh"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<template #toolbar>
@ -35,22 +36,25 @@
添加
</el-button>
</template>
<template #action="d">
{{ d }}
<!-- <template v-if="['tabs'].indexOf(row.type) != -1">-->
<!-- {{ row }}-->
<!-- <el-link type="primary" underline="never" @click="openEdit(row)">添加子项</el-link>-->
<!-- <el-divider direction="vertical"/>-->
<!-- </template>-->
<!-- <el-link type="primary" underline="never" @click="openEdit(row)">-->
<!-- 修改-->
<!-- </el-link>-->
<!-- <template v-if="row.group != 'system'">-->
<!-- <el-divider direction="vertical"/>-->
<!-- <el-link type="danger" underline="never" @click="remove(row)">-->
<!-- 删除-->
<!-- </el-link>-->
<!-- </template>-->
<template #action="{ row }">
<template v-if="['tabs', 'tabs_item', 'collapse', 'card'].indexOf(row.type) != -1">
<el-link
type="primary"
underline="never"
@click="openEdit(null, row)"
>添加子项</el-link
>
<el-divider direction="vertical" />
</template>
<el-link type="primary" underline="never" @click="openEdit(row)">
修改
</el-link>
<template v-if="row.group != 'system'">
<el-divider direction="vertical" />
<el-link type="danger" underline="never" @click="remove(row)">
删除
</el-link>
</template>
</template>
<template #status="{ row }">
<el-switch
@ -59,173 +63,204 @@
@change="(checked: boolean) => editStatus(checked, row)"
/>
</template>
<template #type="{ row }">
<span :title="row.type">{{ getItemLabel(row.type) }}</span>
</template>
<template #sort="{ row }">
<el-input-number :controls="false" v-model="row.sort" :min="1" :max="9999" style="width: 100%;"></el-input-number>
</template>
</ele-pro-table>
</ele-card>
<!-- 编辑弹窗 -->
<config-edit v-model="showEdit" :data="current" @done="reload" :group="configGroup"/>
<config-edit
v-model="showEdit"
:data="current"
:parent="parent"
@done="reload"
:group="configGroup"
/>
</ele-page>
</template>
<script lang="ts" setup>
import {onMounted, ref} from 'vue';
import {ElMessageBox} from 'element-plus';
import {EleMessage} from 'ele-admin-plus';
import type {EleProTable} from 'ele-admin-plus';
import type {
DatasourceFunction,
Columns
} from 'ele-admin-plus/es/ele-pro-table/types';
import {PlusOutlined,} from '@/components/icons';
import ConfigSearch from './components/config-search.vue';
import ConfigEdit from './components/config-edit.vue';
import type {Config, ConfigParam} from '@/api/system/config/model';
import {listConfig, updateConfigStatus, removeConfigs} from "@/api/system/config";
import {getSysConfig} from "@/utils/sys-config";
import { onMounted, ref } from 'vue';
import { ElMessageBox } from 'element-plus';
import {EleMessage, toTree} from 'ele-admin-plus';
import type { EleProTable } from 'ele-admin-plus';
import type {
DatasourceFunction,
Columns
} from 'ele-admin-plus/es/ele-pro-table/types';
import { PlusOutlined } from '@/components/icons';
import ConfigSearch from './components/config-search.vue';
import ConfigEdit from './components/config-edit.vue';
import type { Config, ConfigParam } from '@/api/system/config/model';
import {
listConfigs,
updateConfigStatus,
removeConfigs
} from '@/api/system/config';
import { getSysConfig } from '@/utils/sys-config';
import { ItemType, getItemLabel } from '@/enum/config-item-type.ts';
defineOptions({name: 'SystemConfig'});
defineOptions({ name: 'SystemConfig' });
/** 表格实例 */
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
/** 表格实例 */
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
/** 表格列配置 */
const columns = ref<Columns>([
{
type: 'index',
columnKey: 'index',
width: 50,
align: 'center'
},
{
prop: 'name',
label: '名称',
minWidth: 120
},
{
prop: 'title',
label: '标题',
minWidth: 120
},
{
prop: 'type',
label: '类型',
minWidth: 120
},
{
prop: 'status',
label: '状态',
width: 90,
align: 'center',
slot: 'status',
formatter: (row) => (row.status == 0 ? '正常' : '冻结')
},
{
prop: 'sort',
label: '排序',
minWidth: 140
},
{
prop: 'comments',
label: '备注',
minWidth: 140
},
{
columnKey: 'action',
label: '操作',
width: 200,
align: 'right' /* ,
/** 表格列配置 */
const columns = ref<Columns>([
{
type: 'index',
columnKey: 'index',
width: 50,
align: 'center'
},
{
prop: 'title',
label: '标题',
minWidth: 120
},
{
prop: 'name',
label: '名称',
minWidth: 120
},
{
prop: 'type',
slot: 'type',
label: '类型',
width: 120
},
{
prop: 'status',
label: '状态',
width: 90,
align: 'center',
slot: 'status',
formatter: (row) => (row.status == 0 ? '正常' : '冻结')
},
{
prop: 'sort',
slot: 'sort',
label: '排序',
align: 'center',
width: 140
},
{
prop: 'comments',
label: '备注',
minWidth: 140
},
{
columnKey: 'action',
label: '操作',
width: 200,
align: 'right' /* ,
fixed: 'right' */,
slot: 'action',
hideInPrint: true,
hideInExport: true
}
]);
slot: 'action',
hideInPrint: true,
hideInExport: true
}
]);
/** 表格选中数据 */
const selections = ref<Config[]>([]);
/** 表格选中数据 */
const selections = ref<Config[]>([]);
/** 当前编辑数据 */
const current = ref<Config | null>(null);
/** 当前编辑数据 */
const current = ref<Config | null>(null);
/** 是否显示编辑弹窗 */
const showEdit = ref(false);
const parent = ref<Config | null>(null);
/** 是否显示编辑弹窗 */
const showEdit = ref(false);
/** 表格数据源 */
const datasource: DatasourceFunction = ({pages, where, orders}) => {
return listConfig({...where, ...orders, ...pages, group: configGroup.value, limit: 999});
};
/** 表格数据源 */
const datasource: DatasourceFunction = async ({ pages, where, orders }) => {
const data = await listConfigs({
...where,
...orders,
...pages,
group: configGroup.value,
limit: 999
});
return toTree({
data,
idField: 'id',
parentIdField: 'pid'
});
};
/** 搜索 */
const reload = (where?: ConfigParam) => {
selections.value = [];
tableRef.value?.reload?.({page: 1, where});
};
/** 搜索 */
const reload = (where?: ConfigParam) => {
selections.value = [];
tableRef.value?.reload?.({ page: 1, where });
};
/** 打开编辑弹窗 */
const openEdit = (row?: Config) => {
current.value = row ?? null;
showEdit.value = true;
};
/** 打开编辑弹窗 */
const openEdit = (row?: Config, parentRow?: Config) => {
current.value = row ?? null;
showEdit.value = true;
parent.value = parentRow;
};
/** 删除单个 */
const remove = (row?: Config) => {
const rows = row == null ? selections.value : [row];
if (!rows.length) {
EleMessage.error({message: '请至少选择一条数据', plain: true});
return;
}
ElMessageBox.confirm(
'确定要删除“' + rows.map((d) => d.roleName).join(', ') + '”吗?',
'系统提示',
{type: 'warning', draggable: true}
)
.then(() => {
const loading = EleMessage.loading({
message: '请求中..',
plain: true
});
removeConfigs(rows.map((d) => d.id))
.then((msg) => {
loading.close();
EleMessage.success({message: msg, plain: true});
reload();
})
.catch((e) => {
loading.close();
EleMessage.error({message: e.message, plain: true});
/** 删除单个 */
const remove = (row?: Config) => {
const rows = row == null ? selections.value : [row];
if (!rows.length) {
EleMessage.error({ message: '请至少选择一条数据', plain: true });
return;
}
ElMessageBox.confirm(
'确定要删除“' + rows.map((d) => d.roleName).join(', ') + '”吗?',
'系统提示',
{ type: 'warning', draggable: true }
)
.then(() => {
const loading = EleMessage.loading({
message: '请求中..',
plain: true
});
})
.catch(() => {
});
};
removeConfigs(rows.map((d) => d.id))
.then((msg) => {
loading.close();
EleMessage.success({ message: msg, plain: true });
reload();
})
.catch((e) => {
loading.close();
EleMessage.error({ message: e.message, plain: true });
});
})
.catch(() => {});
};
/** 修改配置状态 */
const editStatus = (checked: boolean, row: Config) => {
const status = checked ? 1 : 0;
updateConfigStatus(row.id, status)
.then((msg) => {
row.status = status;
EleMessage.success({message: msg, plain: true});
})
.catch((e) => {
EleMessage.error({message: e.message, plain: true});
});
};
/** 修改配置状态 */
const editStatus = (checked: boolean, row: Config) => {
const status = checked ? 1 : 0;
updateConfigStatus(row.id, status)
.then((msg) => {
row.status = status;
EleMessage.success({ message: msg, plain: true });
})
.catch((e) => {
EleMessage.error({ message: e.message, plain: true });
});
};
const groupItems = ref([]);
const configGroup = ref("");
onMounted(async () => {
//
const group = [];
const {valueData} = await getSysConfig("config_group");
for (const name in valueData) {
group.push({name: name, label: valueData[name]})
}
groupItems.value = group
if (group.length > 0) {
configGroup.value = group[0].name
}
reload();
})
const groupItems = ref([]);
const configGroup = ref('');
onMounted(async () => {
//
const group = [];
const { valueData } = await getSysConfig('config_group');
for (const name in valueData) {
group.push({ name: name, label: valueData[name] });
}
groupItems.value = group;
if (group.length > 0) {
configGroup.value = group[0].name;
}
reload();
});
</script>