tapi/z_ele/src/views/system/config/components/config-edit.vue
2025-08-26 13:57:09 +08:00

366 lines
9.8 KiB
Vue

<!-- 配置编辑弹窗 -->
<template>
<ele-modal
form
destroy-on-close
:width="980"
v-model="visible"
:title="isUpdate ? '修改配置' : '添加配置'"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="80px"
@submit.prevent=""
>
<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>
</template>
</el-col>
<el-col :span="11" :offset="1">
<el-form-item label="BIND" prop="itemBind">
<el-input
:rows="4"
type="textarea"
v-model="form.itemBind"
placeholder=""
/>
</el-form-item>
<el-form-item label="Class" prop="itemClass">
<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-card header="样式预览:" style="margin-bottom: 30px;" v-if="form.type && HIDE_FORM_ITEM_WHERE.preview()">
<config-form-item
:name="preview.name"
:title="preview.title"
:type="preview.type"
:value="preview.value"
:option="preview.option"
:item-bind="preview.bind"
:key="preview.id"
/>
<el-form-item v-if="preview.tips">
<ele-text type="placeholder" style="line-height: 1.6">
{{ preview.tips }}
</ele-text>
</el-form-item>
</el-card>
</el-col>
</el-row>
<el-form-item label="配置说明" v-if="HIDE_FORM_ITEM_WHERE.tips()">
<el-input
type="textarea"
v-model="form.tips"
placeholder="请输入配置说明(在配置页简短提示作用)"
/>
</el-form-item>
<el-divider/>
<el-form-item label="备注">
<el-input
type="textarea"
v-model="form.comments"
placeholder="请输入备注(详细描述此配置的功能和作用) - 此备注仅开发人员可见"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" :loading="loading" @click="save">
保存
</el-button>
</template>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, 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, checkExistence} from '@/api/system/config';
import ConfigSelect from './config-select.vue';
import {ItemType, getItemLabel} from '@/enum/config-item-type.ts';
import ConfigFormItem from "@/views/system/config-set/components/config-form-item.vue";
import {strToBind, strToOption} from "@/utils/sys-config";
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
},
tips: () => {
return ['separator', 'alert', 'card', 'collapse','tabs'].indexOf(<string>form.type) == -1
},
preview: ()=>{
return ['tabs', 'tabs_item','card', 'collapse', 'alert', 'separator','json'].indexOf(<string>form.type) == -1
}
};
const preview = computed(() => {
return {
...form,
bind: strToBind(<string>form.type, <string>form.itemBind),
option: strToOption(<string>form.type, <string>form.option)
}
})
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;
}>();
const emit = defineEmits<{
(e: 'done'): void;
}>();
/** 弹窗是否打开 */
const visible = defineModel({type: Boolean});
/** 是否是修改 */
const isUpdate = ref(false);
/** 提交状态 */
const loading = ref(false);
/** 表单实例 */
const formRef = ref<FormInstance | null>(null);
interface ConfigEdit extends Config {
_type_readonly: boolean;
}
/** 表单数据 */
const [form, resetFields, assignFields] = useFormData<ConfigEdit>({
id: void 0,
pid: 0,
_type_readonly: false,
type: '',
comments: '',
group: '',
title: '',
name: '',
tips: '',
option: '',
value: '',
itemBind: '',
itemStyle: '',
itemClass: ''
});
/** 表单验证规则 */
const rules = computed<FormRules>(() => {
const obj: FormRules = {
title: [
{
required: true,
type: 'string',
message: '请输入标题',
trigger: 'blur'
}
],
name: [
{
required: true,
message: '请输入配置名称',
type: 'string',
trigger: 'blur'
},
{
type: 'string',
trigger: 'blur',
validator: (_rule: any, value: string, callback: any) => {
checkExistence('name', value, isUpdate.value ? props.data.id : null)
.then(() => {
callback(new Error('配置名称已经存在'));
})
.catch(() => {
callback();
});
}
}
],
itemBind: [
{
type: 'string',
validator: (_rule: any, value: string, callback: any) => {
if (value) {
const msg = '请输入正确的JSON格式';
try {
const obj = JSON.parse(value);
if (obj == null || typeof obj !== 'object') {
callback(msg);
return;
}
} catch (_e) {
callback(msg);
return;
}
}
callback();
},
trigger: 'blur'
}
],
};
return obj;
});
/** 关闭弹窗 */
const handleCancel = () => {
visible.value = false;
};
/** 保存编辑 */
const save = () => {
formRef.value?.validate?.((valid) => {
if (!valid) {
return;
}
loading.value = true;
const saveOrUpdate = isUpdate.value ? updateConfig : addConfig;
saveOrUpdate(form)
.then((msg) => {
loading.value = false;
EleMessage.success({message: msg, plain: true});
handleCancel();
emit('done');
})
.catch((e) => {
loading.value = false;
EleMessage.error({message: e.message, plain: true});
});
});
};
/** 监听弹窗打开 */
watch(visible, () => {
if (visible.value) {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} 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 (['tabs_item','tabs'].indexOf(form.type) != -1) {
form._type_readonly = true;
}
form.group = props.group;
}
});
</script>