hazop插件修改完成
This commit is contained in:
Vendored
+6
@@ -42,6 +42,8 @@ declare module 'vue' {
|
|||||||
NCascader: typeof import('naive-ui')['NCascader']
|
NCascader: typeof import('naive-ui')['NCascader']
|
||||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
||||||
|
NCollapse: typeof import('naive-ui')['NCollapse']
|
||||||
|
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||||
NDatePicker: typeof import('naive-ui')['NDatePicker']
|
NDatePicker: typeof import('naive-ui')['NDatePicker']
|
||||||
@@ -59,6 +61,7 @@ declare module 'vue' {
|
|||||||
NImage: typeof import('naive-ui')['NImage']
|
NImage: typeof import('naive-ui')['NImage']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||||
|
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
|
||||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
@@ -132,6 +135,8 @@ declare global {
|
|||||||
const NCascader: typeof import('naive-ui')['NCascader']
|
const NCascader: typeof import('naive-ui')['NCascader']
|
||||||
const NCheckbox: typeof import('naive-ui')['NCheckbox']
|
const NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
const NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
const NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
|
||||||
|
const NCollapse: typeof import('naive-ui')['NCollapse']
|
||||||
|
const NCollapseItem: typeof import('naive-ui')['NCollapseItem']
|
||||||
const NColorPicker: typeof import('naive-ui')['NColorPicker']
|
const NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||||
const NDataTable: typeof import('naive-ui')['NDataTable']
|
const NDataTable: typeof import('naive-ui')['NDataTable']
|
||||||
const NDatePicker: typeof import('naive-ui')['NDatePicker']
|
const NDatePicker: typeof import('naive-ui')['NDatePicker']
|
||||||
@@ -149,6 +154,7 @@ declare global {
|
|||||||
const NImage: typeof import('naive-ui')['NImage']
|
const NImage: typeof import('naive-ui')['NImage']
|
||||||
const NInput: typeof import('naive-ui')['NInput']
|
const NInput: typeof import('naive-ui')['NInput']
|
||||||
const NInputGroup: typeof import('naive-ui')['NInputGroup']
|
const NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||||
|
const NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
|
||||||
const NInputNumber: typeof import('naive-ui')['NInputNumber']
|
const NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
const NMenu: typeof import('naive-ui')['NMenu']
|
const NMenu: typeof import('naive-ui')['NMenu']
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ const columns = computed<DataTableColumns<ChangeItem>>(() => {
|
|||||||
{
|
{
|
||||||
title: '变更前',
|
title: '变更前',
|
||||||
key: 'before_text',
|
key: 'before_text',
|
||||||
width: 200,
|
|
||||||
render: (row: ChangeItem) =>
|
render: (row: ChangeItem) =>
|
||||||
props.editable
|
props.editable
|
||||||
? h(RichTextInput, {
|
? h(RichTextInput, {
|
||||||
@@ -86,7 +85,6 @@ const columns = computed<DataTableColumns<ChangeItem>>(() => {
|
|||||||
{
|
{
|
||||||
title: '变更后',
|
title: '变更后',
|
||||||
key: 'after_text',
|
key: 'after_text',
|
||||||
width: 200,
|
|
||||||
render: (row: ChangeItem) =>
|
render: (row: ChangeItem) =>
|
||||||
props.editable
|
props.editable
|
||||||
? h(RichTextInput, {
|
? h(RichTextInput, {
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ const regenerateLoading = ref<boolean>(false);
|
|||||||
const submitLoading = ref<boolean>(false);
|
const submitLoading = ref<boolean>(false);
|
||||||
const confirmed = ref<boolean>(false);
|
const confirmed = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 是否重新生成
|
||||||
|
const regenerateTypes = ref<Record<string, boolean>>({
|
||||||
|
'form':false,
|
||||||
|
'risk':false,
|
||||||
|
'train':false,
|
||||||
|
'pssr':false,
|
||||||
|
'evaluation':false,
|
||||||
|
'close':false,
|
||||||
|
});
|
||||||
|
|
||||||
// 存储每个步骤选中的审核人 user_id
|
// 存储每个步骤选中的审核人 user_id
|
||||||
const approvers = ref<Record<number, number[]>>({})
|
const approvers = ref<Record<number, number[]>>({})
|
||||||
const flowList = ref<any[]>([]);
|
const flowList = ref<any[]>([]);
|
||||||
@@ -95,6 +105,7 @@ const download = async () => {
|
|||||||
}
|
}
|
||||||
// 重新生成:AI 调用逻辑由各标签页组件实现(按 type 分发),此处仅派发事件
|
// 重新生成:AI 调用逻辑由各标签页组件实现(按 type 分发),此处仅派发事件
|
||||||
const regenerate = async () => {
|
const regenerate = async () => {
|
||||||
|
regenerateTypes.value[props.type] = true;
|
||||||
emit('regenerate', props.type);
|
emit('regenerate', props.type);
|
||||||
}
|
}
|
||||||
// 供父组件控制「重新生成」按钮 loading
|
// 供父组件控制「重新生成」按钮 loading
|
||||||
@@ -173,25 +184,30 @@ defineExpose({clearApprovers, setRegenerateLoading})
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex flex-wrap items-center justify-between gap-2 border-t p-4">
|
<div class="flex flex-wrap items-center justify-between gap-2 border-t p-4">
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<NButton ghost type="success" :disabled="btnDisabled" :loading="downloadLoading" @click="download">
|
<NButton v-if="isAiOpen && type!=='risk'" ghost type="warning" :disabled="btnDisabled || regenerateLoading" @click="regenerate">
|
||||||
<Icon icon="material-symbols:download" class="size-16px" />
|
<Icon icon="tdesign:refresh" class="size-14px mr-1" :class="regenerateLoading?'animate-spin':''" />
|
||||||
<span v-if="props.type==='form'">下载变更申请表</span>
|
<template v-if="regenerateLoading">
|
||||||
<span v-if="props.type==='risk'">下载风险分析记录表</span>
|
<span>生成中...</span>
|
||||||
<span v-if="props.type==='train'">下载变更培训内容</span>
|
</template>
|
||||||
<span v-if="props.type==='pssr'">下载PSSR检查内容</span>
|
<template v-else>
|
||||||
<span v-if="props.type==='evaluation'">下载验收评价</span>
|
<span v-if="type==='form'">{{regenerateTypes[type]?'重新生成变更申请表':'生成变更申请表'}}</span>
|
||||||
<span v-if="props.type==='close'">下载变更关闭确认表</span>
|
<span v-if="type==='train'">{{regenerateTypes[type]?'重新生成变更培训内容':'生成变更培训内容'}}</span>
|
||||||
|
<span v-if="type==='pssr'">{{regenerateTypes[type]?'重新生成PSSR检查内容':'生成PSSR检查内容'}}</span>
|
||||||
|
<span v-if="type==='evaluation'">{{regenerateTypes[type]?'重新生成验收评价':'生成验收评价'}}</span>
|
||||||
|
<span v-if="type==='close'">{{regenerateTypes[type]?'重新生成变更关闭确认表':'生成变更关闭确认表'}}</span>
|
||||||
|
</template>
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton v-if="props.isAiOpen" ghost type="warning" :disabled="btnDisabled" :loading="regenerateLoading" @click="regenerate">
|
<NButton ghost type="success" :disabled="btnDisabled || downloadLoading" @click="download">
|
||||||
<Icon icon="tdesign:refresh" class="size-14px mr-1" />
|
<Icon v-if="downloadLoading" icon="tdesign:refresh" class="size-14px mr-1 animate-spin" />
|
||||||
<span v-if="props.type==='form'">重新生成变更申请表</span>
|
<Icon v-else icon="material-symbols:download" class="size-16px" />
|
||||||
<span v-if="props.type==='risk'">重新生成风险分析记录表</span>
|
<span v-if="type==='form'">下载变更申请表</span>
|
||||||
<span v-if="props.type==='train'">重新生成变更培训内容</span>
|
<span v-if="type==='risk'">下载风险分析记录表</span>
|
||||||
<span v-if="props.type==='pssr'">重新生成PSSR检查内容</span>
|
<span v-if="type==='train'">下载变更培训内容</span>
|
||||||
<span v-if="props.type==='evaluation'">重新生成验收评价</span>
|
<span v-if="type==='pssr'">下载PSSR检查内容</span>
|
||||||
<span v-if="props.type==='close'">重新生成变更关闭确认表</span>
|
<span v-if="type==='evaluation'">下载验收评价</span>
|
||||||
|
<span v-if="type==='close'">下载变更关闭确认表</span>
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton v-if="props.isAiOpen" ghost :type="confirmed ? 'success' : 'primary'" :disabled="btnDisabled" @click="confirmTab">
|
<NButton v-if="isAiOpen" ghost :type="confirmed ? 'success' : 'primary'" :disabled="btnDisabled" @click="confirmTab">
|
||||||
<Icon icon="ix:success" class="size-16px mr-1" /> {{ confirmed ? '已确认完毕' : '本标签内容确认完毕' }}
|
<Icon icon="ix:success" class="size-16px mr-1" /> {{ confirmed ? '已确认完毕' : '本标签内容确认完毕' }}
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
import { computed, h, onBeforeUnmount, onMounted, ref } from "vue";
|
||||||
import { Icon } from '@iconify/vue'
|
import { Icon } from '@iconify/vue'
|
||||||
import { useThemeStore } from '@/store/modules/theme';
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
import { useDialog } from 'naive-ui';
|
import { useDialog, NInput, NPopconfirm, NButton } from 'naive-ui';
|
||||||
import { peopleListApi } from '@/service/api/system';
|
import { peopleListApi } from '@/service/api/system';
|
||||||
import { hazopNodesApi, hazopSaveApi } from "@/service/api/plugin";
|
import { hazopNodesApi, hazopSaveApi } from "@/service/api/plugin";
|
||||||
|
|
||||||
@@ -13,7 +13,10 @@ const props = defineProps(['isAiOpen','currentId','hazopRiskMatrixConfig'])
|
|||||||
const emit = defineEmits(['close','toTable']);
|
const emit = defineEmits(['close','toTable']);
|
||||||
|
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const analyzing = ref(false);
|
const analyzing = ref<boolean>(false);
|
||||||
|
const inferring = ref<boolean>(false)
|
||||||
|
const inferred = ref<boolean>(false)
|
||||||
|
const keyword = ref<string>('');
|
||||||
// 新增/编辑弹窗
|
// 新增/编辑弹窗
|
||||||
const editModal = ref<boolean>(false);
|
const editModal = ref<boolean>(false);
|
||||||
// 当前弹框标题
|
// 当前弹框标题
|
||||||
@@ -22,7 +25,266 @@ const currentModalTitle = ref<string>('');
|
|||||||
const currentModalType = ref<string>('');
|
const currentModalType = ref<string>('');
|
||||||
// 当前弹框对象
|
// 当前弹框对象
|
||||||
const currentModalObj = ref<string>('');
|
const currentModalObj = ref<string>('');
|
||||||
|
// 总体工艺描述与节点设置弹窗
|
||||||
|
const openSetModal = ref<boolean>(false);
|
||||||
|
// 总体工艺描述
|
||||||
|
const description = ref<string>('');
|
||||||
|
const nodeList = ref<any[]>([]);
|
||||||
const name = ref<string>('');
|
const name = ref<string>('');
|
||||||
|
// 节点描述
|
||||||
|
const node_description = ref<string>('');
|
||||||
|
// 节点操作方式
|
||||||
|
const node_way = ref<string>('');
|
||||||
|
// 节点保护措施
|
||||||
|
const node_protection = ref<string>('');
|
||||||
|
// 分析列表弹窗
|
||||||
|
const openListModal = ref<boolean>(false);
|
||||||
|
|
||||||
|
// 分析列表弹框使用节点
|
||||||
|
const currentNodeListSource = ref<any>([])
|
||||||
|
const currentNodeList = ref<any>([])
|
||||||
|
const currentNodeIndex = ref<number>(-1)
|
||||||
|
|
||||||
|
// 保存数据
|
||||||
|
const saveData = () => {
|
||||||
|
currentNodeListSource.value = JSON.parse(JSON.stringify(currentNodeList.value))
|
||||||
|
}
|
||||||
|
// 搜索关键字
|
||||||
|
const handleSearch = () => {
|
||||||
|
const kw = keyword.value.trim()
|
||||||
|
if(!kw){
|
||||||
|
currentNodeList.value = JSON.parse(JSON.stringify(currentNodeListSource.value))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentNodeList.value = currentNodeListSource.value.map((item:any) => {
|
||||||
|
const objMatched = item.obj_name?.includes(kw)
|
||||||
|
const matchedDeviations = (item.deviations || []).filter((d:any) =>
|
||||||
|
d.deviation?.includes(kw)
|
||||||
|
)
|
||||||
|
// obj_name 命中 → 保留全部 deviations;否则只保留命中的 deviations
|
||||||
|
if (objMatched) {
|
||||||
|
return { ...item } // 浅拷贝,结构不变
|
||||||
|
}
|
||||||
|
// 只有 deviation 命中 → 保留该对象,但 deviations 只留下命中的
|
||||||
|
if (matchedDeviations.length) {
|
||||||
|
return {
|
||||||
|
...item, // 保留 id / node_id / obj_name 等
|
||||||
|
deviations: matchedDeviations // 命中项原样保留,rows 等都在
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}).filter(Boolean)
|
||||||
|
}
|
||||||
|
// 点击偏离
|
||||||
|
const clickDev = (dev:any,nodeIndex:number) => {
|
||||||
|
currentNodeIndex.value = nodeIndex
|
||||||
|
currentNodeList.value = nodes.value[nodeIndex]?.objects || []
|
||||||
|
currentNodeListSource.value = JSON.parse(JSON.stringify(currentNodeList.value))
|
||||||
|
currentDev.value = dev
|
||||||
|
}
|
||||||
|
// 分析列表弹框添加
|
||||||
|
const addListItem = (type: string,objIndex:number = -1) => {
|
||||||
|
// 分析对象
|
||||||
|
if(type === 'obj'){
|
||||||
|
currentNodeList.value.push({
|
||||||
|
id: 0,
|
||||||
|
obj_name: '',
|
||||||
|
node_id: nodes.value[currentNodeIndex.value]?.id,
|
||||||
|
deviations: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 偏离
|
||||||
|
if(type === 'dev'){
|
||||||
|
currentNodeList.value[objIndex].deviations.push({
|
||||||
|
id: 0,
|
||||||
|
object_id: currentNodeList.value[objIndex]?.id,
|
||||||
|
deviation: '',
|
||||||
|
rows: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
currentNodeListSource.value = JSON.parse(JSON.stringify(currentNodeList.value))
|
||||||
|
}
|
||||||
|
// 分析列表弹框删除
|
||||||
|
const removeListItem = (type: string,objIndex:number = -1,devIndex:number = -1) => {
|
||||||
|
// 分析对象
|
||||||
|
if(type === 'obj'){
|
||||||
|
currentNodeList.value.splice(objIndex, 1)
|
||||||
|
}
|
||||||
|
// 偏离
|
||||||
|
if(type === 'dev'){
|
||||||
|
currentNodeList.value[objIndex].deviations.splice(devIndex, 1)
|
||||||
|
}
|
||||||
|
currentNodeListSource.value = JSON.parse(JSON.stringify(currentNodeList.value))
|
||||||
|
}
|
||||||
|
// 分析列表弹框复制
|
||||||
|
const copyListItem = (type: string,objIndex:number = -1,devIndex:number = -1) => {
|
||||||
|
// 分析对象
|
||||||
|
if(type === 'obj'){
|
||||||
|
let list = currentNodeList.value[objIndex]?.deviations?.map((item:any) => ({
|
||||||
|
...item,
|
||||||
|
id: 0,
|
||||||
|
object_id: 0
|
||||||
|
}));
|
||||||
|
currentNodeList.value.push({
|
||||||
|
id: 0,
|
||||||
|
obj_name: currentNodeList.value[objIndex]?.obj_name,
|
||||||
|
node_id: currentNodeList.value[objIndex]?.node_id,
|
||||||
|
deviations: list || []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 偏离
|
||||||
|
if(type === 'dev'){
|
||||||
|
let list = currentNodeList.value[objIndex]?.deviations[devIndex]?.rows?.map((item:any) => ({
|
||||||
|
...item,
|
||||||
|
id: 0,
|
||||||
|
deviation_id: 0
|
||||||
|
}));
|
||||||
|
currentNodeList.value[objIndex].deviations.push({
|
||||||
|
id: 0,
|
||||||
|
object_id: currentNodeList.value[objIndex]?.id,
|
||||||
|
deviation: currentNodeList.value[objIndex]?.deviations[devIndex]?.deviation,
|
||||||
|
rows: list || []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
currentNodeListSource.value = JSON.parse(JSON.stringify(currentNodeList.value))
|
||||||
|
}
|
||||||
|
// 分析列表弹框保存
|
||||||
|
const listSave = () => {
|
||||||
|
const hasEmptyObjName = currentNodeList.value.some((item:any) => item.obj_name.trim() === '')
|
||||||
|
if (hasEmptyObjName) {
|
||||||
|
window.$message?.warning('分析对象名称不能为空')
|
||||||
|
}
|
||||||
|
const hasEmptyDeviation = currentNodeList.value.some((item:any) =>
|
||||||
|
item.deviations?.some((d:any) => d.deviation.trim() === '')
|
||||||
|
)
|
||||||
|
if (hasEmptyDeviation) {
|
||||||
|
window.$message?.warning('偏离名称不能为空')
|
||||||
|
}
|
||||||
|
nodes.value[currentNodeIndex.value].objects = currentNodeList.value
|
||||||
|
openListModal.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
label: '分析当前偏离',
|
||||||
|
key: 'current'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '分析全部偏离',
|
||||||
|
key: 'all'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const columns = ref([
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
key: 'index',
|
||||||
|
width: 60,
|
||||||
|
align: 'center',
|
||||||
|
render: (_:any, index:number) => {
|
||||||
|
return h('span',{}, { default:()=>index+1 })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '节点名称',
|
||||||
|
key: 'node_name',
|
||||||
|
align: 'center',
|
||||||
|
render: (row:any) => {
|
||||||
|
return h(NInput,{
|
||||||
|
size:'small',
|
||||||
|
type:'textarea',
|
||||||
|
placeholder:'节点名称',
|
||||||
|
class:'text-left',
|
||||||
|
value:row.node_name,
|
||||||
|
autosize: { minRows: 1 },
|
||||||
|
clearable: true,
|
||||||
|
onUpdateValue: (val:string) => {
|
||||||
|
row.node_name = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '节点描述',
|
||||||
|
key: 'description',
|
||||||
|
align: 'center',
|
||||||
|
render: (row:any) => {
|
||||||
|
return h(NInput,{
|
||||||
|
size:'small',
|
||||||
|
type:'textarea',
|
||||||
|
placeholder:'如:设计温度XX、设计压力XX、容积XX',
|
||||||
|
class:'text-left',
|
||||||
|
value:row.description,
|
||||||
|
autosize: { minRows: 1 },
|
||||||
|
clearable: true,
|
||||||
|
onUpdateValue: (val:string) => {
|
||||||
|
row.description = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 60,
|
||||||
|
align: 'center',
|
||||||
|
fixed: 'right',
|
||||||
|
render: (_:any,index:number) => {
|
||||||
|
return h('div', {class:'flex items-center justify-center gap-[5px]'}, {
|
||||||
|
default: () => [
|
||||||
|
h(NPopconfirm,{
|
||||||
|
// 组件 Props
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: () => {
|
||||||
|
removeItem(index)
|
||||||
|
},
|
||||||
|
// 可选:自定义确认/取消按钮的样式
|
||||||
|
positiveButtonProps: { size: 'tiny', type: 'primary' },
|
||||||
|
negativeButtonProps: { size: 'tiny' }
|
||||||
|
},{
|
||||||
|
// 插槽 (Slots)
|
||||||
|
trigger: () => h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
text: true,
|
||||||
|
size: 'tiny',
|
||||||
|
type: 'error',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: () => h(Icon, { icon: 'ep:delete',class:'size-14px' }),
|
||||||
|
default: () => '' // 按钮文字
|
||||||
|
}
|
||||||
|
),
|
||||||
|
default: () => '确定要删除吗?' // 弹出框的提示内容
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 弹框新增节点
|
||||||
|
const addNode = () => {
|
||||||
|
nodeList.value.push({id:0,node_name:name.value,change_id:props.currentId,objects:[]})
|
||||||
|
}
|
||||||
|
// 删除节点
|
||||||
|
const removeItem = (index:number) => {
|
||||||
|
nodeList.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
const setSave = async () => {
|
||||||
|
const hasEmptyNodeName = nodeList.value.some((item:any) => item.node_name === '');
|
||||||
|
if(hasEmptyNodeName){
|
||||||
|
return window.$message?.warning('请填写节点名称');
|
||||||
|
}
|
||||||
|
nodes.value = nodes.value.concat(nodeList.value);
|
||||||
|
openSetModal.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理下拉菜单选择
|
||||||
|
const handleSelect = (key: string) => {
|
||||||
|
runAi(key);
|
||||||
|
}
|
||||||
|
|
||||||
const selectOptions = computed(() => {
|
const selectOptions = computed(() => {
|
||||||
const l = props.hazopRiskMatrixConfig?.probability?.map((item:{key:string,label:string}, index:number) => ({
|
const l = props.hazopRiskMatrixConfig?.probability?.map((item:{key:string,label:string}, index:number) => ({
|
||||||
@@ -230,15 +492,31 @@ const exportWord = () => {
|
|||||||
window.$message?.info('还差接口')
|
window.$message?.info('还差接口')
|
||||||
}
|
}
|
||||||
|
|
||||||
// AI 分析当前偏离
|
// AI 自动分析偏离
|
||||||
const runAi = async () => {
|
const runAi = async (type: string) => {
|
||||||
if(!currentDev.value){
|
|
||||||
return window.$message?.warning("请先在左侧选择偏离")
|
|
||||||
}
|
|
||||||
if(currentDev.value.rows.length){
|
if(currentDev.value.rows.length){
|
||||||
return window.$message?.info("当前偏离已有分析记录,可手动新增行补充");
|
return window.$message?.info("当前偏离已有分析记录,可手动新增行补充");
|
||||||
}
|
}
|
||||||
|
// 分析当前偏离
|
||||||
|
if(type==='current'){
|
||||||
|
if(!currentDev.value){
|
||||||
|
return window.$message?.warning("请先在左侧选择偏离")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 分析全部偏离
|
||||||
|
if(type==='all'){
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
// AI 自动推演
|
||||||
|
const runInference = async () => {
|
||||||
|
inferring.value = true
|
||||||
|
inferred.value = false
|
||||||
|
setTimeout(() => {
|
||||||
|
inferring.value = false
|
||||||
|
inferred.value = true
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
// 根据l和s计算RR风险等级
|
// 根据l和s计算RR风险等级
|
||||||
const riskLevel = (l: number, s: number) => {
|
const riskLevel = (l: number, s: number) => {
|
||||||
@@ -329,11 +607,12 @@ const leftToggle = (id: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 打开弹窗
|
// 打开弹窗
|
||||||
const openModal = (type: string,obj: string, title: string,editName: string = '') => {
|
const openModal = (type: string,obj: string, title: string,editName: string = '',editDescription: string = '') => {
|
||||||
if(type==='add'){
|
if(type==='add'){
|
||||||
name.value = '';
|
name.value = '';
|
||||||
}else{
|
}else{
|
||||||
name.value = editName;
|
name.value = editName;
|
||||||
|
node_description.value = editDescription;
|
||||||
}
|
}
|
||||||
currentModalType.value = type;
|
currentModalType.value = type;
|
||||||
currentModalObj.value = obj;
|
currentModalObj.value = obj;
|
||||||
@@ -348,13 +627,14 @@ const modalSave = async () => {
|
|||||||
}
|
}
|
||||||
// 新增节点
|
// 新增节点
|
||||||
if(currentModalType.value==='add' && currentModalObj.value==='node'){
|
if(currentModalType.value==='add' && currentModalObj.value==='node'){
|
||||||
nodes.value.push({id:0,node_name:name.value,change_id:props.currentId,objects:[]});
|
nodes.value.push({id:0,node_name:name.value,change_id:props.currentId,objects:[],description:node_description.value});
|
||||||
window.$message?.success("新增节点成功")
|
window.$message?.success("新增节点成功")
|
||||||
editModal.value = false;
|
editModal.value = false;
|
||||||
}
|
}
|
||||||
// 编辑节点
|
// 编辑节点
|
||||||
if(currentModalType.value==='edit' && currentModalObj.value==='node'){
|
if(currentModalType.value==='edit' && currentModalObj.value==='node'){
|
||||||
nodes.value[contextTarget.value?.nodeIndex].node_name = name.value;
|
nodes.value[contextTarget.value?.nodeIndex].node_name = name.value;
|
||||||
|
nodes.value[contextTarget.value?.nodeIndex].description = node_description.value;
|
||||||
window.$message?.success("编辑节点成功")
|
window.$message?.success("编辑节点成功")
|
||||||
editModal.value = false;
|
editModal.value = false;
|
||||||
}
|
}
|
||||||
@@ -500,7 +780,7 @@ const handleMenuSelect = (key: string) => {
|
|||||||
break
|
break
|
||||||
case 'editNode':
|
case 'editNode':
|
||||||
// 编辑节点
|
// 编辑节点
|
||||||
openModal('edit','node','编辑节点',node?.node_name);
|
openModal('edit','node','编辑节点',node?.node_name,node?.description);
|
||||||
break
|
break
|
||||||
case 'delNode':
|
case 'delNode':
|
||||||
// 删除
|
// 删除
|
||||||
@@ -556,7 +836,7 @@ const getHazopNodes = async () => {
|
|||||||
if(!error){
|
if(!error){
|
||||||
nodes.value = data.nodes;
|
nodes.value = data.nodes;
|
||||||
selectedWorkers.value = data.users;
|
selectedWorkers.value = data.users;
|
||||||
currentDev.value = nodes.value[0]?.objects[0]?.deviations?.[0] || null;
|
clickDev(nodes.value[0]?.objects[0]?.deviations?.[0] || null,0)
|
||||||
}
|
}
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@@ -630,17 +910,28 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-auto flex items-center gap-1.5">
|
<div class="ml-auto flex items-center gap-1.5">
|
||||||
<NButton v-if="isAiOpen" size="small" ghost type="primary" class="text-xs" :disabled="recTotal === 0" @click="enterToTable('all')">
|
<NButton size="small" ghost type="primary" class="text-xs" @click="openSetModal = true;nodeList = [];description = '';">
|
||||||
<Icon icon="lucide:sparkles" class="size-12px mr-1" /> AI 整理入表
|
<Icon icon="akar-icons:file" class="size-12px mr-1" /> 总体工艺描述与节点设置
|
||||||
|
</NButton>
|
||||||
|
<NButton size="small" ghost type="primary" class="text-xs" :disabled="recTotal === 0" @click="enterToTable('all')">
|
||||||
|
<Icon icon="carbon:document-import" class="size-14px mr-1" /> 全部整理入表
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton size="small" ghost type="primary" class="text-xs" @click="exportWord">
|
<NButton size="small" ghost type="primary" class="text-xs" @click="exportWord">
|
||||||
<Icon icon="material-symbols:download" class="size-14px" /> 导出 Word
|
<Icon icon="material-symbols:download" class="size-14px" /> 导出 Word
|
||||||
</NButton>
|
|
||||||
<NButton v-if="isAiOpen" size="small" type="primary" class="text-xs" :disabled="analyzing" @click="runAi">
|
|
||||||
<Icon v-if="analyzing" icon="ri:loader-4-fill" class="size-14px animate-spin" />
|
|
||||||
<Icon v-else icon="lucide:sparkles" class="size-12px" />
|
|
||||||
AI 分析当前偏离
|
|
||||||
</NButton>
|
</NButton>
|
||||||
|
<NDropdown
|
||||||
|
v-if="isAiOpen"
|
||||||
|
trigger="hover"
|
||||||
|
:options="options"
|
||||||
|
@select="handleSelect"
|
||||||
|
>
|
||||||
|
<NButton size="small" type="primary" class="text-xs" :disabled="analyzing">
|
||||||
|
<Icon v-if="analyzing" icon="ri:loader-4-fill" class="size-14px animate-spin" />
|
||||||
|
<Icon v-else icon="lucide:sparkles" class="size-12px" />
|
||||||
|
AI 自动分析
|
||||||
|
<Icon icon="ant-design:caret-down-filled" class="size-12px ml-1" />
|
||||||
|
</NButton>
|
||||||
|
</NDropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -666,6 +957,7 @@ onBeforeUnmount(() => {
|
|||||||
<Icon icon="ic:round-plus" class="size-14px" />新增节点
|
<Icon icon="ic:round-plus" class="size-14px" />新增节点
|
||||||
</NButton>
|
</NButton>
|
||||||
<div class="shrink-0 rounded-md border p-2 scroll">
|
<div class="shrink-0 rounded-md border p-2 scroll">
|
||||||
|
<p v-if="nodes.length === 0" class="text-center text-xs text-slate-400 py-10">暂无节点数据</p>
|
||||||
<div v-for="(node, nodeIndex) in nodes" :key="nodeIndex">
|
<div v-for="(node, nodeIndex) in nodes" :key="nodeIndex">
|
||||||
<div
|
<div
|
||||||
class="cursor-pointer flex items-center select-none"
|
class="cursor-pointer flex items-center select-none"
|
||||||
@@ -689,7 +981,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
<template v-if="!collapsed[node.id+'_'+obj.id]">
|
<template v-if="!collapsed[node.id+'_'+obj.id]">
|
||||||
<div v-for="(dev, devIndex) in obj.deviations" :key="devIndex"
|
<div v-for="(dev, devIndex) in obj.deviations" :key="devIndex"
|
||||||
@click="currentDev = dev"
|
@click="clickDev(dev,Number(nodeIndex))"
|
||||||
@contextmenu.prevent="handleContextMenu($event, 'dev', node, obj, dev, Number(nodeIndex), Number(objIndex), Number(devIndex))"
|
@contextmenu.prevent="handleContextMenu($event, 'dev', node, obj, dev, Number(nodeIndex), Number(objIndex), Number(devIndex))"
|
||||||
class="cursor-pointer flex w-full items-center ml-2 gap-1.5 rounded-md px-3 py-1.5 text-left text-xs transition"
|
class="cursor-pointer flex w-full items-center ml-2 gap-1.5 rounded-md px-3 py-1.5 text-left text-xs transition"
|
||||||
:class="currentDev?.id === dev.id ? 'bg-[var(--theme-color)] text-white' : 'text-slate-600 hover:bg-slate-100'"
|
:class="currentDev?.id === dev.id ? 'bg-[var(--theme-color)] text-white' : 'text-slate-600 hover:bg-slate-100'"
|
||||||
@@ -713,7 +1005,15 @@ onBeforeUnmount(() => {
|
|||||||
<NButton size="small" ghost type="primary" class="text-xs" @click="addRow">
|
<NButton size="small" ghost type="primary" class="text-xs" @click="addRow">
|
||||||
<Icon icon="ic:round-plus" class="size-14px" /> 新增行
|
<Icon icon="ic:round-plus" class="size-14px" /> 新增行
|
||||||
</NButton>
|
</NButton>
|
||||||
<span class="text-[11px] text-slate-400">单元格点击即可修改;风险等级按风险矩阵L / S 取值实时重算;「入表」将本条选入风险分析记录表</span>
|
<NButton v-if="currentDev" size="small" type="primary" class="text-xs" @click="openListModal = true">
|
||||||
|
<Icon icon="ant-design:bars-outlined" class="size-14px" /> 分析列表
|
||||||
|
</NButton>
|
||||||
|
<NButton v-if="isAiOpen && currentDev" size="small" type="primary" class="text-xs" :disabled="analyzing" @click="runAi('current')">
|
||||||
|
<Icon v-if="analyzing" icon="ri:loader-4-fill" class="size-14px animate-spin" />
|
||||||
|
<Icon v-else icon="lucide:sparkles" class="size-12px" />
|
||||||
|
AI 分析当前偏离
|
||||||
|
</NButton>
|
||||||
|
<span class="text-[11px] text-slate-400">风险等级按风险矩阵L / S 取值实时重算;「入表」将本条选入风险分析记录表</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="scroll">
|
<div class="scroll">
|
||||||
<table class="w-full border-collapse text-xs">
|
<table class="w-full border-collapse text-xs">
|
||||||
@@ -881,6 +1181,7 @@ onBeforeUnmount(() => {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<NInput v-model:value="name" placeholder="请输入名称" />
|
<NInput v-model:value="name" placeholder="请输入名称" />
|
||||||
|
<NInput v-if="currentModalObj==='node'" class="mt-3" v-model:value="node_description" placeholder="请输入节点描述" />
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-end gap-[10px]">
|
<div class="flex justify-end gap-[10px]">
|
||||||
@@ -889,6 +1190,221 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NModal>
|
</NModal>
|
||||||
|
<!-- 总体工艺描述与节点设置弹窗 -->
|
||||||
|
<NModal
|
||||||
|
v-model:show="openSetModal"
|
||||||
|
preset="card"
|
||||||
|
title="总体工艺描述与节点设置"
|
||||||
|
:auto-focus="true"
|
||||||
|
:style="{ width: '1000px', height: 'auto' }"
|
||||||
|
:segmented="{ content: true, footer: true }"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div v-if="isAiOpen" class="rounded-lg border border-indigo-100 bg-indigo-50/60 p-3 mb-3">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2 text-sm text-indigo-700">
|
||||||
|
<Icon icon="bx:bot" class="size-16px" />
|
||||||
|
{{
|
||||||
|
inferring
|
||||||
|
? 'AI正在根据MOC工艺描述推演工艺流程、涉及设备与关键参数…'
|
||||||
|
: inferred
|
||||||
|
? 'AI推演完成,已补全空缺内容,可手动修改后保存'
|
||||||
|
: '可点击右侧按钮,由AI根据工艺描述自动推演需要分析的设备及参数'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<n-button size="small" type="primary" @click="runInference" :disabled="inferring || !description">
|
||||||
|
<Icon v-if="inferring" icon="ri:loader-4-fill" class="size-14px animate-spin" />
|
||||||
|
<Icon v-else icon="lucide:sparkles" class="size-12px" />
|
||||||
|
{{ inferring ? '推演中…' : 'AI自动推演' }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mb-1">工艺描述</p>
|
||||||
|
<NInput
|
||||||
|
v-model:value="description"
|
||||||
|
type="textarea"
|
||||||
|
:rows="4"
|
||||||
|
clearable
|
||||||
|
placeholder="简述本节点的工艺流程、物料组成、上下游关系……"
|
||||||
|
/>
|
||||||
|
<div class="flex items-center justify-between mt-3">
|
||||||
|
<label class="block text-sm font-medium text-slate-700">
|
||||||
|
节点设置
|
||||||
|
<span class="ml-2 text-xs font-normal text-slate-400">MOC场景通常一个节点即可,涉及多个时逐行罗列</span>
|
||||||
|
</label>
|
||||||
|
<NButton size="small" dashed type="primary" @click="addNode">
|
||||||
|
<Icon icon="ic:round-plus" class="size-14px" />新增节点
|
||||||
|
</NButton>
|
||||||
|
</div>
|
||||||
|
<NDataTable
|
||||||
|
class="mt-1"
|
||||||
|
size="small"
|
||||||
|
:single-line="false"
|
||||||
|
:columns="columns"
|
||||||
|
:data="nodeList"
|
||||||
|
:pagination="false"
|
||||||
|
:max-height="220"
|
||||||
|
>
|
||||||
|
<template #empty>
|
||||||
|
<p class="text-slate-400 text-sm">
|
||||||
|
暂无节点 — 点击「新增节点」手动添加{{isAiOpen?',或点击上方「AI自动推演」自动生成':''}}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</NDataTable>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex justify-end gap-[10px]">
|
||||||
|
<NButton size="small" @click="openSetModal = false">取消</NButton>
|
||||||
|
<NButton size="small" type="primary" @click="setSave">确定</NButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
|
<!-- 分析列表弹窗 -->
|
||||||
|
<NModal
|
||||||
|
v-model:show="openListModal"
|
||||||
|
preset="card"
|
||||||
|
:auto-focus="true"
|
||||||
|
:style="{ width: '1200px', height: 'auto' }"
|
||||||
|
:segmented="{ content: true, footer: true }"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h2 class="flex items-center gap-2 text-base text-lg leading-none font-semibold">
|
||||||
|
分析列表
|
||||||
|
<span class="text-xs font-normal text-slate-400">编辑全部分析对象及对应偏离,支持增删改与拖拽排序</span>
|
||||||
|
</h2>
|
||||||
|
<div class="flex items-center gap-[10px] mr-[10px]">
|
||||||
|
<NButton size="small" dashed type="primary" @click="addListItem('obj')"><Icon icon="ic:round-plus" class="size-14px" />分析对象</NButton>
|
||||||
|
<NButton size="small" dashed type="primary">AI设置偏离</NButton>
|
||||||
|
<NInput
|
||||||
|
size="small"
|
||||||
|
v-model:value="keyword"
|
||||||
|
clearable
|
||||||
|
placeholder="输入关键字"
|
||||||
|
@clear="keyword = ''; handleSearch()"
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<NButton text @click="handleSearch">
|
||||||
|
<Icon icon="akar-icons:search" class="size-14px text-slate-400 ml-1" />
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
</NInput>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="flex-1 max-h-[60vh] overflow-y-auto border-r mr-3 pr-3">
|
||||||
|
<div>
|
||||||
|
<p class="mb-1">本节点流程描述</p>
|
||||||
|
<NInput v-model:value="node_description" type="textarea" :autosize="{ minRows: 4,}" clearable placeholder="请输入本节点流程描述" />
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<p class="mb-1">本节点操作方式描述</p>
|
||||||
|
<NInput v-model:value="node_way" type="textarea" :autosize="{ minRows: 4,}" clearable placeholder="请输入本节点操作方式描述" />
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<p class="mb-1">本节点现有保护措施罗列</p>
|
||||||
|
<NInput v-model:value="node_protection" type="textarea" :autosize="{ minRows: 4,}" clearable placeholder="请输入本节点现有保护措施罗列" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-500px max-h-[60vh] overflow-y-auto">
|
||||||
|
<NCollapse v-if="currentNodeList.length" :trigger-areas="['arrow']">
|
||||||
|
<template v-for="(obj,objIndex) in currentNodeList" :key="objIndex">
|
||||||
|
<NCollapseItem
|
||||||
|
:title="obj.obj_name"
|
||||||
|
:name="objIndex"
|
||||||
|
:draggable="true"
|
||||||
|
@dragstart="onDeviceDragStart($event, objIndex)"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="onDeviceDrop($event, objIndex)"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="mr-[5px] cursor-grab" :draggable="true">
|
||||||
|
<Icon icon="gravity-ui:grip" class="size-12px" />
|
||||||
|
</div>
|
||||||
|
<NInput v-model:value="obj.obj_name" size="small" clearable placeholder="分析对象名称" style="width: 300px;" @blur="saveData" />
|
||||||
|
</template>
|
||||||
|
<template #header-extra>
|
||||||
|
<div class="flex items-center gap-[10px]">
|
||||||
|
<NButton size="tiny" dashed @click="addListItem('dev',Number(objIndex))"><Icon icon="ic:round-plus" class="size-12px" />偏离</NButton>
|
||||||
|
<NButton text @click="copyListItem('obj',Number(objIndex))">
|
||||||
|
<Icon icon="boxicons:copy" class="size-14px" />
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm
|
||||||
|
positive-text="确定"
|
||||||
|
:positiveButtonProps="{ size: 'tiny', type: 'primary' }"
|
||||||
|
:negativeButtonProps="{ size: 'tiny' }"
|
||||||
|
@positive-click="removeListItem('obj',Number(objIndex))"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<NButton text type="error">
|
||||||
|
<Icon icon="ep:delete" class="size-14px" />
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
确定删除吗?
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-if="obj?.deviations?.length" class="flex flex-col gap-[15px] ml-[40px]">
|
||||||
|
<div
|
||||||
|
v-for="(dev, devIndex) in obj.deviations"
|
||||||
|
:key="devIndex"
|
||||||
|
class="flex items-center justify-between"
|
||||||
|
:draggable="true"
|
||||||
|
@dragstart="onDeviateDragStart($event, objIndex, devIndex)"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="onDeviateDrop($event, objIndex, devIndex)"
|
||||||
|
>
|
||||||
|
<div class="flex items-center gap-[5px]">
|
||||||
|
<div class="mr-[5px] cursor-grab" :draggable="true">
|
||||||
|
<Icon icon="gravity-ui:grip" class="size-12px" />
|
||||||
|
</div>
|
||||||
|
<Split :size="14" class="text-orange-400" />
|
||||||
|
<NInput v-model:value="dev.deviation" size="small" clearable placeholder="偏离名称" style="width: 300px;" @blur="saveData" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-[10px]">
|
||||||
|
<span v-if="dev?.rows?.length" class="rounded-full bg-emerald-100 px-1.5 text-[10px] text-emerald-600">
|
||||||
|
{{ dev?.rows?.length }}条
|
||||||
|
</span>
|
||||||
|
<span v-else class="rounded-full bg-slate-100 px-1.5 text-[10px] text-slate-400">待分析</span>
|
||||||
|
<NButton text @click="copyListItem('dev',Number(objIndex), Number(devIndex))">
|
||||||
|
<Icon icon="boxicons:copy" class="size-14px" />
|
||||||
|
</NButton>
|
||||||
|
<NPopconfirm
|
||||||
|
positive-text="确定"
|
||||||
|
:positiveButtonProps="{ size: 'tiny', type: 'primary' }"
|
||||||
|
:negativeButtonProps="{ size: 'tiny' }"
|
||||||
|
@positive-click="removeListItem('dev',Number(objIndex), Number(devIndex))"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<NButton text type="error">
|
||||||
|
<Icon icon="ep:delete" class="size-14px" />
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
确定删除吗?
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NCollapseItem>
|
||||||
|
</template>
|
||||||
|
</NCollapse>
|
||||||
|
<NEmpty v-else description="暂无数据" size="large">
|
||||||
|
<template #icon>
|
||||||
|
<Icon icon="lucide:file-search-corner" class="size-48px" />
|
||||||
|
</template>
|
||||||
|
</NEmpty>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div class="flex justify-end gap-[10px]">
|
||||||
|
<NButton size="small" @click="openListModal = false">取消</NButton>
|
||||||
|
<NButton size="small" type="primary" @click="listSave">确定</NButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -462,7 +462,7 @@ onMounted(() => {
|
|||||||
@confirmTab="confirmTab"
|
@confirmTab="confirmTab"
|
||||||
/>
|
/>
|
||||||
<!-- 插件抽屉 -->
|
<!-- 插件抽屉 -->
|
||||||
<NDrawer v-model:show="pluginDrawer" width="85%" placement="right">
|
<NDrawer v-model:show="pluginDrawer" width="100%" placement="right">
|
||||||
<NDrawerContent>
|
<NDrawerContent>
|
||||||
<template v-if="currentPlugin === 'RISK'">
|
<template v-if="currentPlugin === 'RISK'">
|
||||||
<RiskPre :isAiOpen="props.isAiOpen" :currentId="props.currentId" @close="closePlugin" @toTable="exportAll" />
|
<RiskPre :isAiOpen="props.isAiOpen" :currentId="props.currentId" @close="closePlugin" @toTable="exportAll" />
|
||||||
|
|||||||
@@ -199,8 +199,8 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-auto flex items-center gap-1.5">
|
<div class="ml-auto flex items-center gap-1.5">
|
||||||
<NButton v-if="isAiOpen" size="small" ghost type="primary" class="text-xs" :disabled="riskRecords.length === 0" @click="enterToTable('all',null)">
|
<NButton size="small" ghost type="primary" class="text-xs" :disabled="riskRecords.length === 0" @click="enterToTable('all',null)">
|
||||||
<Icon icon="lucide:sparkles" class="size-12px mr-1" /> AI 整理入表
|
<Icon icon="carbon:document-import" class="size-14px mr-1" /> 全部整理入表
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton size="small" ghost type="primary" class="text-xs" @click="generateReport">
|
<NButton size="small" ghost type="primary" class="text-xs" @click="generateReport">
|
||||||
<Icon icon="material-symbols:download" class="size-14px" /> 导出 Word
|
<Icon icon="material-symbols:download" class="size-14px" /> 导出 Word
|
||||||
|
|||||||
Reference in New Issue
Block a user