From eea8cb3bbe6379755410dffb774bd14e389612c2 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 04 二月 2026 13:54:26 +0800
Subject: [PATCH] 应急预案查阅模块开发
---
src/api/safeProduction/emergencyPlanReview.js | 38 +
src/pages/index.vue | 9
src/pages.json | 21
src/pages/safeProduction/emergencyPlanReview/view.vue | 372 ++++++++++++++
src/pages/safeProduction/emergencyPlanReview/index.vue | 373 ++++++++++++++
src/pages/safeProduction/emergencyPlanReview/detail.vue | 724 +++++++++++++++++++++++++++
6 files changed, 1,537 insertions(+), 0 deletions(-)
diff --git a/src/api/safeProduction/emergencyPlanReview.js b/src/api/safeProduction/emergencyPlanReview.js
new file mode 100644
index 0000000..5634bb7
--- /dev/null
+++ b/src/api/safeProduction/emergencyPlanReview.js
@@ -0,0 +1,38 @@
+// 搴旀�ラ妗堝鏍搁〉闈㈡帴鍙�
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function safeContingencyPlanListPage(query) {
+ return request({
+ url: "/safeContingencyPlan/page",
+ method: "get",
+ params: query,
+ });
+}
+
+// 鏂板搴旀�ラ妗�
+export function safeContingencyPlanAdd(query) {
+ return request({
+ url: '/safeContingencyPlan',
+ method: 'post',
+ data: query
+ })
+}
+
+// 淇敼搴旀�ラ妗�
+export function safeContingencyPlanUpdate(query) {
+ return request({
+ url: '/safeContingencyPlan',
+ method: 'put',
+ data: query
+ })
+}
+
+// 鍒犻櫎搴旀�ラ妗�
+export function safeContingencyPlanDel(ids) {
+ return request({
+ url: '/safeContingencyPlan/' + ids,
+ method: 'delete',
+ data: ids
+ })
+}
diff --git a/src/pages.json b/src/pages.json
index c3210ff..5588424 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -772,6 +772,27 @@
"navigationBarTitleText": "鍗遍櫓鐗╂枡璇︽儏",
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/safeProduction/emergencyPlanReview/index",
+ "style": {
+ "navigationBarTitleText": "搴旀�ラ妗堝鏍�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/emergencyPlanReview/detail",
+ "style": {
+ "navigationBarTitleText": "搴旀�ラ妗堣鎯�",
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/safeProduction/emergencyPlanReview/view",
+ "style": {
+ "navigationBarTitleText": "搴旀�ラ妗堣鎯�",
+ "navigationStyle": "custom"
+ }
}
],
"subPackages": [
diff --git a/src/pages/index.vue b/src/pages/index.vue
index 2d6bf1c..36c285a 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -323,6 +323,10 @@
icon: "/static/images/icon/guzhangfenxi@2x.png",
label: "鍗遍櫓鐗╂枡",
},
+ {
+ icon: "/static/images/icon/guzhangfenxi@2x.png",
+ label: "搴旀�ラ妗�",
+ },
]);
// 鍗忓悓鍔炲叕鍔熻兘鏁版嵁
const collaborationItems = reactive([
@@ -706,6 +710,11 @@
url: "/pages/safeProduction/hazardousMaterialsControl/index",
});
break;
+ case "搴旀�ラ妗�":
+ uni.navigateTo({
+ url: "/pages/safeProduction/emergencyPlanReview/index",
+ });
+ break;
default:
uni.showToast({
title: `鐐瑰嚮浜�${item.label}`,
diff --git a/src/pages/safeProduction/emergencyPlanReview/detail.vue b/src/pages/safeProduction/emergencyPlanReview/detail.vue
new file mode 100644
index 0000000..6117254
--- /dev/null
+++ b/src/pages/safeProduction/emergencyPlanReview/detail.vue
@@ -0,0 +1,724 @@
+<template>
+ <view class="emergency-plan-detail">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader :title="isEdit ? '缂栬緫搴旀�ラ妗�' : '鏂板搴旀�ラ妗�'"
+ @back="goBack" />
+ <!-- 琛ㄥ崟鍖哄煙 -->
+ <u-form :model="form"
+ label-width="110"
+ :rules="rules"
+ ref="formRef">
+ <!-- 搴旀�ラ妗堢紪鐮� -->
+ <u-form-item label="搴旀�ラ妗堢紪鐮�"
+ border-bottom
+ required
+ prop="planCode">
+ <up-input v-model="form.planCode"
+ placeholder="璇疯緭鍏ュ簲鎬ラ妗堢紪鐮�"
+ clearable />
+ </u-form-item>
+ <!-- 搴旀�ラ妗堝悕绉� -->
+ <u-form-item label="搴旀�ラ妗堝悕绉�"
+ required
+ border-bottom
+ prop="planName">
+ <up-input v-model="form.planName"
+ placeholder="璇疯緭鍏ュ簲鎬ラ妗堝悕绉�"
+ clearable />
+ </u-form-item>
+ <!-- 鍙戝竷鐢熸晥鏃堕棿 -->
+ <u-form-item label="鍙戝竷鐢熸晥鏃堕棿"
+ required
+ border-bottom
+ prop="publishTime">
+ <up-input v-model="form.publishTime"
+ placeholder="璇烽�夋嫨鍙戝竷鐢熸晥鏃堕棿"
+ readonly
+ @click="showTime = true" />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showTime = true"></up-icon>
+ </template>
+ </u-form-item>
+ <!-- 棰勬绫诲瀷 -->
+ <u-form-item label="棰勬绫诲瀷"
+ prop="planType"
+ required
+ border-bottom>
+ <u-input v-model="emergencyPlanTypeLabel"
+ placeholder="璇烽�夋嫨棰勬绫诲瀷"
+ @click="showPlanTypeActionSheet = true"
+ readonly />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showPlanTypeActionSheet = true"></up-icon>
+ </template>
+ </u-form-item>
+ <u-form-item label="鏍稿績璐d换浜�"
+ prop="coreResponsorUserId"
+ required
+ border-bottom>
+ <u-input v-model="form.coreResponsorUserName"
+ placeholder="璇烽�夋嫨鏍稿績璐d换浜�"
+ @click="showUserActionSheet = true"
+ readonly />
+ <template #right>
+ <up-icon name="arrow-right"
+ @click="showUserActionSheet = true"></up-icon>
+ </template>
+ </u-form-item>
+ <!-- 澶囨敞 -->
+ <u-form-item label="澶囨敞"
+ prop="remark">
+ <up-input v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉�"
+ type="textarea"
+ rows="3"
+ clearable />
+ </u-form-item>
+ <!-- 閫傜敤鑼冨洿 -->
+ <u-form-item label="閫傜敤鑼冨洿"
+ required
+ prop="applyScope">
+ <view class="checkbox-group">
+ <u-checkbox-group v-model="form.applyScope"
+ @change="handleApplyScopeChange">
+ <u-checkbox shape="circle"
+ size="32rpx"
+ class="checkbox-item"
+ v-for="(item, index) in applyScopeOptions"
+ :key="index"
+ :label="item.label"
+ :name="item.value">
+ </u-checkbox>
+ </u-checkbox-group>
+ </view>
+ </u-form-item>
+ <!-- 搴旀�ュ缃楠� -->
+ <view class="exec-steps-container">
+ <view class="steps-header">
+ <text class="steps-title">澶勭疆姝ラ鍒楄〃</text>
+ <text class="steps-count">鍏� {{ execStepsList.length }} 涓楠�</text>
+ </view>
+ <view class="steps-list">
+ <view v-for="(step, index) in execStepsList"
+ :key="index"
+ class="exec-step-item">
+ <view class="delete-btn"
+ @click="removeExecStep(index)">
+ <u-icon name="close"
+ color="#fff"
+ size="16" />
+ </view>
+ <view class="step-number">
+ {{ index + 1 }}
+ </view>
+ <view class="step-content">
+ <view class="step-row">
+ <text class="step-label">姝ラ鍚嶇О锛�</text>
+ <u-textarea v-model="step.step"
+ placeholder="璇疯緭鍏ユ楠ゅ悕绉�"
+ clearable
+ border-bottom
+ class="step-input" />
+ </view>
+ <view class="step-row">
+ <text class="step-label">澶勭疆鎺柦锛�</text>
+ <u-textarea v-model="step.description"
+ placeholder="璇疯緭鍏ュ叿浣撶殑搴旀�ュ缃帾鏂�"
+ type="textarea"
+ clearable
+ class="step-textarea" />
+ </view>
+ </view>
+ </view>
+ </view>
+ <u-button type="primary"
+ @click="addExecStep"
+ class="add-step-btn">
+ <text>娣诲姞姝ラ</text>
+ </u-button>
+ </view>
+ </u-form>
+ <!-- 鍙戝竷鐢熸晥鏃堕棿閫夋嫨鍣� -->
+ <up-datetime-picker :show="showTime"
+ v-model="currentTime"
+ @confirm="handleDateConfirm"
+ @cancel="showTime = false"
+ mode="date" />
+ <!--棰勬绫诲瀷閫夋嫨鍣� -->
+ <up-action-sheet :show="showPlanTypeActionSheet"
+ :actions="emergencyPlanTypeOptions"
+ @select="handlePlanTypeConfirm"
+ title="閫夋嫨棰勬绫诲瀷" />
+ <!--鏍稿績璐d换浜洪�夋嫨鍣� -->
+ <up-action-sheet :show="showUserActionSheet"
+ :actions="userList"
+ @select="handleUserConfirm"
+ title="閫夋嫨鏍稿績璐d换浜�" />
+ </view>
+ <!-- 搴曢儴鎸夐挳 -->
+ <view class="bottom-buttons">
+ <u-button type="default"
+ size="default"
+ @click="goBack"
+ class="bottom-btn">
+ 鍙栨秷
+ </u-button>
+ <u-button type="primary"
+ size="default"
+ @click="submitForm"
+ class="bottom-btn">
+ 淇濆瓨
+ </u-button>
+ </view>
+</template>
+
+<script setup>
+ import { ref, onMounted, computed } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ safeContingencyPlanAdd,
+ safeContingencyPlanUpdate,
+ } from "@/api/safeProduction/emergencyPlanReview";
+ import { userListNoPage } from "@/api/system/user";
+ import { useDict } from "@/utils/dict";
+ import dayjs from "dayjs";
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "emergency-plan-detail" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ // 琛ㄥ崟寮曠敤
+ const formRef = ref();
+
+ // 琛ㄥ崟鏁版嵁
+ const form = ref({
+ id: "",
+ planCode: "",
+ planName: "",
+ publishTime: "",
+ planType: "",
+ coreResponsorUserId: "",
+ coreResponsorUserName: "",
+ remark: "",
+ applyScope: [],
+ execSteps: "",
+ });
+
+ // 搴旀�ュ缃楠ゅ垪琛�
+ const execStepsList = ref([]);
+
+ // 鏃ユ湡鑼冨洿
+ const minDate = new Date("2000-01-01");
+ const maxDate = new Date("2030-12-31");
+ const currentTime = ref(Date.now());
+ // 鐢ㄦ埛鍒楄〃
+ const userList = ref([]);
+
+ // 搴旀�ラ妗堢被鍨嬮�夐」
+ const { emergency_plan_type } = useDict("emergency_plan_type");
+ const emergencyPlanTypeOptions = computed(() => {
+ return (
+ emergency_plan_type?.value.map(item => ({
+ value: item.value,
+ name: item.label,
+ })) || []
+ );
+ });
+
+ // 搴旀�ラ妗堢被鍨嬫爣绛�
+ const emergencyPlanTypeLabel = ref("");
+
+ // 閫傜敤鑼冨洿閫夐」
+ const applyScopeOptions = [
+ { value: "all", label: "鍏ㄤ綋鍛樺伐" },
+ { value: "manager", label: "绠$悊灞�" },
+ { value: "hr", label: "浜轰簨閮ㄩ棬" },
+ { value: "finance", label: "璐㈠姟閮ㄩ棬" },
+ { value: "tech", label: "鎶�鏈儴闂�" },
+ ];
+
+ // 鏄惁涓虹紪杈戞ā寮�
+ const isEdit = ref(false);
+
+ // ActionSheet 鏄剧ず鐘舵��
+ const showPlanTypeActionSheet = ref(false);
+ const showUserActionSheet = ref(false);
+ const showTime = ref(false);
+
+ // 鍒濆鍖栨暟鎹�
+ const initData = () => {
+ const emergencyPlan = uni.getStorageSync("emergencyPlan") || {};
+ if (emergencyPlan.id) {
+ // 缂栬緫妯″紡
+ isEdit.value = true;
+ form.value = {
+ id: emergencyPlan.id,
+ planCode: emergencyPlan.planCode || "",
+ planName: emergencyPlan.planName || "",
+ publishTime: emergencyPlan.publishTime || "",
+ planType: emergencyPlan.planType || "",
+ coreResponsorUserId: emergencyPlan.coreResponsorUserId || "",
+ coreResponsorUserName: emergencyPlan.coreResponsorUserName || "",
+ remark: emergencyPlan.remark || "",
+ applyScope: emergencyPlan.applyScope
+ ? emergencyPlan.applyScope.split(",")
+ : [],
+ execSteps: emergencyPlan.execSteps || "",
+ };
+ currentTime.value = new Date(emergencyPlan.publishTime).getTime();
+ // 璁剧疆棰勬绫诲瀷鏍囩
+ const planTypeItem = emergencyPlanTypeOptions.value.find(
+ item => item.value === emergencyPlan.planType
+ );
+ emergencyPlanTypeLabel.value = planTypeItem ? planTypeItem.name : "";
+ console.log(form.value.applyScope, form.value.applyScope);
+ // 鍒濆鍖栧簲鎬ュ缃楠�
+ initExecSteps(emergencyPlan.execSteps);
+ } else {
+ // 鏂板妯″紡
+ isEdit.value = false;
+ form.value = {
+ planCode: "",
+ planName: "",
+ publishTime: new Date().toISOString().split("T")[0],
+ planType: "",
+ coreResponsorUserId: "",
+ coreResponsorUserName: "",
+ remark: "",
+ applyScope: [],
+ execSteps: "",
+ };
+ emergencyPlanTypeLabel.value = "";
+ execStepsList.value = [];
+ addExecStep();
+ }
+ };
+ const handleApplyScopeChange = e => {
+ // form.value.applyScope = e;
+ console.log(e, "e");
+ console.log(form.value.applyScope, "form.value.applyScope");
+ };
+
+ // 鍒濆鍖栧簲鎬ュ缃楠�
+ const initExecSteps = execSteps => {
+ if (execSteps) {
+ try {
+ execStepsList.value = JSON.parse(execSteps);
+ } catch (e) {
+ execStepsList.value = [];
+ }
+ } else {
+ execStepsList.value = [];
+ }
+ if (execStepsList.value.length === 0) {
+ addExecStep();
+ }
+ };
+
+ // 娣诲姞搴旀�ュ缃楠�
+ const addExecStep = () => {
+ const stepNumber = execStepsList.value.length + 1;
+ execStepsList.value.push({
+ step: `姝ラ${stepNumber}`,
+ description: "",
+ });
+ };
+
+ // 鍒犻櫎搴旀�ュ缃楠�
+ const removeExecStep = index => {
+ if (execStepsList.value.length > 1) {
+ execStepsList.value.splice(index, 1);
+ } else {
+ showToast("鑷冲皯淇濈暀涓�涓楠�");
+ }
+ };
+
+ // 鑾峰彇鐢ㄦ埛鍒楄〃
+ const getUserList = () => {
+ userListNoPage()
+ .then(res => {
+ userList.value = res.data.map(item => ({
+ value: item.userId,
+ name: item.nickName,
+ }));
+ })
+ .catch(() => {
+ showToast("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触");
+ });
+ };
+
+ // 鏃ユ湡閫夋嫨纭
+ const handleDateConfirm = e => {
+ form.value.publishTime = dayjs(e.value).format("YYYY-MM-DD");
+ showTime.value = false;
+ };
+
+ // 棰勬绫诲瀷閫夋嫨纭
+ const handlePlanTypeConfirm = e => {
+ form.value.planType = e.value;
+ const selectedType = emergencyPlanTypeOptions.value.find(
+ item => item.value === e.value
+ );
+ if (selectedType) {
+ emergencyPlanTypeLabel.value = selectedType.name;
+ }
+ showPlanTypeActionSheet.value = false;
+ };
+
+ // 鐢ㄦ埛閫夋嫨纭
+ const handleUserConfirm = e => {
+ form.value.coreResponsorUserId = e.value;
+ const selectedUser = userList.value.find(user => user.value === e.value);
+ if (selectedUser) {
+ form.value.coreResponsorUserName = selectedUser.name;
+ }
+ showUserActionSheet.value = false;
+ };
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ // 琛ㄥ崟楠岃瘉瑙勫垯
+ const rules = {
+ planCode: [
+ {
+ required: true,
+ message: "璇疯緭鍏ュ簲鎬ラ妗堢紪鐮�",
+ trigger: ["submit", "blur"],
+ },
+ ],
+ planName: [
+ {
+ required: true,
+ message: "璇疯緭鍏ュ簲鎬ラ妗堝悕绉�",
+ trigger: ["submit", "blur"],
+ },
+ ],
+ publishTime: [
+ {
+ required: true,
+ message: "璇烽�夋嫨鍙戝竷鐢熸晥鏃堕棿",
+ trigger: ["submit", "change"],
+ },
+ ],
+ planType: [
+ {
+ required: true,
+ message: "璇烽�夋嫨棰勬绫诲瀷",
+ trigger: ["submit", "change"],
+ },
+ ],
+ coreResponsorUserId: [
+ {
+ required: true,
+ message: "璇烽�夋嫨鏍稿績璐d换浜�",
+ trigger: ["submit", "change"],
+ },
+ ],
+ applyScope: [
+ {
+ required: true,
+ message: "璇烽�夋嫨閫傜敤鑼冨洿",
+ trigger: ["submit", "change"],
+ },
+ ],
+ };
+
+ // 鎻愪氦琛ㄥ崟
+ const submitForm = async () => {
+ // 楠岃瘉琛ㄥ崟蹇呭~椤�
+ if (!formRef.value) return;
+
+ const valid = await formRef.value.validate();
+ if (!valid) {
+ return;
+ }
+
+ // 楠岃瘉搴旀�ュ缃楠�
+ for (let i = 0; i < execStepsList.value.length; i++) {
+ const step = execStepsList.value[i];
+ if (!step.step || !step.step.trim()) {
+ showToast(`绗�${i + 1}鏉℃楠ょ殑"姝ラ"涓嶈兘涓虹┖`);
+ return;
+ }
+ if (!step.description || !step.description.trim()) {
+ showToast(`绗�${i + 1}鏉℃楠ょ殑"鎺柦"涓嶈兘涓虹┖`);
+ return;
+ }
+ }
+
+ // 灏嗗簲鎬ュ缃楠よ浆鎹负JSON瀛楃涓�
+ form.value.execSteps = JSON.stringify(execStepsList.value);
+
+ // 澶勭悊閫傜敤鑼冨洿
+ form.value.applyScope = form.value.applyScope.join(",");
+
+ showLoadingToast("淇濆瓨涓�...");
+
+ try {
+ if (isEdit.value) {
+ // 缂栬緫妯″紡
+ const res = await safeContingencyPlanUpdate(form.value);
+ if (res.code === 200) {
+ showToast("鏇存柊鎴愬姛");
+ setTimeout(() => {
+ uni.navigateBack();
+ }, 1000);
+ } else {
+ showToast(res.msg || "鏇存柊澶辫触");
+ }
+ } else {
+ // 鏂板妯″紡
+ const res = await safeContingencyPlanAdd(form.value);
+ if (res.code === 200) {
+ showToast("娣诲姞鎴愬姛");
+ setTimeout(() => {
+ uni.navigateBack();
+ }, 1000);
+ } else {
+ showToast(res.msg || "娣诲姞澶辫触");
+ }
+ }
+ } catch (error) {
+ console.error("鎻愪氦澶辫触:", error);
+ showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
+ } finally {
+ closeToast();
+ }
+ };
+
+ // 鏄剧ず鍔犺浇鎻愮ず
+ const showLoadingToast = message => {
+ uni.showLoading({
+ title: message,
+ mask: true,
+ });
+ };
+
+ // 鍏抽棴鎻愮ず
+ const closeToast = () => {
+ uni.hideLoading();
+ };
+
+ onMounted(() => {
+ initData();
+ getUserList();
+ });
+
+ onShow(() => {
+ initData();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "@/static/scss/form-common.scss";
+ .emergency-plan-detail {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 100px;
+ }
+
+ .form-section {
+ }
+
+ .checkbox-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px;
+ }
+
+ .checkbox-item {
+ margin-right: 16px;
+ }
+
+ .select-container {
+ position: relative;
+ width: 100%;
+ }
+
+ .select-container .up-input {
+ width: 100%;
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ padding: 12px 16px;
+ background-color: #ffffff;
+ }
+
+ .exec-steps-container {
+ padding: 20px;
+ background-color: #fff;
+ }
+
+ .steps-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ padding-bottom: 12px;
+ border-bottom: 1px solid #e4e7ed;
+ }
+
+ .steps-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .steps-count {
+ font-size: 14px;
+ color: #909399;
+ }
+
+ .steps-list {
+ margin-bottom: 20px;
+ }
+
+ .exec-step-item {
+ position: relative;
+ display: flex;
+ margin-bottom: 16px;
+ padding: 16px;
+ background-color: #ffffff;
+ border: 1px solid #e4e7ed;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+ }
+
+ .exec-step-item:hover {
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+ border-color: #409eff;
+ transform: translateY(-1px);
+ }
+
+ .delete-btn {
+ position: absolute;
+ top: -25rpx;
+ right: -25rpx;
+ width: 50rpx;
+ height: 50rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ font-size: 20px;
+ border-radius: 50%;
+ background-color: red;
+ border: none;
+ z-index: 10;
+ }
+
+ .delete-btn:hover {
+ transform: scale(1.1);
+ box-shadow: 0 3px 6px rgba(245, 108, 108, 0.4);
+ }
+
+ .step-number {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ margin-right: 16px;
+ background-color: #ecf5ff;
+ color: #409eff;
+ font-size: 14px;
+ font-weight: 600;
+ border-radius: 50%;
+ flex-shrink: 0;
+ }
+
+ .step-content {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .step-row {
+ display: flex;
+ align-items: flex-start;
+ margin-bottom: 12px;
+ }
+
+ .step-row:last-child {
+ margin-bottom: 0;
+ }
+
+ .step-label {
+ display: inline-block;
+ width: 80px;
+ font-size: 14px;
+ color: #606266;
+ margin-right: 12px;
+ flex-shrink: 0;
+ line-height: 36px;
+ }
+
+ .step-input {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .step-input input {
+ font-size: 14px;
+ color: #303133;
+ }
+
+ .step-textarea {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .step-textarea textarea {
+ font-size: 14px;
+ color: #303133;
+ min-height: 80px;
+ line-height: 1.5;
+ }
+
+ .add-step-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 44px;
+ line-height: 44px;
+ font-size: 14px;
+ border-radius: 8px;
+ transition: all 0.3s ease;
+ gap: 8px;
+ }
+
+ .add-step-btn:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
+ }
+
+ .add-step-btn text {
+ font-size: 14px;
+ }
+
+ .bottom-buttons {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ padding: 16px 20px;
+ background: #ffffff;
+ border-top: 1px solid #f0f0f0;
+ gap: 16px;
+ }
+
+ .bottom-btn {
+ flex: 1;
+ }
+</style>
diff --git a/src/pages/safeProduction/emergencyPlanReview/index.vue b/src/pages/safeProduction/emergencyPlanReview/index.vue
new file mode 100644
index 0000000..f575002
--- /dev/null
+++ b/src/pages/safeProduction/emergencyPlanReview/index.vue
@@ -0,0 +1,373 @@
+<template>
+ <view class="emergency-plan-review">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="搴旀�ラ妗堝鏍�"
+ @back="goBack" />
+ <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+ <view class="search-section">
+ <view class="search-bar">
+ <view class="search-input">
+ <up-input class="search-text"
+ placeholder="璇疯緭鍏ュ簲鎬ラ妗堝悕绉�"
+ v-model="searchForm.planName"
+ @change="searchChange"
+ clearable />
+ </view>
+ <view class="filter-button"
+ @click="getList">
+ <u-icon name="search"
+ size="24"
+ color="#999"></u-icon>
+ </view>
+ </view>
+ </view>
+ <!-- 搴旀�ラ妗堝垪琛� -->
+ <view class="ledger-list"
+ v-if="planList.length > 0">
+ <view v-for="(item, index) in planList"
+ :key="index">
+ <view class="ledger-item">
+ <view class="item-header">
+ <view class="item-left">
+ <view class="document-icon">
+ <up-icon name="file-text"
+ size="16"
+ color="#ffffff"></up-icon>
+ </view>
+ <text class="item-title">{{ item.planName }}</text>
+ </view>
+ </view>
+ <up-divider></up-divider>
+ <view class="item-details"
+ @click="viewDetail(item)">
+ <view class="detail-row">
+ <text class="detail-label">棰勬缂栫爜</text>
+ <text class="detail-value">{{ item.planCode || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">棰勬绫诲瀷</text>
+ <u-tag :type="getPlanTypeTagType(item.planType)">
+ {{ emergencyPlanTypeLabel(item.planType) }}
+ </u-tag>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鍙戝竷鐢熸晥鏃堕棿</text>
+ <text class="detail-value">{{ item.publishTime || '-' }}</text>
+ </view>
+ <view class="detail-row">
+ <text class="detail-label">鏍稿績璐d换浜�</text>
+ <text class="detail-value">{{ item.coreResponsorUserName || '-' }}</text>
+ </view>
+ <view class="detail-row"
+ v-if="item.remark">
+ <text class="detail-label">澶囨敞</text>
+ <text class="detail-value">{{ item.remark }}</text>
+ </view>
+ </view>
+ <!-- 鎸夐挳鍖哄煙 -->
+ <view class="action-buttons">
+ <u-button type="primary"
+ size="small"
+ class="action-btn"
+ @click="editPlan(item)">
+ 缂栬緫
+ </u-button>
+ <u-button type="info"
+ size="small"
+ class="action-btn"
+ @click="viewDetail(item)">
+ 鏌ョ湅璇︽儏
+ </u-button>
+ <u-button type="error"
+ size="small"
+ class="action-btn"
+ @click="deletePlan(item)">
+ 鍒犻櫎
+ </u-button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <view v-else
+ class="no-data">
+ <text>鏆傛棤搴旀�ラ妗�</text>
+ </view>
+ <!-- 娴姩鏂板鎸夐挳 -->
+ <view class="fab-button"
+ @click="addPlan">
+ <up-icon name="plus"
+ size="24"
+ color="#ffffff"></up-icon>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, onMounted, computed } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import {
+ safeContingencyPlanListPage,
+ safeContingencyPlanDel,
+ } from "@/api/safeProduction/emergencyPlanReview";
+ import { useDict } from "@/utils/dict";
+ import useUserStore from "@/store/modules/user";
+
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "emergency-plan-review-index" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ const userStore = useUserStore();
+
+ // 鎼滅储琛ㄥ崟
+ const searchForm = ref({
+ planName: "",
+ });
+
+ // 搴旀�ラ妗堟暟鎹�
+ const planList = ref([]);
+
+ // 搴旀�ラ妗堢被鍨嬮�夐」
+ const { emergency_plan_type } = useDict("emergency_plan_type");
+ const emergencyPlanTypeOptions = computed(
+ () => emergency_plan_type?.value || []
+ );
+
+ // 鑾峰彇棰勬绫诲瀷鏍囩绫诲瀷
+ const getPlanTypeTagType = planType => {
+ const typeMap = {
+ emergency: "warning",
+ fire: "danger",
+ natural: "info",
+ accident: "error",
+ };
+ return typeMap[planType] || "info";
+ };
+
+ // 鑾峰彇棰勬绫诲瀷鏍囩鏂囨湰
+ const emergencyPlanTypeLabel = val => {
+ const item = emergencyPlanTypeOptions.value.find(
+ i => String(i.value) === String(val)
+ );
+ return item ? item.label : val;
+ };
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+ const searchChange = val => {
+ searchForm.value.planName = val;
+ getList();
+ };
+
+ // 鏌ヨ鍒楄〃
+ const getList = () => {
+ showLoadingToast("鍔犺浇涓�...");
+ const params = {
+ current: -1,
+ size: -1,
+ ...searchForm.value,
+ };
+ safeContingencyPlanListPage(params)
+ .then(res => {
+ planList.value = res.records || res.data?.records || [];
+ closeToast();
+ })
+ .catch(() => {
+ closeToast();
+ showToast("鑾峰彇鏁版嵁澶辫触");
+ });
+ };
+
+ // 鏄剧ず鍔犺浇鎻愮ず
+ const showLoadingToast = message => {
+ uni.showLoading({
+ title: message,
+ mask: true,
+ });
+ };
+
+ // 鍏抽棴鎻愮ず
+ const closeToast = () => {
+ uni.hideLoading();
+ };
+
+ // 鏂板搴旀�ラ妗�
+ const addPlan = () => {
+ uni.setStorageSync("emergencyPlan", {});
+ uni.navigateTo({
+ url: "/pages/safeProduction/emergencyPlanReview/detail",
+ });
+ };
+
+ // 缂栬緫搴旀�ラ妗�
+ const editPlan = item => {
+ uni.setStorageSync("emergencyPlan", item);
+ uni.navigateTo({
+ url: "/pages/safeProduction/emergencyPlanReview/detail",
+ });
+ };
+
+ // 鍒犻櫎搴旀�ラ妗�
+ const deletePlan = item => {
+ uni.showModal({
+ title: "鍒犻櫎纭",
+ content: `纭畾瑕佸垹闄よ搴旀�ラ妗堝悧锛焋,
+ success: res => {
+ if (res.confirm) {
+ deleteEmergencyPlan(item.id);
+ }
+ },
+ });
+ };
+
+ // 鍒犻櫎搴旀�ラ妗堣褰�
+ const deleteEmergencyPlan = id => {
+ showLoadingToast("鍒犻櫎涓�...");
+ safeContingencyPlanDel([id])
+ .then(() => {
+ closeToast();
+ showToast("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ closeToast();
+ showToast("鍒犻櫎澶辫触");
+ });
+ };
+
+ // 鏌ョ湅璇︽儏
+ const viewDetail = item => {
+ uni.setStorageSync("emergencyPlan", item);
+ uni.navigateTo({
+ url: "/pages/safeProduction/emergencyPlanReview/view",
+ });
+ };
+
+ onMounted(() => {
+ getList();
+ });
+
+ onShow(() => {
+ getList();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "../../../styles/sales-common.scss";
+
+ // 椤甸潰鐗瑰畾鐨勬牱寮忚鐩�
+ .emergency-plan-review {
+ min-height: 100vh;
+ background: #f8f9fa;
+ position: relative;
+ }
+
+ // 鐗瑰畾鐨勫浘鏍囨牱寮�
+ .document-icon {
+ background: #2979ff; // 涓庨攢鍞ā鍧椾繚鎸佷竴鑷寸殑鑳屾櫙鑹�
+ }
+
+ // 鐗规湁鏍峰紡
+ .detail-value {
+ word-break: break-all; // 淇濈暀椤甸潰鐗规湁鐨勬枃鏈崲琛屾牱寮�
+ }
+
+ // 鐗瑰畾鐨勬诞鍔ㄦ寜閽牱寮�
+ .fab-button {
+ background: #2979ff; // 涓庨攢鍞ā鍧椾繚鎸佷竴鑷寸殑鑳屾櫙鑹�
+ box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3); // 涓庨攢鍞ā鍧椾繚鎸佷竴鑷寸殑闃村奖鏁堟灉
+ }
+
+ // 鎿嶄綔鎸夐挳鏍峰紡
+ .action-buttons {
+ display: flex;
+ gap: 12px;
+ padding: 0 0 16px 0;
+ justify-content: space-between;
+ }
+
+ .action-btn {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ }
+
+ // 鍒楄〃瀹瑰櫒鏍峰紡
+ .plan-list {
+ padding: 20px;
+ }
+
+ // 鍒楄〃椤规牱寮�
+ .plan-item {
+ background: #ffffff;
+ border-radius: 12px;
+ margin-bottom: 16px;
+ overflow: hidden;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ padding: 0 16px;
+
+ &:active {
+ transform: scale(0.98);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
+ }
+ }
+
+ // 椤圭洰澶撮儴鏍峰紡
+ .item-header {
+ padding: 16px 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+
+ .item-left {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+
+ .item-title {
+ font-size: 14px;
+ color: #333;
+ font-weight: 500;
+ }
+
+ // 璇︽儏鍖哄煙鏍峰紡
+ .item-details {
+ padding: 16px 0;
+ }
+
+ .detail-row {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ margin-bottom: 8px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ .detail-label {
+ font-size: 12px;
+ color: #777777;
+ min-width: 60px;
+ }
+
+ .detail-value {
+ font-size: 12px;
+ color: #000000;
+ text-align: right;
+ flex: 1;
+ margin-left: 16px;
+ }
+</style>
diff --git a/src/pages/safeProduction/emergencyPlanReview/view.vue b/src/pages/safeProduction/emergencyPlanReview/view.vue
new file mode 100644
index 0000000..45a8124
--- /dev/null
+++ b/src/pages/safeProduction/emergencyPlanReview/view.vue
@@ -0,0 +1,372 @@
+<template>
+ <view class="emergency-plan-view">
+ <!-- 浣跨敤閫氱敤椤甸潰澶撮儴缁勪欢 -->
+ <PageHeader title="搴旀�ラ妗堣鎯�"
+ @back="goBack" />
+ <!-- 璇︽儏鍐呭 -->
+ <view class="detail-content"
+ v-if="currentPlan">
+ <!-- 鍩烘湰淇℃伅 -->
+ <view class="info-section">
+ <!-- <view class="section-title">
+ <text class="title-text">{{ currentPlan.planName }}</text>
+ </view> -->
+ <view class="info-grid">
+ <view class="info-item">
+ <text class="info-label">搴旀�ラ妗堢紪鐮�</text>
+ <text class="info-value">{{ currentPlan.planCode || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">搴旀�ラ妗堝悕绉�</text>
+ <text class="info-value">{{ currentPlan.planName || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">棰勬绫诲瀷</text>
+ <u-tag :type="getPlanTypeTagType(currentPlan.planType)">
+ {{ emergencyPlanTypeLabel(currentPlan.planType) }}
+ </u-tag>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鍙戝竷鐢熸晥鏃堕棿</text>
+ <text class="info-value">{{ currentPlan.publishTime || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">鏍稿績璐d换浜�</text>
+ <text class="info-value">{{ currentPlan.coreResponsorUserName || '-' }}</text>
+ </view>
+ <view class="info-item">
+ <text class="info-label">澶囨敞</text>
+ <text class="info-value">{{ currentPlan.remark || '-' }}</text>
+ </view>
+ </view>
+ </view>
+ <!-- 閫傜敤鑼冨洿 -->
+ <view class="scope-section">
+ <view class="section-header">
+ <text class="section-heading">閫傜敤鑼冨洿</text>
+ </view>
+ <view class="scope-tags">
+ <u-tag v-for="(scope, index) in applyScopeList"
+ :key="index"
+ type="primary"
+ size="small"
+ class="scope-tag">
+ {{ scope }}
+ </u-tag>
+ </view>
+ </view>
+ <!-- 搴旀�ュ缃楠� -->
+ <view class="steps-section">
+ <view class="section-header">
+ <text class="section-heading">搴旀�ュ缃楠�</text>
+ </view>
+ <view class="steps-list"
+ v-if="execStepsList.length > 0">
+ <view v-for="(step, index) in execStepsList"
+ :key="index"
+ class="step-item">
+ <view class="step-number">{{ index + 1 }}</view>
+ <view class="step-content">
+ <text class="step-title">{{ step.step }}</text>
+ <text class="step-description">{{ step.description }}</text>
+ </view>
+ </view>
+ </view>
+ <view class="no-steps"
+ v-else>
+ <text>鏆傛棤搴旀�ュ缃楠�</text>
+ </view>
+ </view>
+ </view>
+ <!-- 绌虹姸鎬� -->
+ <view class="empty-state"
+ v-else>
+ <text>鏆傛棤搴旀�ラ妗堜俊鎭�</text>
+ </view>
+ </view>
+</template>
+
+<script setup>
+ import { ref, onMounted, computed } from "vue";
+ import { onShow } from "@dcloudio/uni-app";
+ import PageHeader from "@/components/PageHeader.vue";
+ import { useDict } from "@/utils/dict";
+
+ // 鏇挎崲 toast 鏂规硶
+ defineOptions({ name: "emergency-plan-view" });
+ const showToast = message => {
+ uni.showToast({
+ title: message,
+ icon: "none",
+ });
+ };
+
+ // 褰撳墠搴旀�ラ妗�
+ const currentPlan = ref(null);
+
+ // 搴旀�ュ缃楠ゅ垪琛�
+ const execStepsList = ref([]);
+
+ // 閫傜敤鑼冨洿鍒楄〃
+ const applyScopeList = ref([]);
+
+ // 搴旀�ラ妗堢被鍨嬮�夐」
+ const { emergency_plan_type } = useDict("emergency_plan_type");
+ const emergencyPlanTypeOptions = computed(
+ () => emergency_plan_type?.value || []
+ );
+
+ // 鑾峰彇棰勬绫诲瀷鏍囩绫诲瀷
+ const getPlanTypeTagType = planType => {
+ const typeMap = {
+ emergency: "warning",
+ fire: "danger",
+ natural: "info",
+ accident: "error",
+ };
+ return typeMap[planType] || "info";
+ };
+
+ // 鑾峰彇棰勬绫诲瀷鏍囩鏂囨湰
+ const emergencyPlanTypeLabel = val => {
+ const item = emergencyPlanTypeOptions.value.find(
+ i => String(i.value) === String(val)
+ );
+ return item ? item.label : val;
+ };
+
+ // 鍒濆鍖栨暟鎹�
+ const initData = () => {
+ const emergencyPlan = uni.getStorageSync("emergencyPlan") || {};
+ if (emergencyPlan.id) {
+ currentPlan.value = emergencyPlan;
+
+ // 澶勭悊閫傜敤鑼冨洿
+ if (emergencyPlan.applyScope) {
+ const scopes = emergencyPlan.applyScope.split(",");
+ applyScopeList.value = scopes.map(scope => {
+ const scopeMap = {
+ all: "鍏ㄤ綋鍛樺伐",
+ manager: "绠$悊灞�",
+ hr: "浜轰簨閮ㄩ棬",
+ finance: "璐㈠姟閮ㄩ棬",
+ tech: "鎶�鏈儴闂�",
+ };
+ return scopeMap[scope] || scope;
+ });
+ } else {
+ applyScopeList.value = [];
+ }
+
+ // 澶勭悊搴旀�ュ缃楠�
+ if (emergencyPlan.execSteps) {
+ try {
+ execStepsList.value = JSON.parse(emergencyPlan.execSteps);
+ } catch (e) {
+ execStepsList.value = [];
+ }
+ } else {
+ execStepsList.value = [];
+ }
+ } else {
+ currentPlan.value = null;
+ applyScopeList.value = [];
+ execStepsList.value = [];
+ showToast("鏈壘鍒板簲鎬ラ妗堜俊鎭�");
+ }
+ };
+
+ // 杩斿洖涓婁竴椤�
+ const goBack = () => {
+ uni.navigateBack();
+ };
+
+ onMounted(() => {
+ initData();
+ });
+
+ onShow(() => {
+ initData();
+ });
+</script>
+
+<style scoped lang="scss">
+ @import "../../../styles/sales-common.scss";
+
+ .emergency-plan-view {
+ min-height: 100vh;
+ background: #f8f9fa;
+ padding-bottom: 32px;
+ }
+
+ .detail-content {
+ padding: 20px;
+ }
+
+ // 淇℃伅 section
+ .info-section {
+ background: #ffffff;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ }
+
+ .section-title {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 24px;
+ }
+
+ .title-text {
+ font-size: 18px;
+ font-weight: 600;
+ color: #303133;
+ flex: 1;
+ }
+
+ .type-tag {
+ margin-left: 16px;
+ }
+
+ .info-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 20px;
+ }
+
+ .info-item {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .info-label {
+ font-size: 14px;
+ color: #909399;
+ }
+
+ .info-value {
+ font-size: 14px;
+ color: #303133;
+ word-break: break-all;
+ }
+
+ // 閫傜敤鑼冨洿 section
+ .scope-section {
+ background: #ffffff;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ }
+
+ .section-header {
+ margin-bottom: 16px;
+ }
+
+ .section-heading {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ border-left: 4px solid #2979ff;
+ padding-left: 16px;
+ }
+
+ .scope-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ }
+
+ .scope-tag {
+ margin-bottom: 8px;
+ }
+
+ // 搴旀�ュ缃楠� section
+ .steps-section {
+ background: #ffffff;
+ border-radius: 12px;
+ padding: 24px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+ }
+
+ .steps-list {
+ margin-top: 16px;
+ }
+
+ .step-item {
+ display: flex;
+ margin-bottom: 24px;
+ position: relative;
+ }
+
+ .step-item::before {
+ content: "";
+ position: absolute;
+ left: 15px;
+ top: 40px;
+ bottom: -24px;
+ width: 2px;
+ background-color: #eaeaea;
+ }
+
+ .step-item:last-child::before {
+ display: none;
+ }
+
+ .step-number {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background-color: #2979ff;
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 16px;
+ flex-shrink: 0;
+ margin-top: 4px;
+ }
+
+ .step-content {
+ flex: 1;
+ }
+
+ .step-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ display: block;
+ margin-bottom: 8px;
+ }
+
+ .step-description {
+ font-size: 14px;
+ color: #606266;
+ line-height: 1.5;
+ word-break: break-all;
+ }
+
+ .no-steps {
+ padding: 40px;
+ text-align: center;
+ color: #909399;
+ font-size: 14px;
+ background-color: #f8f9fa;
+ border-radius: 8px;
+ }
+
+ // 绌虹姸鎬�
+ .empty-state {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 60vh;
+ font-size: 16px;
+ color: #909399;
+ }
+</style>
--
Gitblit v1.9.3