Quellcode durchsuchen

修复检验方案审核审批按钮显示有误的问题;增加检验结束时间填写逻辑

yangguanjin vor 4 Tagen
Ursprung
Commit
8efd097591

+ 4 - 0
src/api/boiler/boilerTaskOrder.ts

@@ -110,3 +110,7 @@ export const updateBoilerOrderItemChecker = (data: any) => {
 export const batchSuspendBoilerOrderItem = (data: any) => {
   return httpPost('/pressure2/boiler-task-order-item-suspend/suspend', data)
 }
+
+export const saveBoilerItemEndCheckDate = (data: any) => {
+  return httpPost('/pressure2/boiler-task-order/item/endCheckDate', data)
+}

+ 5 - 1
src/api/pipe/pipeTaskOrder.ts

@@ -114,4 +114,8 @@ export const getPipeDetailByOrderItemId = (params: any) => {
 
 export const getInputIdByEquipId = (params: any) => {
   return httpGet('/pressure2/pipe-task-order/equipToInputId', params)
-}
+}
+
+export const savePipeInputEndCheckDate = (data: any) => {
+  return httpPost('/pressure2/pipe-task-order/item/endCheckDate', data)
+}

+ 221 - 0
src/components/EndDatePrompt/EndDatePrompt.vue

@@ -0,0 +1,221 @@
+<template>
+  <wd-popup
+    transition="zoom-in"
+    :model-value="visible"
+    :close-on-click-modal="closeOnClickModal"
+    :duration="200"
+    custom-class="end-date-prompt"
+    @update:model-value="onPopupUpdate"
+    @close="onClose"
+  >
+    <view class="end-date-prompt__container">
+      <view class="end-date-prompt__body">
+        <view v-if="title" class="end-date-prompt__title">{{ title }}</view>
+        <view class="end-date-prompt__content">
+          <view v-if="description" class="end-date-prompt__desc">{{ description }}</view>
+          <wd-datetime-picker-view
+            v-model="selectedValue"
+            :type="type"
+            :min-date="minDate"
+            :max-date="maxDate"
+            :use-second="useSecond"
+          />
+        </view>
+      </view>
+      <view
+        class="end-date-prompt__actions"
+        :class="{ 'end-date-prompt__flex': showCancelButton }"
+      >
+        <wd-button
+          v-if="showCancelButton"
+          block
+          type="info"
+          :disabled="loading"
+          custom-class="end-date-prompt__actions-btn"
+          @click="handleCancel"
+        >
+          {{ cancelButtonText }}
+        </wd-button>
+        <wd-button
+          block
+          :loading="loading"
+          :disabled="loading"
+          custom-class="end-date-prompt__actions-btn"
+          @click="handleConfirm"
+        >
+          {{ confirmButtonText }}
+        </wd-button>
+      </view>
+    </view>
+  </wd-popup>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+import dayjs from 'dayjs'
+
+type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime' | 'year'
+
+interface Props {
+  /** 是否显示弹框,支持 v-model:visible */
+  visible: boolean
+  /** 标题 */
+  title?: string
+  /** 日期选择器上方的描述文案 */
+  description?: string
+  /** 初始选中的值,date/datetime 类型为时间戳,time 类型为字符串 */
+  defaultValue?: number | string
+  /** 选择器类型:date / year-month / time / datetime / year */
+  type?: DateTimeType
+  /** 最小日期(时间戳) */
+  minDate?: number
+  /** 最大日期(时间戳) */
+  maxDate?: number
+  /** 是否展示秒,仅在 time/datetime 类型下生效 */
+  useSecond?: boolean
+  /** 是否展示取消按钮 */
+  showCancelButton?: boolean
+  /** 确定按钮文案 */
+  confirmButtonText?: string
+  /** 取消按钮文案 */
+  cancelButtonText?: string
+  /** 是否允许点击蒙层关闭 */
+  closeOnClickModal?: boolean
+  /** 加载中状态。为 true 时确认按钮显示加载态,且确认/取消按钮均禁用,由父组件在请求后端期间控制 */
+  loading?: boolean
+  /** 返回值格式化模板(dayjs 格式,如 'YYYY-MM-DD HH:mm:ss')。不传则返回原始时间戳(date/datetime)或字符串(time) */
+  formatter?: string
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  title: '选择结束日期',
+  description: '',
+  defaultValue: 0,
+  type: 'date',
+  minDate: () => new Date(new Date().getFullYear() - 10, 0, 1).getTime(),
+  maxDate: () => new Date(new Date().getFullYear() + 10, 11, 31).getTime(),
+  useSecond: false,
+  showCancelButton: true,
+  confirmButtonText: '确定',
+  cancelButtonText: '取消',
+  closeOnClickModal: false,
+  loading: false,
+  formatter: '',
+})
+
+const emit = defineEmits<{
+  'update:visible': [value: boolean]
+  /** 确认回调。未传 formatter 时返回原始时间戳(date/datetime)或字符串(time);传入 formatter 则返回按模板格式化后的字符串 */
+  confirm: [value: any]
+  /** 取消回调 */
+  cancel: []
+}>()
+
+const selectedValue = ref<number | string>(getInitialValue())
+
+function getInitialValue() {
+  if (props.defaultValue) return props.defaultValue
+  return props.type === 'time' ? '' : Date.now()
+}
+
+// 每次打开时重置为初始值,保证回显正确
+watch(
+  () => props.visible,
+  (val) => {
+    if (val) {
+      selectedValue.value = getInitialValue()
+    }
+  },
+)
+
+function onPopupUpdate(val: boolean) {
+  emit('update:visible', val)
+}
+
+function onClose() {
+  emit('update:visible', false)
+}
+
+function handleConfirm() {
+  if (props.loading) return
+  const raw = selectedValue.value
+  const result = props.formatter ? formatValue(raw) : raw
+  emit('confirm', result)
+}
+
+function formatValue(raw: number | string): string {
+  if (raw === '' || raw == null) return ''
+  return props.type === 'time' ? String(raw) : dayjs(Number(raw)).format(props.formatter)
+}
+
+function handleCancel() {
+  emit('cancel')
+  emit('update:visible', false)
+}
+
+/** 暴露给父组件按需格式化使用 */
+defineExpose({
+  selectedValue,
+  /** 获取格式化后的字符串 */
+  getFormatted: (format = 'YYYY-MM-DD HH:mm:ss') => {
+    const val = selectedValue.value
+    if (!val) return ''
+    return props.type === 'time' ? String(val) : dayjs(Number(val)).format(format)
+  },
+})
+</script>
+
+<style lang="scss" scoped>
+:deep(.end-date-prompt) {
+  border-radius: 16px;
+  overflow: hidden;
+}
+
+.end-date-prompt__container {
+  width: 300px;
+  box-sizing: border-box;
+}
+
+.end-date-prompt__body {
+  padding: 25px 24px 0;
+  background-color: #fff;
+}
+
+.end-date-prompt__title {
+  text-align: center;
+  font-size: 16px;
+  color: rgba(0, 0, 0, 0.85);
+  line-height: 20px;
+  font-weight: 500;
+  padding-top: 5px;
+  padding-bottom: 10px;
+}
+
+.end-date-prompt__content {
+  max-height: 264px;
+  overflow: auto;
+  text-align: center;
+}
+
+.end-date-prompt__desc {
+  font-size: 14px;
+  color: #666;
+  line-height: 20px;
+  padding: 8px 0 12px;
+}
+
+.end-date-prompt__actions {
+  padding: 24px;
+  background-color: #fff;
+
+  &.end-date-prompt__flex {
+    display: flex;
+  }
+}
+
+:deep(.end-date-prompt__actions-btn) {
+  &:not(:last-child) {
+    margin-right: 16px;
+  }
+}
+</style>

+ 2 - 1
src/pages/inspectionPlanAudit/components/Item.vue

@@ -88,7 +88,8 @@ const checkDate = computed(() => {
 })
 
 const isAuditStage = computed(() => {
-  return props.item.approvalId == null ? 1 : 0
+  // 第二个是回退再重新提交的情况
+  return props.item.approvalId == null || props.item.currentNode?.includes('主任') ? 1 : 0
 })
 
 const statusDict = computed(() => {

+ 2 - 1
src/pages/inspectionPlanAudit/list/InspectionPlanAuditList.vue

@@ -144,7 +144,8 @@ const handleRightRefresh = (value: string | number | boolean) => {
 
 // 跳转操作
 const pushAction = (item: any) => {
-  const isAuditStage = item.approvalId == null ? 1 : 0
+  // 第二个是回退再重新提交的情况
+  const isAuditStage = item.approvalId == null || item.currentNode?.includes('主任') ? 1 : 0
   uni.navigateTo({
     url: `/pages/inspectionPlanAudit/preViewReport/index?id=${item.id}&reportName=${item.reportName}&templateId=${item.templateId}&isAuditStage=${isAuditStage}&manualUrl=${encodeURIComponent(item.manualUrl)}`,
   })

+ 49 - 1
src/pages/taskOnline/TaskOnlineEquipmentList.vue

@@ -294,6 +294,14 @@
         />
       </view>
     </view>
+
+    <EndDatePrompt
+      v-model:visible="showEndDatePrompt"
+      :loading="endDateLoading"
+      title="选择结束检验时间"
+      formatter="YYYY-MM-DD"
+      @confirm="handleEndDateConfirm"
+    />
   </view>
 </template>
 
@@ -312,10 +320,12 @@ import {
   getBoilerTaskItemListByOrderId,
   addInspectProject,
   confirmBoilerEquipmentBatchClaim,
+  saveBoilerItemEndCheckDate,
 } from '@/api/boiler/boilerTaskOrder'
 import { updateEquipBoilerSecurityManager } from '@/api/boiler/boilerEquip'
 import NavBar from '@/components/NavBar/NavBar.vue'
 import BoilerCheckProject from '@/pages/taskOnline/components/BoilerCheckProject.vue'
+import EndDatePrompt from '@/components/EndDatePrompt/EndDatePrompt.vue'
 
 interface PopupData {
   text: string
@@ -386,6 +396,10 @@ const selectTemplates = ref<Record<string, any[]>>({})
 const currentSelectedCheckProjectItems = ref<any[]>([])
 const equipDataForPopup = ref<any>({})
 
+const showEndDatePrompt = ref(false)
+const endDatePromptContext = ref<{ item: any; pageType: string }>({ item: {}, pageType: '' })
+const endDateLoading = ref(false)
+
 const currentSafeManager = computed(() => {
   return {
     name: currentItem.value?.safery || '',
@@ -819,12 +833,46 @@ const handleUpdateSafetyManagerConfirm = async (params: { name: string; phone: s
   }
 }
 
-const handleRouteToEquipmentDetail = (item: any, pageType: string) => {
+const navigateToEquipmentDetail = (item: any, pageType: string) => {
   uni.navigateTo({
     url: `/pages/equipment/detail/equipmentDetail?orderId=${orderId.value}&orderItemId=${item.mainID}&equipId=${item.equipId}&pageType=${pageType}&useOnline=1&canEdit=${true}`,
   })
 }
 
+const handleRouteToEquipmentDetail = (item: any, pageType: string) => {
+  if (
+    pageType === 'INSPECT' &&
+    (item.endCheckDate == null || item.endCheckDate == '')
+  ) {
+    // 记录录入时,结束检验时间为空则弹出日期选择器供用户填写
+    endDatePromptContext.value = { item, pageType }
+    showEndDatePrompt.value = true
+    return
+  }
+  navigateToEquipmentDetail(item, pageType)
+}
+
+const handleEndDateConfirm = async (value: string) => {
+  const { item, pageType } = endDatePromptContext.value
+  endDateLoading.value = true
+  try {
+    const res = await saveBoilerItemEndCheckDate({ id: item.mainID, endCheckDate: value })
+    if (res?.code !== 0) {
+      uni.showToast({ title: res?.msg || '保存结束检验时间失败', icon: 'none' })
+      return
+    }
+
+    // 保存成功后更新本地数据并跳转页面
+    item.endCheckDate = value
+    showEndDatePrompt.value = false
+    navigateToEquipmentDetail(item, pageType)
+  } catch (error) {
+    uni.showToast({ title: '保存失败', icon: 'none' })
+  } finally {
+    endDateLoading.value = false
+  }
+}
+
 const handleCalcTotalFee = (reportDOList: any) => {
   if (!reportDOList || !Array.isArray(reportDOList)) return 0
   return reportDOList

+ 72 - 10
src/pages/taskOnline/TaskOnlinePipeEquipmentList.vue

@@ -113,7 +113,7 @@
               </view>
               <view
                 class="child-pipe-info"
-                @click="handleRouteToEquipmentDetail(childPipe, 'EQUIPMENT')"
+                @click="handleRouteToEquipmentDetail(pipeSet, 'EQUIPMENT')"
               >
                 <view class="info-box">
                   <text class="info-label">
@@ -308,6 +308,14 @@
         />
       </view>
     </view>
+
+    <EndDatePrompt
+      v-model:visible="showEndDatePrompt"
+      :loading="endDateLoading"
+      title="选择结束检验时间"
+      formatter="YYYY-MM-DD"
+      @confirm="handleEndDateConfirm"
+    />
   </view>
 </template>
 
@@ -332,10 +340,12 @@ import {
   getPipeTaskItemListByOrderId,
   addInspectProject,
   getInputIdByEquipId,
+  savePipeInputEndCheckDate,
 } from '@/api/pipe/pipeTaskOrder'
 import { updateEquipPipeSafetyManager } from '@/api/pipe/pipeEquip'
 import NavBar from '@/components/NavBar/NavBar.vue'
 import PipeCheckProject from '@/pages/taskOnline/components/PipeCheckProject.vue'
+import EndDatePrompt from '@/components/EndDatePrompt/EndDatePrompt.vue'
 
 interface PopupData {
   text: string
@@ -411,6 +421,10 @@ const currentSelectedItems = ref<any[]>([])
 const equipDataForPopup = ref<any>({})
 const mainCheckerUser = ref<any>({})
 
+const showEndDatePrompt = ref(false)
+const endDatePromptContext = ref<{ item: any; pageType: string }>({ item: {}, pageType: '' })
+const endDateLoading = ref(false)
+
 const currentSafeManager = computed(() => ({
   name: currentItem.value?.securityMan || '',
   phone: currentItem.value?.securityManPhone || '',
@@ -425,11 +439,14 @@ onShow(() => {
   fetchPipeSetList()
 })
 
+const endCheckDate = ref()
+
 const fetchPipeSetList = async () => {
   loading.value = true
   try {
     const res = await getPipeTaskItemListByOrderId({ id: orderId.value })
     pipeSetList.value = res?.data?.orderItems || []
+    endCheckDate.value = res?.data?.endCheckDate || ''
     mainCheckerUser.value = res?.data?.manager
   } catch (error) {
     console.error('获取管道工程列表失败:', error)
@@ -865,20 +882,65 @@ const handleUpdateSafetyManagerConfirm = async (params: { name: string; phone: s
   }
 }
 
-const handleRouteToEquipmentDetail = async (item: any, pageType: string) => {
-  uni.showLoading({ title: '加载中' })
+const navigateToEquipmentDetail = async (item: any, pageType: string, inputId?: string) => {
   const equipPipeId = item.id
-  const inputIdResp = await getInputIdByEquipId({ equipId: equipPipeId })
-  if (inputIdResp.code != 0 || inputIdResp.data == '') {
-    uni.showToast({ title: '查找设备信息失败', icon: 'none' })
-    return
+  let finalInputId = inputId
+  if (!finalInputId) {
+    uni.showLoading({ title: '加载中' })
+    const inputIdResp = await getInputIdByEquipId({ equipId: equipPipeId })
+    uni.hideLoading()
+    if (inputIdResp.code != 0 || inputIdResp.data == '') {
+      uni.showToast({ title: '查找设备信息失败', icon: 'none' })
+      return
+    }
+    finalInputId = inputIdResp.data
   }
-  uni.hideLoading()
-  const inputId = inputIdResp.data
   uni.navigateTo({
-    url: `/pages/equipment/detail/equipmentDetail?orderId=${orderId.value}&orderItemId=${inputId}&equipId=${equipPipeId}&pageType=${pageType}&useOnline=1&canEdit=${true}`,
+    url: `/pages/equipment/detail/equipmentDetail?orderId=${orderId.value}&orderItemId=${finalInputId}&equipId=${equipPipeId}&pageType=${pageType}&useOnline=1&canEdit=${true}`,
   })
 }
+
+const handleRouteToEquipmentDetail = (item: any, pageType: string) => {
+  if (
+    pageType === 'INSPECT' &&
+    (endCheckDate.value == null || endCheckDate.value == '')
+  ) {
+    // 记录录入时,结束检验时间为空则弹出日期选择器供用户填写
+    endDatePromptContext.value = { item, pageType }
+    showEndDatePrompt.value = true
+    return
+  }
+  navigateToEquipmentDetail(item, pageType)
+}
+
+const handleEndDateConfirm = async (value: string) => {
+  const { item, pageType } = endDatePromptContext.value
+  endDateLoading.value = true
+  try {
+    // inputId 取自 getInputIdByEquipId,作为保存接口的 id 参数
+    const inputIdResp = await getInputIdByEquipId({ equipId: item.id })
+    if (inputIdResp.code != 0 || inputIdResp.data == '') {
+      uni.showToast({ title: '查找设备信息失败', icon: 'none' })
+      return
+    }
+    const inputId = inputIdResp.data
+
+    const res = await savePipeInputEndCheckDate({ id: inputId, endCheckDate: value })
+    if (res?.code !== 0) {
+      uni.showToast({ title: res?.msg || '保存结束检验时间失败', icon: 'none' })
+      return
+    }
+
+    // 保存成功后更新本地数据并跳转页面(复用 inputId 避免重复查询)
+    endCheckDate.value = value
+    showEndDatePrompt.value = false
+    navigateToEquipmentDetail(item, pageType, inputId)
+  } catch (error) {
+    uni.showToast({ title: '保存失败', icon: 'none' })
+  } finally {
+    endDateLoading.value = false
+  }
+}
 </script>
 
 <style lang="scss" scoped>

+ 3 - 4
src/pages/taskOnlinePage/components/BoilerTaskItem.vue

@@ -62,6 +62,7 @@ const props = defineProps<Props>()
 
 const emit = defineEmits<{
   claimTask: [id: string, isClaim: boolean]
+  inspect: [item: any]
 }>()
 
 const isClaim = ref(props.item.isClaim)
@@ -87,11 +88,9 @@ const taskItemInfo = computed(() => ({
   checkUsers: props.item?.checkUsers?.map((user: any) => user.nickname)?.join(',') || '',
 }))
 
-// 跳转到设备详情页(检验录入
+// 通知父组件进行检验录入(父组件处理结束检验时间校验与跳转
 const handleRouteToEquipmentDetail = () => {
-  uni.navigateTo({
-    url: `/pages/equipment/detail/equipmentDetail?orderId=${props.item.orderId}&orderItemId=${props.item.id}&equipId=${props.item.equipId}&pageType=INSPECT&useOnline=1&canEdit=true`,
-  })
+  emit('inspect', props.item)
 }
 
 // 认领/取消认领

+ 3 - 4
src/pages/taskOnlinePage/components/PipeTaskItem.vue

@@ -57,6 +57,7 @@ const props = defineProps<Props>()
 
 const emit = defineEmits<{
   claimTask: [id: string, isClaim: boolean]
+  inspect: [item: any]
 }>()
 
 const isClaim = ref(props.item.isClaim)
@@ -82,11 +83,9 @@ const taskItemInfo = computed(() => ({
   checkUsers: props.item?.checkUsers?.map((user: any) => user.nickname)?.join(',') || '',
 }))
 
-// 跳转到设备详情页(检验录入
+// 通知父组件进行检验录入(父组件处理结束检验时间校验与跳转
 const handleRouteToEquipmentDetail = () => {
-  uni.navigateTo({
-    url: `/pages/equipment/detail/equipmentDetail?orderId=${props.item.orderId}&orderItemId=${props.item.id}&equipId=${props.item.equipId}&pageType=INSPECT&useOnline=1&canEdit=true`,
-  })
+  emit('inspect', props.item)
 }
 
 // 认领/取消认领

+ 55 - 0
src/pages/taskOnlinePage/taskOnline.vue

@@ -43,6 +43,7 @@
           :item="item"
           :user-id="userId"
           @claim-task="handleClaimTask"
+          @inspect="handleInspect"
         />
       </view>
 
@@ -52,6 +53,14 @@
         <text>暂无数据</text>
       </view>
     </scroll-view>
+
+    <EndDatePrompt
+      v-model:visible="showEndDatePrompt"
+      :loading="endDateLoading"
+      title="选择结束检验时间"
+      formatter="YYYY-MM-DD"
+      @confirm="handleEndDateConfirm"
+    />
   </view>
 </template>
 
@@ -67,6 +76,9 @@ import BoilerTaskItem from './components/BoilerTaskItem.vue'
 import PipeTaskItem from './components/PipeTaskItem.vue'
 import { EquipmentType } from '@/utils/dictMap'
 import { onShow } from '@dcloudio/uni-app'
+import EndDatePrompt from '@/components/EndDatePrompt/EndDatePrompt.vue'
+import { saveBoilerItemEndCheckDate } from '@/api/boiler/boilerTaskOrder'
+import { savePipeInputEndCheckDate } from '@/api/pipe/pipeTaskOrder'
 
 defineOptions({
   name: 'taskOnline',
@@ -94,6 +106,10 @@ const userId = computed(() => userInfo.value?.id || '')
 
 const equipType = useConfigStore().getEquipType()
 
+const showEndDatePrompt = ref(false)
+const endDatePromptContext = ref<any>(null)
+const endDateLoading = ref(false)
+
 const taskItemComponent = computed(() => {
   switch (equipType) {
     case EquipmentType.BOILER:
@@ -226,6 +242,45 @@ const handleClaimTask = async (id: string, isClaim: boolean) => {
   }
 }
 
+// 检验录入:校验结束检验时间,为空则弹窗填写
+const handleInspect = (item: any) => {
+  if (item.endCheckDate == null || item.endCheckDate === '') {
+    endDatePromptContext.value = item
+    showEndDatePrompt.value = true
+    return
+  }
+  navigateToEquipmentDetail(item)
+}
+
+const navigateToEquipmentDetail = (item: any) => {
+  uni.navigateTo({
+    url: `/pages/equipment/detail/equipmentDetail?orderId=${item.orderId}&orderItemId=${item.id}&equipId=${item.equipId}&pageType=INSPECT&useOnline=1&canEdit=true`,
+  })
+}
+
+const handleEndDateConfirm = async (value: string) => {
+  const item = endDatePromptContext.value
+  if (!item) return
+  endDateLoading.value = true
+  try {
+    const saveEndDate =
+      equipType === EquipmentType.PIPE ? savePipeInputEndCheckDate : saveBoilerItemEndCheckDate
+    const res = await saveEndDate({ id: item.id, endCheckDate: value })
+    if (res?.code !== 0) {
+      uni.showToast({ title: res?.msg || '保存结束检验时间失败', icon: 'none' })
+      return
+    }
+    // 保存成功后更新本地数据并跳转页面
+    item.endCheckDate = value
+    showEndDatePrompt.value = false
+    navigateToEquipmentDetail(item)
+  } catch (error) {
+    uni.showToast({ title: '保存失败', icon: 'none' })
+  } finally {
+    endDateLoading.value = false
+  }
+}
+
 onShow(() => {
   refreshList()
 })