| src/api/safeProduction/emergencyPlanReview.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/safeProduction/emergencyPlanReview/detail.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/safeProduction/emergencyPlanReview/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/pages/safeProduction/emergencyPlanReview/view.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
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 }) } 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": [ 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}`, 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="æ ¸å¿è´£ä»»äºº" prop="coreResponsorUserId" required border-bottom> <u-input v-model="form.coreResponsorUserName" placeholder="è¯·éæ©æ ¸å¿è´£ä»»äºº" @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="鿩颿¡ç±»å" /> <!--æ ¸å¿è´£ä»»äººéæ©å¨ --> <up-action-sheet :show="showUserActionSheet" :actions="userList" @select="handleUserConfirm" title="éæ©æ ¸å¿è´£ä»»äºº" /> </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: "è¯·éæ©æ ¸å¿è´£ä»»äºº", 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> 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">æ ¸å¿è´£ä»»äºº</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> 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">æ ¸å¿è´£ä»»äºº</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>