系统配置页面部分接口完成

This commit is contained in:
2026-08-28 10:55:01 +08:00
parent 97a0465890
commit 6651cb6749
5 changed files with 367 additions and 180 deletions
+320 -123
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ref, computed, onMounted } from 'vue';
import { useAppStore } from '@/store/modules/app';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { getColorWithOpacity } from '@/utils/common';
import { getColorWithOpacity, tagTextColor, tagBgColor, tagBorderColor } from '@/utils/common';
import { systemConfigApi, addSystemConfigApi, editSystemConfigApi, deleteSystemConfigApi } from '@/service/api/system';
const appStore = useAppStore();
const themeStore = useThemeStore();
@@ -31,30 +32,34 @@ const AI_LOGS = [
{ scene: "AI-09 关闭辅助判定", user: "系统管理员", change: "MOC-2026-R01-0035", time: "08-23 15:02", cost: "1.5s", adopted: "采纳" },
];
// 变更类型
// *********************变更类型*********************//
const typeRef = ref<any>(null);
const typeModal = ref<boolean>(false);
const typeLoading = ref<boolean>(false);
const currentType = ref<string>('add');
const typeInfo = ref<{id:number, name:string, desc?:string}>({
const typeInfo = ref<{id:number, label:string, remark?:string, color:string,value:string,group_key:string}>({
id:0,
name:'',
desc:'',
label:'',
remark:'',
color:'',
value: '',
group_key:'change_type'
})
const typeRules = {
name:{required:true, message:'请输入类型名称', trigger:'blur'},
label:{required:true, message:'请输入类型名称', trigger:'blur'},
color:{required:true, message:'请选择类型颜色', trigger:['blur','change']},
}
const typeList = ref<{id:number, name:string, desc?:string}[]>([
{id:1,name:'工艺', desc:'工艺参数、配方、操作条件、操作规程等工艺技术类变更'},
{id:2,name:'设备', desc:'设备本体、附件、材质、规格、备件等硬件类变更'},
{id:3,name:'管理', desc:'制度、职责、程序、组织与人员等管理类变更'},
])
const typeList = ref<{id:number, label:string, remark?:string, color:string,value:string}[]>([])
// 新增类型
const addType = () => {
typeInfo.value = {
id:0,
name:'',
desc:'',
label:'',
remark:'',
color:'',
value: '',
group_key:'change_type'
}
currentType.value = 'add';
typeModal.value = true;
@@ -63,120 +68,183 @@ const addType = () => {
const editType = (row:any) => {
typeInfo.value = {
id:row.id,
name:row.name,
desc:row.desc,
label:row.label,
remark:row.remark,
color:row.color,
value: row.value,
group_key:'change_type'
}
currentType.value = 'edit';
typeModal.value = true;
}
// 删除类型
const deleteType = (item:any) => {
typeList.value = typeList.value.filter((i:any) => i.id !== item.id);
const deleteType = async (id:number) => {
const {error} = await deleteSystemConfigApi(id);
if(!error){
typeList.value = typeList.value.filter((i:any) => i.id !== id);
window.$message?.success('删除成功')
}
}
// 保存类型
const typeSave = async (e:MouseEvent) => {
e.preventDefault()
typeRef.value?.validate((errors:any) => {
typeRef.value?.validate(async (errors:any) => {
if (!errors) {
typeLoading.value = true;
try {
// await addTypeApi(typeInfo.value);
window.$message?.success('操作成功')
typeLoading.value = false;
typeModal.value = false;
} finally {
typeLoading.value = false;
// 新增类型
if(currentType.value === 'add'){
// 去掉id字段
const { id, ...rest } = typeInfo.value;
rest.value = (typeList.value.length + 1).toString();
const {error} = await addSystemConfigApi(rest);
if(!error){
window.$message?.success('添加成功')
typeModal.value = false;
}
}
}
})
}
// 变更等级
const levelRef = ref<any>(null);
const levelModal = ref<boolean>(false);
const levelLoading = ref<boolean>(false);
const currentLevel = ref<string>('add');
const toolsOptions = ref<{label:string, value:number}[]>([
{label:'AI预分析',value:1},
{label:'检查表法',value:2},
{label:'HAZOP',value:3},
{label:'JSA',value:4},
{label:'SCL',value:5},
])
const levelInfo = ref<{id:number, name:string, way:string,tools:number[]}>({
id:0,
name:'',
way:'',
tools:[],
})
const levelRules = {
name:{required:true, message:'请输入等级名称', trigger:'blur'},
way:{required:true, message:'请输入等级判定方式', trigger:'blur'},
tools:{type:'array', required:true, message:'请选择分析工具', trigger:['blur','change']},
}
const levelList = ref<{id:number, name:string, way:string,tools:{id:number, name:string}[]}[]>([
{id:1,name:'一般', way:'等级评分< 20 分',tools:[{id:1,name:'AI预分析'},{id:2,name:'检查表法'}]},
{id:2,name:'重要', way:'等级评分 > 20 分',tools:[{id:1,name:'AI预分析'},{id:2,name:'HAZOP'},{id:3,name:'JSA'},{id:4,name:'SCL'}]},
])
// 新增等级
const addLevel = () => {
levelInfo.value = {
id:0,
name:'',
way:'',
tools:[],
}
currentLevel.value = 'add';
levelModal.value = true;
}
// 编辑类型
const editLevel = (row:any) => {
levelInfo.value = {
id:row.id,
name:row.name,
way:row.way,
tools:row.tools.map((item:any) => (item.id)),
}
currentLevel.value = 'edit';
levelModal.value = true;
}
// 删除类型
const deleteLevel = (item:any) => {
levelList.value = levelList.value.filter((i:any) => i.id !== item.id);
}
// 保存类型
const levelSave = async (e:MouseEvent) => {
e.preventDefault()
levelRef.value?.validate((errors:any) => {
if (!errors) {
levelLoading.value = true;
try {
// await addLevelApi(levelInfo.value);
window.$message?.success('操作成功')
levelLoading.value = false;
levelModal.value = false;
} finally {
levelLoading.value = false;
// 编辑类型
if(currentType.value === 'edit'){
// 去掉id字段
const { id, ...rest } = typeInfo.value;
const {error} = await editSystemConfigApi(typeInfo.value.id, rest);
if(!error){
window.$message?.success('编辑成功')
typeModal.value = false;
}
}
typeLoading.value = false;
getConfig();
}
})
}
// 变更编号规则
const ruleInfo = ref<{prefix:string, year:null|string, code:number|null, digit:number|null, rule:number|null}>({
prefix:'MOC',
// *********************变更等级*********************//
const levelRef = ref<any>(null);
const levelModal = ref<boolean>(false);
const levelLoading = ref<boolean>(false);
const currentLevel = ref<string>('add');
// const toolsOptions = ref<{label:string, value:number}[]>([
// {label:'AI预分析',value:1},
// {label:'检查表法',value:2},
// {label:'HAZOP',value:3},
// {label:'JSA',value:4},
// {label:'SCL',value:5},
// ])
const levelInfo = ref<{id:number, label:string, remark:string,color:string,value:string,group_key:string}>({
id:0,
label:'',
remark:'',
color:'',
value: '',
group_key:'change_level'
})
const levelRules = {
label:{required:true, message:'请输入等级名称', trigger:'blur'},
color:{required:true, message:'请选择等级颜色', trigger:['blur','change']},
remark:{required:true, message:'请输入等级判定方式', trigger:'blur'},
}
const levelList = ref<{id:number, label:string, remark:string,color:string,value:string}[]>([])
// 新增等级
const addLevel = () => {
levelInfo.value = {
id:0,
label:'',
remark:'',
color:'',
value:'',
group_key:'change_level'
}
currentLevel.value = 'add';
levelModal.value = true;
}
// 编辑等级
const editLevel = (row:any) => {
levelInfo.value = {
id:row.id,
label:row.label,
remark:row.remark,
color:row.color,
value:row.value,
group_key:'change_level'
}
currentLevel.value = 'edit';
levelModal.value = true;
}
// 删除等级
const deleteLevel = async (id:number) => {
const {error} = await deleteSystemConfigApi(id);
if(!error){
levelList.value = levelList.value.filter((i:any) => i.id !== id);
window.$message?.success('删除成功')
}
}
// 保存等级
const levelSave = async (e:MouseEvent) => {
e.preventDefault()
levelRef.value?.validate(async (errors:any) => {
if (!errors) {
levelLoading.value = true;
if(currentLevel.value === 'add'){
// 去掉id字段
const { id, ...rest } = levelInfo.value;
rest.value = (levelList.value.length + 1).toString();
const {error} = await addSystemConfigApi(rest);
if(!error){
window.$message?.success('添加成功')
levelModal.value = false;
}
}
if(currentLevel.value === 'edit'){
// 去掉id字段
const { id, ...rest } = levelInfo.value;
const {error} = await editSystemConfigApi(levelInfo.value.id, rest);
if(!error){
window.$message?.success('编辑成功')
levelModal.value = false;
}
}
levelLoading.value = false;
getConfig();
}
})
}
// *********************变更编号规则*********************//
const previewNumber = computed(() => {
let year = '';
let digit = '';
const currentYear = new Date().getFullYear();
if (ruleInfo.value.year === 'yyyy') {
year = currentYear.toString();
} else if (ruleInfo.value.year === 'yy') {
year = currentYear.toString().substring(2);
}
if (ruleInfo.value.digit) {
digit = '0'.repeat(ruleInfo.value.digit - 1) + '1';
}
return ruleInfo.value.prefix+'-' + year + '-' + ruleInfo.value.code + '-' + digit;
});
const ruleInfo = ref<{prefix:string, year:null|string, code:string|null, digit:number|null, rule:string|null}>({
prefix:'',
year:null,
code: null,
digit: null,
rule: null,
})
const yearOptions = ref<{label:string, value:string}[]>([
{label:'四位年份(yyyy',value:'yyyy'}
{label:'四位年份(yyyy',value:'yyyy'},
{label:'两位年份(yy',value:'yy'},
])
const codeOptions = ref<{label:string, value:number}[]>([
{label:'申请部门装置代码',value:1}
const codeOptions = ref<{label:string, value:string}[]>([
{label:'申请部门装置代码',value:'R01'},
{label:'公司统一编号(不带装置代码)',value:'R02'},
])
const ruleOptions = ref<{label:string, value:number}[]>([
{label:'按年度+装置代码重置',value:1}
const ruleOptions = ref<{label:string, value:string}[]>([
{label:'按年度+装置代码重置',value:'YEARLY+CODE'},
{label:'按年度重置',value:'YEARLY'},
{label:'连续不重置',value:'CONTINUE'},
])
// ---------- 风险矩阵设置(按分析方法) ----------
@@ -265,6 +333,57 @@ const handleTabChange = (v: number) => {
activeMatrix.value = v;
}
// 获取配置
const getConfig = async () => {
const {data,error} = await systemConfigApi();
if(!error){
// 解析 AI 辅助功能配置
const aiAssistGroup = data.find((group:{group_key:string}) => group.group_key === 'ai_assist');
if (aiAssistGroup) {
const items = aiAssistGroup.items;
items.forEach((item:any) => {
switch (item.item_key) {
case 'enabled': aiEnabled.value = item.value === '1'; break;
case 'api_url': aiConfig.value.address = item.value; break;
case 'model': aiConfig.value.mode = item.value; break;
case 'api_key': aiConfig.value.key = item.value; break;
case 'timeout_seconds': aiConfig.value.timeout = Number(item.value); break;
default: break;
}
});
}
// 解析 变更类型设置
const changeTypeGroup = data.find((group:{group_key:string}) => group.group_key === 'change_type');
if (changeTypeGroup) {
typeList.value = changeTypeGroup.items;
}
// 解析 变更等级设置
const changeLevelGroup = data.find((group:{group_key:string}) => group.group_key === 'change_level');
if (changeLevelGroup) {
levelList.value = changeLevelGroup.items;
}
// 解析 变更编号规则
const changeCodeGroup = data.find((group:{group_key:string}) => group.group_key === 'change_no_rule');
if (changeCodeGroup) {
const items = changeCodeGroup.items;
items.forEach((item:any) => {
switch (item.item_key) {
case 'prefix': ruleInfo.value.prefix = item.value; break;
case 'year_format': ruleInfo.value.year = item.value; break;
case 'unit_code': ruleInfo.value.code = item.value; break;
case 'seq_length': ruleInfo.value.digit = Number(item.value); break;
case 'seq_reset': ruleInfo.value.rule = item.value; break;
default: break;
}
});
}
}
}
onMounted(() => {
getConfig();
})
</script>
<template>
@@ -360,8 +479,15 @@ const handleTabChange = (v: number) => {
<template v-for="(item,index) in typeList" :key="item.id">
<div class="p-3 flex items-center gap-2" :class="{'border-t':index !== 0}">
<div class="flex-1 flex gap-2">
<NTag size="small" type="success">{{item.name}}</NTag>
<p class="flex-1">{{item.desc}}</p>
<NTag
size="small"
:color="{
color: tagBgColor(item.color),
textColor: tagTextColor(item.color),
borderColor: tagBorderColor(item.color),
}"
>{{item.label}}</NTag>
<p class="flex-1">{{item.remark}}</p>
</div>
<div class="flex items-center gap-2">
<NButton text type="primary" class="text-xs" @click="editType(item)">
@@ -371,7 +497,7 @@ const handleTabChange = (v: number) => {
positive-text="确定"
:positiveButtonProps="{ size: 'tiny' }"
:negativeButtonProps="{ size: 'tiny' }"
@positive-click="deleteType(item)"
@positive-click="deleteType(item.id)"
>
<template #trigger>
<NButton text type="error" class="text-xs">
@@ -418,9 +544,18 @@ const handleTabChange = (v: number) => {
<div v-for="item in levelList" :key="item.id" class="border rounded-md p-3">
<div class="flex items-center">
<div class="flex-1 flex items-center gap-2">
<NTag v-if="item.name==='一般'" size="small">{{item.name}}</NTag>
<NTag v-else-if="item.name==='重要'" size="small" type="error">{{item.name}}</NTag>
<p class="flex-1">判定方式<b>{{item.way}}</b></p>
<NTag
size="small"
:color="{
color: tagBgColor(item.color),
textColor: tagTextColor(item.color),
borderColor: tagBorderColor(item.color),
}"
>
{{item.label}}
</NTag>
<p class="flex-1">判定方式<b>{{item.remark}}</b></p>
</div>
<div class="flex items-center gap-2">
<NButton text type="primary" class="text-xs" @click="editLevel(item)">
@@ -430,7 +565,7 @@ const handleTabChange = (v: number) => {
positive-text="确定"
:positiveButtonProps="{ size: 'tiny' }"
:negativeButtonProps="{ size: 'tiny' }"
@positive-click="deleteLevel(item)"
@positive-click="deleteLevel(item.id)"
>
<template #trigger>
<NButton text type="error" class="text-xs">
@@ -441,12 +576,12 @@ const handleTabChange = (v: number) => {
</NPopconfirm>
</div>
</div>
<div class="flex flex-wrap mt-2">
<!-- <div class="flex flex-wrap mt-2">
<p>对应分析工具</p>
<div class="flex items-center flex-wrap gap-2">
<NTag v-for="tool in item.tools" :key="tool.id" size="small" type="info">{{tool.name}}</NTag>
</div>
</div>
</div> -->
</div>
</div>
<div v-else>
@@ -507,7 +642,7 @@ const handleTabChange = (v: number) => {
</div>
<div class="flex items-center p-3 bg-[#f8fafc] rounded-md">
<span>编号示例预览</span>
<span class="text-[var(--theme-color)]">MOC-2026-R01-0001</span>
<span class="text-[var(--theme-color)]">{{previewNumber}}</span>
<NButton size="small" type="primary" class="text-xs ml-auto">保存规则</NButton>
</div>
</div>
@@ -744,11 +879,42 @@ const handleTabChange = (v: number) => {
:model="typeInfo"
:rules="typeRules"
>
<NFormItem label="类型名称" path="name">
<NInput v-model:value="typeInfo.name" placeholder="如:仪表控制" />
<NFormItem label="类型名称" path="label">
<NInput v-model:value="typeInfo.label" placeholder="如:仪表控制" />
</NFormItem>
<NFormItem label="类型定义描述" path="desc">
<NInput v-model:value="typeInfo.desc" type="textarea" placeholder="该类型覆盖的变更范围说明" />
<NFormItem label="颜色" path="color">
<NColorPicker v-model:value="typeInfo.color" :modes="['hex']">
<template #trigger="{ value, onClick, ref: triggerRef }">
<div
:ref="triggerRef"
:style="{
width: '40px',
height: '26px',
borderRadius: '8px',
backgroundColor: value || '#333',
cursor: 'pointer',
border: '2px solid #fff',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
}"
@click="onClick"
></div>
</template>
</NColorPicker>
<NTag
v-if="typeInfo.label"
size="small"
class="ml-5"
:color="{
color: tagBgColor(typeInfo.color),
textColor: tagTextColor(typeInfo.color),
borderColor: tagBorderColor(typeInfo.color),
}"
>
{{typeInfo.label}}
</NTag>
</NFormItem>
<NFormItem label="类型定义描述" path="remark">
<NInput v-model:value="typeInfo.remark" type="textarea" placeholder="该类型覆盖的变更范围说明" />
</NFormItem>
</NForm>
</div>
@@ -775,15 +941,46 @@ const handleTabChange = (v: number) => {
:model="levelInfo"
:rules="levelRules"
>
<NFormItem label="等级名称" path="name">
<NInput v-model:value="levelInfo.name" placeholder="如:重大" />
<NFormItem label="等级名称" path="label">
<NInput v-model:value="levelInfo.label" placeholder="如:重大" />
</NFormItem>
<NFormItem label="等级判定方式" path="way">
<NInput v-model:value="levelInfo.way" placeholder="如:等级评分 ≥ 20 分" />
<NFormItem label="颜色" path="color">
<NColorPicker v-model:value="levelInfo.color" :modes="['hex']">
<template #trigger="{ value, onClick, ref: triggerRef }">
<div
:ref="triggerRef"
:style="{
width: '40px',
height: '26px',
borderRadius: '8px',
backgroundColor: value || '#333',
cursor: 'pointer',
border: '2px solid #fff',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
}"
@click="onClick"
></div>
</template>
</NColorPicker>
<NTag
v-if="levelInfo.label"
size="small"
class="ml-5"
:color="{
color: tagBgColor(levelInfo.color),
textColor: tagTextColor(levelInfo.color),
borderColor: tagBorderColor(levelInfo.color),
}"
>
{{levelInfo.label}}
</NTag>
</NFormItem>
<NFormItem label="对应分析工具(多选)" path="tools">
<NFormItem label="等级判定方式" path="remark">
<NInput v-model:value="levelInfo.remark" placeholder="如:等级评分 ≥ 20 分" />
</NFormItem>
<!-- <NFormItem label="对应分析工具(多选)" path="tools">
<NSelect v-model:value="levelInfo.tools" multiple :options="toolsOptions" placeholder="请选择分析工具" />
</NFormItem>
</NFormItem> -->
</NForm>
</div>
<template #footer>