src/views/safety/risk-source/index.vue
@@ -3,36 +3,52 @@
import { onMounted, reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { Alert, Button, Card, Form, FormItem, Input, InputNumber, message, Modal, Popconfirm, Select, Space, Table, Tag } from 'ant-design-vue';
import { createRiskSource, deleteRiskSource, getRiskSourcePage, updateRiskSource } from '#/api/safety/risk-source';
import { createRiskSource, deleteRiskSource, getRiskSourcePage, refreshRiskSourceFromCollect, updateRiskSource } from '#/api/safety/risk-source';
defineOptions({ name: 'SafetyRiskSource' });
const rows=ref<SafetyRiskSource[]>([]); const loading=ref(false); const saving=ref(false); const open=ref(false);
const filters=reactive<{name?:string}>({}); const paging=reactive({current:1,pageSize:10,total:0});
const blank=():SafetyRiskSource=>({name:'',category:'MAJOR',location:'',metricName:'',unit:'',lowerLimit:undefined,upperLimit:undefined,currentValue:undefined,remark:''});
const blank=():SafetyRiskSource=>({name:'',category:'MAJOR',location:'',metricName:'',unit:'',lowerLimit:undefined,upperLimit:undefined,currentValue:undefined,remark:'',machineryId:undefined,metricKey:''});
const model=reactive<SafetyRiskSource>(blank());
const categories:Record<string,string>={MAJOR:'重大危险源',HIGH_VOLTAGE:'高压试验区域',OTHER:'其他'};
const states:Record<string,string>={NORMAL:'正常',ALARM:'超限',UNKNOWN:'未知'};
const columns=[{title:'危险源名称',dataIndex:'name'},{title:'类别',key:'category'},{title:'位置',dataIndex:'location'},
 {title:'监测指标',dataIndex:'metricName'},{title:'当前值',key:'value'},{title:'阈值',key:'limits'},
 {title:'监测指标',dataIndex:'metricName'},{title:'数据来源',key:'source'},{title:'当前值',key:'value'},{title:'阈值',key:'limits'},
 {title:'数值变更时间',dataIndex:'observedAt'},{title:'状态',key:'status'},{title:'操作',key:'action',width:150}];
async function load(reset=false){if(reset)paging.current=1;loading.value=true;try{const r=await getRiskSourcePage({pageNo:paging.current,pageSize:paging.pageSize,...filters});rows.value=r.list;paging.total=r.total;}finally{loading.value=false;}}
function edit(row?:SafetyRiskSource){Object.assign(model,blank(),{id:undefined},row||{});open.value=true;}
async function save(){
 if(!model.name.trim()||!model.metricName.trim()){message.warning('请填写危险源名称和监测指标');return;}
 if(model.lowerLimit!=null&&model.upperLimit!=null&&model.lowerLimit>model.upperLimit){message.warning('下限不能大于上限');return;}
 if(model.machineryId&&!model.metricKey?.trim()){message.warning('绑定设备后必须填写数采指标键');return;}
 if(!model.machineryId&&model.metricKey?.trim()){message.warning('填写数采指标键前请先绑定设备');return;}
 saving.value=true;try{if(model.id)await updateRiskSource(model);else await createRiskSource(model);message.success('保存成功');open.value=false;await load();}finally{saving.value=false;}
}
async function remove(id:number){await deleteRiskSource(id);message.success('已删除');await load();}
/** 从设备采集刷新已绑定设备的危险源 */
const refreshing=ref(false);
async function refreshFromCollect(){
 if(refreshing.value)return;
 refreshing.value=true;
 try{
  const alarms=await refreshRiskSourceFromCollect();
  const count=alarms?.length??0;
  if(count>0){message.warning(`已刷新,${count} 个危险源超限:${alarms.map(a=>a.name).join('、')}`);}
  else{message.success('已刷新,暂无超限危险源');}
  await load();
 }finally{refreshing.value=false;}
}
onMounted(()=>load());
</script>
<template>
 <Page auto-content-height>
  <Card title="危险源台账">
   <Alert class="mb-4" type="info" show-icon message="人工维护当前监测值和阈值;此页面不代表现场自动采集,亦不保存历史观测序列。" />
   <Space class="mb-3"><Input v-model:value="filters.name" placeholder="危险源名称" allow-clear /><Button @click="load(true)">查询</Button><Button v-access:code="['safety:risk:create']" type="primary" @click="edit()">新增危险源</Button></Space>
   <Alert class="mb-4" type="info" show-icon message="未绑定设备的危险源由人工维护当前值;绑定设备并填写数采指标键后,可点「从设备采集刷新」自动取值并按阈值判定,不保存历史观测序列。" />
   <Space class="mb-3"><Input v-model:value="filters.name" placeholder="危险源名称" allow-clear /><Button @click="load(true)">查询</Button><Button v-access:code="['safety:risk:create']" type="primary" @click="edit()">新增危险源</Button><Button v-access:code="['safety:risk:update']" :loading="refreshing" @click="refreshFromCollect">从设备采集刷新</Button></Space>
   <Table :columns="columns" :data-source="rows" :loading="loading" :pagination="paging" row-key="id" :scroll="{x:1100}" @change="p=>{paging.current=p.current||1;paging.pageSize=p.pageSize||10;load();}">
    <template #bodyCell="{column,record}">
     <template v-if="column.key==='category'">{{categories[record.category]||record.category}}</template>
     <template v-else-if="column.key==='source'"><Tag :color="record.machineryId?'blue':'default'">{{record.machineryId?`设备采集(${record.metricKey||'-'})`:'人工录入'}}</Tag></template>
     <template v-else-if="column.key==='value'">{{record.currentValue??'未录入'}} {{record.unit||''}}</template>
     <template v-else-if="column.key==='limits'">{{record.lowerLimit??'不限'}} ~ {{record.upperLimit??'不限'}} {{record.unit||''}}</template>
     <Tag v-else-if="column.key==='status'" :color="record.riskStatus==='ALARM'?'red':record.riskStatus==='NORMAL'?'green':'default'">{{states[record.riskStatus]||'未知'}}</Tag>
@@ -49,6 +65,12 @@
    <FormItem label="单位"><Input v-model:value="model.unit" :maxlength="32" /></FormItem>
    <FormItem label="下限(可空)"><InputNumber v-model:value="model.lowerLimit" class="w-full" /></FormItem>
    <FormItem label="上限(可空)"><InputNumber v-model:value="model.upperLimit" class="w-full" /></FormItem>
    <FormItem label="关联设备编号(可空)" extra="填写后由数采自动取值,人工当前值将被覆盖">
     <InputNumber v-model:value="model.machineryId" class="w-full" :min="1" placeholder="设备台账编号" />
    </FormItem>
    <FormItem label="数采指标键(可空)" extra="取 mes_collect_realtime.data_json 中的字段名,如 current、speed;也可用 ot/sts/opm">
     <Input v-model:value="model.metricKey" :maxlength="64" placeholder="如 current" />
    </FormItem>
    <FormItem label="当前值(人工录入)"><InputNumber v-model:value="model.currentValue" class="w-full" /></FormItem>
    <FormItem label="备注"><Input.TextArea v-model:value="model.remark" :maxlength="500" /></FormItem>
   </Form>