From df64c34d92cbe8501bbbfe837bc491a47452c0b6 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期一, 09 六月 2025 10:58:19 +0800
Subject: [PATCH] feat(eims): 新增保养工单批量修改功能并优化相关领域对象

---
 eims-ui-mobile/src/pages/maint/maint-order.vue |  467 +++++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 328 insertions(+), 139 deletions(-)

diff --git a/eims-ui-mobile/src/pages/maint/maint-order.vue b/eims-ui-mobile/src/pages/maint/maint-order.vue
index 7f2da57..88398ec 100644
--- a/eims-ui-mobile/src/pages/maint/maint-order.vue
+++ b/eims-ui-mobile/src/pages/maint/maint-order.vue
@@ -39,31 +39,31 @@
           <view class="flex justify-between">
             <view class="flex items-center menu-title-box">
               <view class="menu-indicator"></view>
-              <view class="ml-1 text-sm align-center">{{ maintSt.equName }}</view>
-              <view class="text-color-gray ml-2 text-mini">{{ maintSt.assetNo }}</view>
+              <view class="ml-1 text-lg align-center">{{ maintSt.equName }}</view>
+              <view class="text-color-gray ml-2 text-sm">{{ maintSt.assetNo }}</view>
             </view>
 
             <view class="flex items-center">
-              <text class="text-color-gray text-mini">{{ maintSt.planTime }}</text>
+              <text class="text-color-gray text-sm">{{ maintSt.planTime }}</text>
             </view>
           </view>
         </template>
         <view class="flex h-[140rpx]" items-center>
           <image class="slot-img text-center" src="/static/images/camera.png" />
           <view class="flex-1">
-            <view class="text-color-gray text-xs mt-1 flex">
+            <view class="text-color-gray text-sm mt-1 flex">
               <text class="mr-3">宸ュ崟鎬绘暟: {{ maintSt.orderCount }}</text>
               |
               <text class="mx-3">宸插畬鎴�: {{ maintSt.wcCount }}</text>
             </view>
-            <view class="text-color-gray text-xs mt-2 flex">
+            <view class="text-color-gray text-sm mt-2 flex">
               <text class="mr-3">寰呬繚鍏�: {{ maintSt.dbyCount }}</text>
               |
               <text class="mx-3">淇濆吇涓�: {{ maintSt.byCount }}</text>
               |
               <text class="ml-3">寰呴獙璇�: {{ maintSt.dyzCount }}</text>
             </view>
-            <view class="text-color-gray text-xs mt-2 flex">
+            <view class="text-color-gray text-sm mt-2 flex">
               <text>鐘舵�侊細</text>
               <template v-if="maintSt.status === '1'">
                 <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
@@ -75,7 +75,7 @@
               </template>
             </view>
 
-            <view class="text-color-gray text-xs mt-2 flex">
+            <view class="text-color-gray text-sm mt-2 flex">
               鍒涘缓鏃堕棿: {{ maintSt.createTime }}
             </view>
           </view>
@@ -87,145 +87,118 @@
       <view class="w-full h-[24rpx]"></view>
       <wd-cell class="mb-[2px]">
         <template #title>
-          <text class="text-color-gray">淇濆吇椤�</text>
+          <text class="text-color-gray text-sm">淇濆吇椤�</text>
         </template>
       </wd-cell>
 
-      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id">
+      <wd-card type="rectangle" v-for="(item, index) in dataList" :key="item.id" :class="['status-' + item.maintFun]">
         <template #title>
-          <view class="flex justify-between">
-            <view class="flex items-center menu-title-box">
-              <view class="menu-indicator"></view>
-              <view class="ml-1 text-sm align-center">
-                <wd-text :text="item.maintName" :lines="1"></wd-text>
-              </view>
+          <view class="flex items-center">
+            <view class="menu-indicator"></view>
+            <view class="ml-1 text-sm align-center">
+              <wd-text color="black" :text="item.maintName"></wd-text>
             </view>
-
-            <view class="flex items-center w-[20%] justify-end">
-              <text class="text-color-gray text-mini">
-                {{ item?.planTime }}
-              </text>
+            <!-- 鏂板鐘舵�佹樉绀猴紝缁戝畾鐐瑰嚮浜嬩欢 -->
+            <view
+              v-if="item.status === '2'"
+              class="ml-auto text-sm"
+              style="width: 60px; text-align: end"
+              :style="{ color: getStatusColor(item.maintFun) }"
+              @click.stop="handleUndoAction(item)"
+            >
+              {{ getStatusText(item.maintFun) }}
             </view>
           </view>
         </template>
-        <view class="flex h-[200rpx]" items-center>
-          <image class="slot-img text-center" src="/static/ico/ico-platform.png" />
-          <view class="flex-1 text-color-gray text-xs flex-row">
-            <view class="mr-3 mt-2">淇濆吇鍗曞彿: {{ item.maintCode }}</view>
-            <view class="mr-3 mt-2">璁″垝淇濆吇鏃ユ湡: {{ item.planTime }}</view>
-            <view class="mr-3 mt-2">淇濆吇寮�濮嬫椂闂�: {{ item.startTime }}</view>
-            <view class="mr-3 mt-2">淇濆吇缁撴潫鏃堕棿: {{ item.endTime }}</view>
-            <view class="text-color-gray text-xs mt-2 flex">
-              <text>鐘舵�侊細</text>
-              <template v-if="item.status === '0'">
-                <wd-icon class="icon-color-warning" name="books" size="34rpx"></wd-icon>
-                <text class="ml-1">寰呬繚鍏�</text>
-              </template>
-              <template v-else-if="item.status === '1'">
-                <wd-icon class="icon-color-base" name="books" size="34rpx"></wd-icon>
-                <text class="ml-1">淇濆吇涓�</text>
-              </template>
-              <template v-else-if="item.status === '2'">
-                <wd-icon class="icon-color-purple" name="books" size="34rpx"></wd-icon>
-                <text class="ml-1">寰呴獙璇�</text>
-              </template>
-              <template v-else-if="item.status === '3'">
-                <wd-icon class="icon-color-success" name="check-outline" size="34rpx"></wd-icon>
-                <text class="ml-1">宸插畬鎴�</text>
-              </template>
-              <text class="mx-3">|</text>
-              <wd-icon class="icon-color-base" name="camera" size="30rpx"></wd-icon>
-              <text class="ml-1">{{ item.maintUserName }}</text>
+
+        <!-- 鎸夐挳鍖哄煙 -->
+        <view v-if="item.maintFun == null" class="flex justify-around mt-2">
+          <wd-button
+            type="primary"
+            size="small"
+            class="mr-2"
+            @click.stop="handleAction(item, '0')"
+          >
+            妫�鏌�
+          </wd-button>
+          <wd-button
+            type="success"
+            size="small"
+            class="mr-2"
+            @click.stop="handleAction(item, '1')"
+          >
+            淇濆吇
+          </wd-button>
+          <wd-button type="warning" size="small" @click.stop="handleAction(item, '2')">
+            缁翠慨
+          </wd-button>
+        </view>
+
+        <!-- 姝f枃鍖哄煙 -->
+        <view v-else class="mt-2">
+          <!-- 淇濆吇璇存槑鍖哄煙 -->
+          <view v-if="item.maintFun === '1'" class="mt-2">
+            <wd-input
+              v-model="item.maintDesc"
+              placeholder="璇疯緭鍏ヤ繚鍏昏鏄�"
+              clearable
+              :maxlength="200"
+            />
+          </view>
+
+          <!-- 缁翠慨璇存槑鍖哄煙 -->
+          <view v-if="item.maintFun === '2'" class="mt-2">
+            <wd-input
+              v-model="item.repairDesc"
+              placeholder="璇疯緭鍏ョ淮淇鏄�"
+              clearable
+              :maxlength="200"
+            />
+          </view>
+
+
+          <!-- 澶囦欢淇℃伅褰曞叆鍖哄煙 -->
+          <view v-if="item.spareParts && item.spareParts.length > 0" class="mt-2">
+            <view
+              v-for="(part, partIndex) in item.spareParts"
+              :key="partIndex"
+              class="flex justify-between mt-1"
+            >
+              <wd-input
+                v-model="part.name"
+                label="鍚嶇О"
+                label-width="100rpx"
+                placeholder="澶囦欢鍚嶇О"
+
+
+              />
+              <wd-input
+                v-model="part.quantity"
+                label="鏁伴噺" label-width="100rpx"
+                placeholder="鏁伴噺" type="number" :maxlength="5" />
             </view>
           </view>
-          <!--鎿嶄綔宸ユ垨缁翠慨宸ヨ鑹�-->
-          <template v-if="isOperatorOrRepair()">
-            <view class="flex flex-col justify-between"  v-if="item.status === '0'">
-              <wd-button
-                size="small"
-                icon="edit-outline"
-                @click.stop="handleStartMaint(item)"
-              >
-                寮�濮嬩繚鍏�
-              </wd-button>
+          <!-- 缁翠慨璇存槑鍖哄煙 -->
+          <view v-if="item.maintFun === '2'" class="mt-2">
+            <wd-button type="info" size="small" @click.stop="addSparePart(item, index)">
+              娣诲姞澶囦欢
+            </wd-button>
+          </view>
 
-              <wd-button
-                class="mt-3"
-                size="small"
-                icon="edit-outline"
-                @click.stop="handleMaintFinish(item)"
-              >
-                涓�閿繚鍏�
-              </wd-button>
-            </view>
-            <wd-button
-              v-if="item.status === '1'"
-              size="small"
-              icon="edit-outline"
-              @click.stop="itemClick(item)"
-            >
-              淇濆吇涓�
-            </wd-button>
 
-            <wd-button
-              v-if="item.status === '2'"
-              size="small"
-              icon="edit-outline"
-              @click.stop="itemClick(item)"
-            >
-              寰呴獙璇�
-            </wd-button>
-
-            <wd-button
-              v-if="item.status === '3'"
-              size="small"
-              icon="check-outline"
-              @click.stop="itemClick(item)"
-            >
-              宸插畬鎴�
-            </wd-button>
-          </template>
-
-          <!--绠$悊鍛樿鑹�-->
-          <template v-else-if="isLeader()">
-            <wd-button v-if="item.status === '0'" size="small" icon="warn-bold" disabled>
-              寰呬繚鍏�
-            </wd-button>
-            <wd-button v-if="item.status === '1'" size="small" icon="warn-bold" disabled>
-              淇濆吇涓�
-            </wd-button>
-
-            <wd-button
-              v-if="item.status === '2'"
-              size="small"
-              icon="edit-outline"
-              @click.stop="itemClick(item)"
-            >
-              寰呴獙璇�
-            </wd-button>
-
-            <wd-button
-              v-if="item.status === '3'"
-              size="small"
-              icon="check-outline"
-              @click.stop="itemClick(item)"
-            >
-              宸插畬鎴�
-            </wd-button>
-          </template>
+          <!-- 淇濆吇浜哄拰淇濆吇鏃堕棿鍖哄煙 -->
+          <view class="flex justify-between mt-2">
+            <text>淇濆吇浜�: {{ item.maintUserName }}</text>
+            <text>淇濆吇鏃堕棿: {{ item.endTime }}</text>
+          </view>
         </view>
       </wd-card>
-      <wd-cell>
-        <template #title>
-          <text class="text-color-gray">鍏朵粬淇℃伅</text>
-        </template>
-      </wd-cell>
-      <view class="h-[2px] w-full bg-base"></view>
+
       <wd-textarea
         label="鐗硅浜嬮」"
         label-width="200rpx"
         type="textarea"
-        v-model="maintSt.specialNote"
+        v-model="specialNote"
         auto-height
         :maxlength="200"
         show-word-limit
@@ -233,22 +206,63 @@
         clearable
       />
     </view>
+    <!-- 鏂板鎻愪氦鎸夐挳 -->
+    <view class="flex justify-center mt-4">
+      <wd-button type="primary" block size="large" @click="handleClickRight">鎻愪氦</wd-button>
+    </view>
   </z-paging>
+
+  <!-- 澶囦欢閫夋嫨寮瑰嚭灞� -->
+  <wd-popup v-model="showSparePopup" position="bottom" height="33.33vh">
+    <view class="flex justify-between p-2 bg-white">
+      <wd-button type="text" @click="closeSparePopup">鍙栨秷</wd-button>
+      <wd-button type="text" @click="addOtherSparePart">鍏朵粬</wd-button>
+
+    </view>
+    <wd-input
+      v-model="searchKeyword"
+      placeholder="璇疯緭鍏ュ浠跺悕绉版垨鍨嬪彿"
+      clearable
+      @input="filterSpareParts"
+    />
+    <view class="p-2">
+      <view
+        v-for="(part, index) in sparePartsList"
+        :key="index"
+        class="flex justify-between items-center p-2 border-b"
+        @click="selectFilteredSparePart(part)"
+      >
+        <text>{{ part.name }} ({{ part.code }})</text>
+        <text>鍓╀綑: {{ part.actualStock }}</text>
+      </view>
+    </view>
+  </wd-popup>
+
+
 </template>
+
 <script setup lang="ts">
 import type { MaintStVO } from '@/service/maint.d'
-import { getMaintSt, getMaintStOrderList, updateMaintOrder, updateMaintSt } from '@/service/maint'
+import { getMaintSt, getMaintStOrderList, updateMaintOrder, updateMaintSt, updateMaintOrderBatch } from '@/service/maint'
 import { ref, reactive } from 'vue'
 import { useToast, useMessage } from 'wot-design-uni'
 import { isLeader, isOperatorOrRepair } from '@/utils/RoleUtils'
 import { formatDate } from '@/utils/DateUtils'
+import { useUserStore } from "@/store";
+import { getSpareList } from '@/service/spare'
+
 const message = useMessage()
 const toast = useToast()
-
+const userStore = useUserStore()
 const paging = ref(null)
 const dataList = ref([])
 const maintStId = ref('')
-
+const showSparePopup = ref(false)
+const selectedPartIndex = ref(-1)
+const sparePartsList = ref([])
+const searchKeyword = ref('')
+const dataChange = ref(false)
+const specialNote = ref('')
 interface QueryParams {
   pageNum: number
   pageSize: number
@@ -272,7 +286,7 @@
 const queryList = (pageNum?: number, pageSize?: number) => {
   const params: QueryParams = {
     pageNum,
-    pageSize,
+    pageSize: 30,
     maintCode: maintCode.value,
   }
   getMaintStOrderList(params)
@@ -307,6 +321,65 @@
   uni.navigateTo({
     url: `/pages/maint/order-detail?id=${item.id}`,
   })
+}
+
+// 澶囦欢閫夋嫨鐩稿叧閫昏緫
+function addSparePart(item: any, index: number) {
+  if (!item.spareParts) {
+    item.spareParts = []
+  }
+  selectedPartIndex.value = index
+  // item.spareParts.push({ name: '', quantity: '' })
+  selectSparePart(item)
+}
+
+function selectSparePart(item: any) {
+  showSparePopup.value = true
+
+  loadSpareParts()
+}
+
+function loadSpareParts(value?: string) {
+  getSpareList({ name: value }).then((res: any) => {
+    sparePartsList.value = res.rows
+
+  })
+}
+
+function filterSpareParts() {
+  if (!searchKeyword.value) {
+    loadSpareParts()
+  } else {
+    loadSpareParts(searchKeyword.value)
+  }
+}
+
+function selectFilteredSparePart(part: any) {
+  dataList.value[selectedPartIndex.value].spareParts.push({
+    id: part.id,
+    name: part.name,
+    quantity: '',
+  })
+  closeSparePopup()
+}
+
+function closeSparePopup() {
+  showSparePopup.value = false
+  selectedPartIndex.value = -1
+  searchKeyword.value = ''
+}
+
+function confirmSpareSelection() {
+  closeSparePopup()
+}
+
+function addOtherSparePart() {
+  dataList.value[selectedPartIndex.value].spareParts.push({
+    id:  '',
+    name: '',
+    quantity: '',
+  })
+  closeSparePopup();
 }
 
 /**
@@ -380,19 +453,36 @@
 }
 
 function handleUpdateMaintSt() {
-  if (maintSt.orderCount !== maintSt.wcCount) {
-    toast.info('璇峰厛瀹屾垚鎵�鏈変繚鍏婚」锛�')
+  // if (maintSt.orderCount !== maintSt.wcCount) {
+  //   toast.info('璇峰厛瀹屾垚鎵�鏈変繚鍏婚」锛�')
+  //   return false
+  // }
+  // 鍏堝垽鏂繚鍏婚」鏄惁鏈夋洿鏀�
+  console.log('handleUPdateMaintst', dataChange.value)
+  if (!dataChange.value) {
+    message.alert('璇锋搷浣滃悗鎻愪氦!')
     return false
   }
+  // 杩囨护鎺� maintFun涓虹┖鐨勪繚鍏婚」
+  const submitList = dataList.value.filter((item) => item.maintFun != null)
+  // 濡傛灉maintFun涓�1鐨勫垯鍒ゆ柇淇濆吇璇存槑涓嶈兘涓虹┖锛屽鏋滀负2鍒欏垽鏂淮淇鏄庝笉鑳戒负绌�
+  if (submitList.some((item) => item.maintFun === '1' && !item.maintDesc)) {
+    toast.info('璇峰~鍐欎繚鍏昏鏄庯紒')
+    return false
+  } else if (submitList.some((item) => item.maintFun === '2' && !item.repairDesc)) {
+    toast.info('璇峰~鍐欑淮淇鏄庯紒')
+    return false
+  }
+
 
   const data: any = Object.assign(
     {},
     {
       id: maintSt.id,
       orderCount: maintSt.orderCount,
-      wcCount: maintSt.wcCount,
+      wcCount: submitList.length,
       status: maintSt.status,
-      specialNote: maintSt.specialNote,
+      specialNote: specialNote.value,
     },
   )
   if (data.orderCount === data.wcCount) {
@@ -403,6 +493,7 @@
       msg: '纭畾鏇存柊宸ュ崟姹囨�绘暟鎹紵',
       title: '鎻愮ず',
       beforeConfirm: ({ resolve }) => {
+        updateMaintOrderBatch({maintOrderList:submitList})
         updateMaintSt(data)
           .then((res: any) => {
             resolve(true)
@@ -457,6 +548,90 @@
   maintStId.value = options.id
   reloadData()
 })
+watch(
+  () => [...dataList.value, specialNote.value ], // 浣跨敤鎵╁睍杩愮畻绗﹀垱寤烘柊鏁扮粍浠ヨЕ鍙戠洃鍚�
+  (newVal, oldVal) => {
+    if (oldVal.length > 0) {
+      console.log('dataChange',oldVal,  newVal)
+      dataChange.value = true
+    }
+  },
+  { deep: true },
+)
+/**
+ * 澶勭悊鎸夐挳鐐瑰嚮浜嬩欢
+ * @param item 淇濆吇椤�
+ * @param action 鎿嶄綔绫诲瀷锛坈heck/check/maintain/repair锛�
+ */
+function handleAction(item: any, action: number) {
+  // 璁剧疆褰撳墠閫変腑鐨勬搷浣滅被鍨�
+  item.maintFun = action
+  item.status = '2'
+  // 鑷姩濉厖淇濆吇浜哄拰鏃堕棿
+  item.maintUserName = userStore?.userInfo?.realName || ''
+  const now = new Date()
+  item.endTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
+  console.log('handleAction', item)
+}
+
+/**
+ * 鎾ら攢淇濆吇鎿嶄綔
+ * @param item 淇濆吇椤�
+ */
+function handleUndoAction(item: any) {
+  message
+    .confirm({
+      msg: '纭畾鎾ら攢褰撳墠鎿嶄綔锛�',
+      title: '鎻愮ず',
+      beforeConfirm: ({ resolve }) => {
+        // 閲嶇疆鐘舵�佸拰鐩稿叧瀛楁
+        item.maintFun = null;
+        item.status = '0';
+        item.maintDesc = '';
+        item.repairDesc = '';
+        item.spareParts = [];
+        item.maintUserName = '';
+        item.endTime = '';
+        resolve(true);
+      },
+    })
+    .then(() => {
+      toast.success('鎿嶄綔宸叉挙閿�');
+      dataChange.value = true;
+    })
+    .catch((error) => {
+      console.log(error);
+    });
+}
+
+// 鏂板鏂规硶锛氳幏鍙栫姸鎬佹枃鏈�
+function getStatusText(maintFun: string): string {
+  switch (maintFun) {
+    case '0':
+      return '宸叉鏌�';
+    case '1':
+      return '宸蹭繚鍏�';
+    case '2':
+      return '宸茬淮淇�';
+    default:
+      return '';
+  }
+}
+
+// 鏂板鏂规硶锛氳幏鍙栫姸鎬侀鑹�
+function getStatusColor(maintFun: string): string {
+  switch (maintFun) {
+    case '0':
+      return '#007bff'; // 妫�鏌ユ寜閽鑹�
+    case '1':
+      return '#28a745'; // 淇濆吇鎸夐挳棰滆壊
+    case '2':
+      return '#ffc107'; // 缁翠慨鎸夐挳棰滆壊
+    default:
+      return '#000';
+  }
+}
+
 </script>
 
 <style scoped lang="scss">
@@ -472,7 +647,7 @@
 }
 
 .text-mini {
-  font-size: 22rpx;
+  font-size: 24rpx;
 }
 
 .menu-indicator {
@@ -494,7 +669,7 @@
 }
 
 :deep(.wd-navbar__text) {
-  font-size: 26rpx;
+  font-size: 28rpx;
   color: white;
 }
 
@@ -502,6 +677,20 @@
 :deep(.wd-navbar__title) {
   color: white;
   font-weight: 0;
-  font-size: 32rpx;
+  font-size: 34rpx;
+}
+
+// 鏂板鏍峰紡锛氬姩鎬佽缃崱鐗囪儗鏅壊
+:deep(.wd-card) {
+  transition: background-color 0.3s ease;
+  &.status-0 {
+    background-color: #e6f7ff; // 妫�鏌ョ姸鎬佽儗鏅壊
+  }
+  &.status-1 {
+    background-color: #f6ffed; // 淇濆吇鐘舵�佽儗鏅壊
+  }
+  &.status-2 {
+    background-color: #fffbe6; // 缁翠慨鐘舵�佽儗鏅壊
+  }
 }
 </style>

--
Gitblit v1.9.3