<template>
|
<div class="app-container">
|
<div class="search_form">
|
<div>
|
<span class="search_title">事故名称:</span>
|
<el-input v-model="searchForm.accidentName"
|
style="width: 240px"
|
placeholder="请输入事故名称搜索"
|
@change="handleQuery"
|
clearable
|
:prefix-icon="Search" />
|
<span class="search_title ml10">事故类型:</span>
|
<el-select v-model="searchForm.accidentType"
|
clearable
|
@change="handleQuery"
|
style="width: 240px">
|
<el-option v-for="item in accidentTypeOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value" />
|
</el-select>
|
<el-button type="primary"
|
@click="handleQuery"
|
style="margin-left: 10px">
|
搜索
|
</el-button>
|
</div>
|
<div>
|
<el-button type="primary"
|
@click="openForm('add')">新增事故</el-button>
|
<el-button type="danger"
|
plain
|
@click="handleDelete">删除</el-button>
|
</div>
|
</div>
|
<div class="table_list">
|
<PIMTable rowKey="id"
|
:column="tableColumn"
|
:tableData="tableData"
|
:page="page"
|
:isSelection="true"
|
@selection-change="handleSelectionChange"
|
:tableLoading="tableLoading"
|
@pagination="pagination"
|
:total="page.total"></PIMTable>
|
</div>
|
<!-- 新增/编辑事故弹窗 -->
|
<el-dialog v-model="dialogVisible"
|
:title="dialogTitle"
|
width="800px"
|
:close-on-click-modal="false">
|
<el-form ref="formRef"
|
:model="form"
|
:rules="rules"
|
label-position="top"
|
label-width="150px">
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="事故编号"
|
prop="accidentCode">
|
<el-input v-model="form.accidentCode"
|
placeholder="请输入事故编号" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="事故名称"
|
prop="accidentName">
|
<el-input v-model="form.accidentName"
|
placeholder="请输入事故名称" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="事故发生时间:"
|
prop="happenTime">
|
<el-date-picker style="width: 100%"
|
v-model="form.happenTime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
type="datetime"
|
placeholder="请选择"
|
clearable />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="事故发生位置"
|
prop="happenLocation">
|
<el-input v-model="form.happenLocation"
|
placeholder="请输入事故发生位置" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="事故等级"
|
prop="accidentGrade">
|
<el-select v-model="form.accidentGrade"
|
placeholder="请选择事故等级"
|
style="width: 100%">
|
<el-option v-for="item in accidentGradeOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="事故类型"
|
prop="accidentType">
|
<el-select v-model="form.accidentType"
|
placeholder="请选择事故类型"
|
style="width: 100%">
|
<el-option v-for="item in accidentTypeOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="人员损失情况 "
|
prop="personLoss">
|
<el-input v-model="form.personLoss"
|
placeholder="请输入人员损失情况" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="直接财产损失(元)"
|
prop="assetLoss">
|
<el-input v-model="form.assetLoss"
|
type="number"
|
placeholder="请输入直接财产损失" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="上报人"
|
prop="createUser">
|
<el-select v-model="form.createUser"
|
placeholder="请选择"
|
disabled
|
clearable>
|
<el-option v-for="item in userList"
|
:key="item.userId"
|
:label="item.nickName"
|
:value="item.userId" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="上报时间"
|
prop="createTime">
|
<el-date-picker style="width: 100%"
|
v-model="form.createTime"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
type="datetime"
|
disabled
|
placeholder="请选择"
|
clearable />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-form-item label="事故直接原因"
|
prop="accidentCause">
|
<el-input v-model="form.accidentCause"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入事故直接原因" />
|
</el-form-item>
|
<el-form-item label="事故根本原因"
|
prop="rootCause">
|
<el-input v-model="form.rootCause"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入事故根本原因" />
|
</el-form-item>
|
<el-form-item label="生产影响情况"
|
prop="productionLoss">
|
<el-input v-model="form.productionLoss"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入生产影响情况" />
|
</el-form-item>
|
<el-form-item label="现场应急处置措施"
|
prop="handleMeasures">
|
<el-input v-model="form.handleMeasures"
|
type="textarea"
|
:rows="3"
|
placeholder="现场应急处置措施" />
|
</el-form-item>
|
<el-form-item label="备注"
|
prop="remark">
|
<el-input v-model="form.remark"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入备注" />
|
</el-form-item>
|
</el-form>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button type="primary"
|
@click="submitForm">确定</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
<!-- 查看事故详情弹窗 -->
|
<el-dialog v-model="viewDialogVisible"
|
title="事故详情"
|
width="900px"
|
:close-on-click-modal="false">
|
<div class="knowledge-detail">
|
<el-descriptions :column="2"
|
border>
|
<el-descriptions-item label="事故编码">
|
<span>{{ currentKnowledge.accidentCode }}</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="事故名称">
|
<span>{{ currentKnowledge.accidentName }}</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="事故发生时间">
|
{{ currentKnowledge.happenTime }}
|
</el-descriptions-item>
|
<el-descriptions-item label="事故发生地点">
|
{{ currentKnowledge.happenLocation }}
|
</el-descriptions-item>
|
<el-descriptions-item label="事故等级">
|
<el-tag :type="accidentGradeType(currentKnowledge.accidentGrade)">{{ currentKnowledge.accidentGrade }}</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="事故类型">
|
<el-tag type="info">{{ accidentTypeLabel(currentKnowledge.accidentType) }}</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="人员伤亡情况">
|
{{ currentKnowledge.personLoss }}
|
</el-descriptions-item>
|
<el-descriptions-item label="直接财产损失(元)">
|
{{ currentKnowledge.assetLoss }}
|
</el-descriptions-item>
|
<el-descriptions-item label="上报人">
|
{{ currentKnowledge.createUserName }}
|
</el-descriptions-item>
|
<el-descriptions-item label="上报时间">
|
{{ currentKnowledge.createTime }}
|
</el-descriptions-item>
|
</el-descriptions>
|
<div class="detail-section">
|
<h4>事故直接原因</h4>
|
<div class="detail-content">{{ currentKnowledge.accidentCause }}</div>
|
</div>
|
<div class="detail-section">
|
<h4>事故根本原因</h4>
|
<div class="detail-content">{{ currentKnowledge.rootCause }}</div>
|
</div>
|
<div class="detail-section">
|
<h4>生产影响情况</h4>
|
<div class="detail-content">{{ currentKnowledge.productionLoss }}</div>
|
</div>
|
<div class="detail-section">
|
<h4>现场应急处置措施</h4>
|
<div class="detail-content">{{ currentKnowledge.handleMeasures }}</div>
|
</div>
|
<div class="detail-section">
|
<h4>备注</h4>
|
<div class="detail-content">{{ currentKnowledge.remark }}</div>
|
</div>
|
</div>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="viewDialogVisible = false">关闭</el-button>
|
<!-- <el-button type="success" @click="markAsFavorite">收藏@</el-button> -->
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { Search } from "@element-plus/icons-vue";
|
import {
|
onMounted,
|
ref,
|
reactive,
|
toRefs,
|
getCurrentInstance,
|
computed,
|
} from "vue";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
import PIMTable from "@/components/PIMTable/PIMTable.vue";
|
import { userListNoPage } from "@/api/system/user.js";
|
import useUserStore from "@/store/modules/user";
|
import {
|
safeAccidentListPage,
|
safeAccidentAdd,
|
safeAccidentUpdate,
|
safeAccidentDel,
|
} from "@/api/safeProduction/accidentReportingRecord.js";
|
import dayjs from "dayjs";
|
const userStore = useUserStore();
|
// 表单验证规则
|
const rules = {
|
accidentCode: [
|
{ required: true, message: "请输入事故编号", trigger: "blur" },
|
],
|
accidentName: [
|
{ required: true, message: "请输入事故名称", trigger: "blur" },
|
],
|
happenTime: [
|
{ required: true, message: "请选择事故发生时间", trigger: "change" },
|
],
|
happenLocation: [
|
{ required: true, message: "请输入事故发生位置", trigger: "blur" },
|
],
|
accidentGrade: [
|
{ required: true, message: "请选择事故等级", trigger: "change" },
|
],
|
accidentType: [
|
{ required: true, message: "请选择事故类型", trigger: "change" },
|
],
|
};
|
|
// 响应式数据
|
const data = reactive({
|
searchForm: {
|
accidentName: "",
|
accidentType: "",
|
},
|
tableLoading: false,
|
page: {
|
current: 1,
|
size: 20,
|
total: 0,
|
},
|
tableData: [],
|
selectedIds: [],
|
form: {
|
accidentCode: "", // 事故编码
|
accidentName: "", // 事故名称
|
happenTime: "", // 事故发生时间
|
happenLocation: "", // 事故发生地点
|
accidentGrade: "", // 事故等级
|
accidentType: "", // 事故类型
|
personLoss: "", // 人员伤亡
|
assetLoss: "", // 资产损失
|
createUser: "", // 创建用户
|
createTime: "", // 创建时间
|
createUserName: "", // 创建用户名
|
accidentCause: "", // 事故直接原因
|
rootCause: "", // 事故根本原因
|
productionLoss: "", // 生产损失
|
handleMeasures: "", // 应急处置措施
|
remark: "", // 备注
|
},
|
dialogVisible: false,
|
dialogTitle: "",
|
dialogType: "add",
|
viewDialogVisible: false,
|
currentKnowledge: {},
|
});
|
|
const {
|
searchForm,
|
tableLoading,
|
page,
|
tableData,
|
selectedIds,
|
form,
|
dialogVisible,
|
dialogTitle,
|
dialogType,
|
viewDialogVisible,
|
currentKnowledge,
|
} = toRefs(data);
|
|
// 表单引用
|
const formRef = ref();
|
|
// 表格列配置
|
const tableColumn = ref([
|
{
|
label: "事故编码",
|
prop: "accidentCode",
|
showOverflowTooltip: true,
|
},
|
{
|
label: "事故名称",
|
prop: "accidentName",
|
showOverflowTooltip: true,
|
},
|
{
|
label: "事故发生时间",
|
prop: "happenTime",
|
showOverflowTooltip: true,
|
},
|
{
|
label: "事故发生位置",
|
prop: "happenLocation",
|
showOverflowTooltip: true,
|
},
|
{
|
label: "事故等级",
|
prop: "accidentGrade",
|
showOverflowTooltip: true,
|
},
|
{
|
label: "事故类型",
|
prop: "accidentType",
|
showOverflowTooltip: true,
|
formatData: params => {
|
return accidentTypeLabel(params);
|
},
|
},
|
{
|
dataType: "action",
|
label: "操作",
|
align: "center",
|
fixed: "right",
|
width: 200,
|
operation: [
|
{
|
name: "编辑",
|
type: "text",
|
clickFun: row => {
|
openForm("edit", row);
|
},
|
},
|
{
|
name: "查看",
|
type: "text",
|
clickFun: row => {
|
viewKnowledge(row);
|
},
|
},
|
],
|
},
|
]);
|
const userList = ref([]);
|
// 生命周期
|
onMounted(() => {
|
getCurrentFactoryName();
|
userListNoPage().then(res => {
|
userList.value = res.data;
|
});
|
getList();
|
startAutoRefresh();
|
});
|
const handleChange = userId => {
|
const selectedUser = userList.value.find(user => user.userId === userId);
|
if (selectedUser) {
|
form.value.coreResponsorUserName = selectedUser.nickName;
|
}
|
};
|
// 开始自动刷新
|
const startAutoRefresh = () => {
|
setInterval(() => {
|
getList();
|
}, 600000); // 10分钟刷新一次 (10 * 60 * 1000 = 600000ms)
|
};
|
|
// 查询数据
|
const handleQuery = () => {
|
page.value.current = 1;
|
getList();
|
};
|
const accidentGradeType = val => {
|
switch (val) {
|
case "轻微事故":
|
return "info";
|
case "一般事故":
|
return "info";
|
case "较大事故":
|
return "warning";
|
case "重大事故":
|
return "danger";
|
default:
|
return "info";
|
}
|
};
|
const accidentGradeOptions = [
|
{
|
label: "轻微事故",
|
value: "轻微事故",
|
},
|
{
|
label: "一般事故",
|
value: "一般事故",
|
},
|
{
|
label: "较大事故",
|
value: "较大事故",
|
},
|
{
|
label: "重大事故",
|
value: "重大事故",
|
},
|
];
|
const { proxy } = getCurrentInstance();
|
const { accident_type } = proxy.useDict("accident_type");
|
const accidentTypeOptions = computed(() => accident_type?.value || []);
|
const accidentTypeLabel = val => {
|
const item = accidentTypeOptions.value.find(
|
i => String(i.value) === String(val)
|
);
|
return item ? item.label : val;
|
};
|
const getList = () => {
|
tableLoading.value = true;
|
safeAccidentListPage({ ...page.value, ...searchForm.value })
|
.then(res => {
|
tableLoading.value = false;
|
tableData.value = res.data.records;
|
page.total = res.data.total;
|
})
|
.catch(err => {
|
tableLoading.value = false;
|
});
|
};
|
|
// 分页处理
|
const pagination = obj => {
|
page.value.current = obj.page;
|
page.value.size = obj.limit;
|
handleQuery();
|
};
|
const currentUserId = ref("");
|
const currentUserName = ref("");
|
const getCurrentFactoryName = async () => {
|
let res = await userStore.getInfo();
|
currentUserId.value = res.user.userId;
|
currentUserName.value = res.user.nickName;
|
};
|
// 选择变化处理
|
const handleSelectionChange = selection => {
|
selectedIds.value = selection.map(item => item.id);
|
};
|
|
// 打开表单
|
const openForm = (type, row = null) => {
|
dialogType.value = type;
|
if (type === "add") {
|
dialogTitle.value = "新增事故";
|
// 重置表单
|
Object.assign(form.value, {
|
accidentCode: "", // 事故编码
|
accidentName: "", // 事故名称
|
happenTime: "", // 事故发生时间
|
happenLocation: "", // 事故发生地点
|
accidentGrade: "", // 事故等级
|
accidentType: "", // 事故类型
|
personLoss: "", // 人员伤亡
|
assetLoss: "", // 资产损失
|
createUser: currentUserId.value, // 创建用户
|
createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 创建时间
|
createUserName: currentUserName.value, // 创建用户名
|
accidentCause: "", // 事故直接原因
|
rootCause: "", // 事故根本原因
|
productionLoss: "", // 生产损失
|
handleMeasures: "", // 应急处置措施
|
remark: "", // 备注
|
});
|
} else if (type === "edit" && row) {
|
dialogTitle.value = "编辑事故";
|
Object.assign(form.value, {
|
id: row.id,
|
accidentCode: row.accidentCode, // 事故编码
|
accidentName: row.accidentName, // 事故名称
|
happenTime: row.happenTime, // 事故发生时间
|
happenLocation: row.happenLocation, // 事故发生地点
|
accidentGrade: row.accidentGrade, // 事故等级
|
accidentType: row.accidentType, // 事故类型
|
personLoss: row.personLoss, // 人员伤亡
|
assetLoss: row.assetLoss, // 资产损失
|
createUser: row.createUser, // 创建用户
|
createTime: row.createTime, // 创建时间
|
createUserName: row.createUserName, // 创建用户名
|
accidentCause: row.accidentCause, // 事故直接原因
|
rootCause: row.rootCause, // 事故根本原因
|
productionLoss: row.productionLoss, // 生产损失
|
handleMeasures: row.handleMeasures, // 应急处置措施
|
remark: row.remark, // 备注
|
});
|
}
|
dialogVisible.value = true;
|
};
|
|
// 查看事故详情
|
const viewKnowledge = row => {
|
currentKnowledge.value = { ...row };
|
viewDialogVisible.value = true;
|
};
|
const getApplyScopeLabel = scope => {
|
const scopeMap = {
|
all: "全体员工",
|
manager: "管理层",
|
hr: "人事部门",
|
finance: "财务部门",
|
tech: "技术部门",
|
};
|
return scopeMap[scope] || scope;
|
};
|
|
// 获取类型标签类型
|
const getTypeTagType = type => {
|
const typeMap = {
|
contract: "success",
|
approval: "warning",
|
solution: "primary",
|
experience: "info",
|
guide: "danger",
|
};
|
return typeMap[type] || "info";
|
};
|
|
// 获取类型标签文本
|
const getTypeLabel = type => {
|
return getKnowledgeTypeLabel(type);
|
};
|
|
// 获取效率标签类型
|
const getEfficiencyTagType = efficiency => {
|
const typeMap = {
|
high: "success",
|
medium: "warning",
|
low: "info",
|
};
|
return typeMap[efficiency] || "info";
|
};
|
|
// 获取效率标签文本
|
const getEfficiencyLabel = efficiency => {
|
const efficiencyMap = {
|
high: "显著提升",
|
medium: "一般提升",
|
low: "轻微提升",
|
};
|
return efficiencyMap[efficiency] || efficiency;
|
};
|
|
// 获取效率提升百分比
|
const getEfficiencyScore = efficiency => {
|
const scoreMap = {
|
high: 40,
|
medium: 25,
|
low: 15,
|
};
|
return scoreMap[efficiency] || 0;
|
};
|
|
// 获取平均节省时间
|
const getTimeSaved = efficiency => {
|
const timeMap = {
|
high: "2-3天",
|
medium: "1-2天",
|
low: "0.5-1天",
|
};
|
return timeMap[efficiency] || "未知";
|
};
|
// 提交应急预案表单
|
const submitForm = async () => {
|
try {
|
await formRef.value.validate();
|
if (dialogType.value === "add") {
|
safeAccidentAdd({ ...form.value })
|
.then(res => {
|
if (res.code == 200) {
|
ElMessage.success("添加成功");
|
dialogVisible.value = false;
|
getList();
|
}
|
})
|
.catch(err => {
|
ElMessage.error(err.msg);
|
});
|
} else {
|
safeAccidentUpdate({ ...form.value })
|
.then(res => {
|
if (res.code == 200) {
|
ElMessage.success("更新成功");
|
dialogVisible.value = false;
|
getList();
|
}
|
})
|
.catch(err => {
|
ElMessage.error(err.msg);
|
});
|
}
|
} catch (error) {
|
console.error("表单验证失败:", error);
|
}
|
};
|
|
// 删除应急预案
|
const handleDelete = () => {
|
if (selectedIds.value.length === 0) {
|
ElMessage.warning("请选择要删除的应急预案");
|
return;
|
}
|
|
ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
// console.log(selectedIds.value);
|
safeAccidentDel(selectedIds.value).then(res => {
|
if (res.code == 200) {
|
ElMessage.success("删除成功");
|
selectedIds.value = [];
|
getList();
|
}
|
});
|
})
|
.catch(() => {
|
// 用户取消
|
});
|
};
|
|
// 字典工具
|
const knowledgeTypeOptions = computed(() => knowledge_type?.value || []);
|
const getKnowledgeTypeLabel = val => {
|
const item = knowledgeTypeOptions.value.find(
|
i => String(i.value) === String(val)
|
);
|
return item ? item.label : val;
|
};
|
const getKnowledgeTypeTagType = val => {
|
const item = knowledgeTypeOptions.value.find(
|
i => String(i.value) === String(val)
|
);
|
return item?.elTagType || "info";
|
};
|
const handleExport = () => {
|
proxy.download(
|
"/knowledgeBase/export",
|
{ ...searchForm.value },
|
"应急预案库.xlsx"
|
);
|
};
|
</script>
|
|
<style scoped>
|
.auto-refresh-info {
|
margin-bottom: 15px;
|
}
|
|
.auto-refresh-info .el-alert {
|
border-radius: 8px;
|
}
|
|
.dialog-footer {
|
text-align: right;
|
}
|
|
.knowledge-detail {
|
padding: 20px 0;
|
}
|
|
.detail-title {
|
font-size: 18px;
|
font-weight: bold;
|
color: #303133;
|
}
|
|
.detail-section {
|
margin-top: 24px;
|
}
|
|
.detail-section h4 {
|
margin: 0 0 12px 0;
|
font-size: 16px;
|
font-weight: 600;
|
color: #303133;
|
border-left: 4px solid #409eff;
|
padding-left: 12px;
|
}
|
|
.detail-content {
|
background: #f8f9fa;
|
padding: 16px;
|
border-radius: 6px;
|
line-height: 1.6;
|
color: #606266;
|
white-space: pre-wrap;
|
}
|
|
.key-points {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 8px;
|
}
|
|
.usage-stats {
|
margin-top: 16px;
|
}
|
|
.stat-item {
|
text-align: center;
|
padding: 20px;
|
background: #f8f9fa;
|
border-radius: 8px;
|
}
|
|
.stat-number {
|
font-size: 24px;
|
font-weight: bold;
|
color: #409eff;
|
margin-bottom: 8px;
|
}
|
|
.stat-label {
|
font-size: 14px;
|
color: #909399;
|
}
|
|
.exec-steps-container {
|
border: 1px solid #e4e7ed;
|
border-radius: 4px;
|
padding: 15px;
|
background-color: #f9fafc;
|
}
|
|
.exec-step-item {
|
margin-bottom: 10px;
|
padding: 10px;
|
background-color: #ffffff;
|
border: 1px solid #e4e7ed;
|
border-radius: 4px;
|
}
|
|
.step-header {
|
display: flex;
|
align-items: flex-start;
|
flex-direction: column;
|
}
|
|
.exec-step-view {
|
margin-bottom: 8px;
|
padding-left: 20px;
|
position: relative;
|
}
|
|
.step-number {
|
position: absolute;
|
left: 0;
|
font-weight: bold;
|
color: #409eff;
|
}
|
|
.step-title {
|
font-weight: bold;
|
margin-right: 5px;
|
}
|
|
.no-data {
|
color: #909399;
|
font-style: italic;
|
}
|
</style>
|