Переглянути джерело

设备查询调整、单位查询调整、检测录入结论录入功能

yangguanjin 1 місяць тому
батько
коміт
ac7bae5a43

+ 2 - 2
env/.env.development

@@ -5,8 +5,8 @@ VITE_DELETE_CONSOLE = false
 # 是否开启sourcemap
 VITE_SHOW_SOURCEMAP = true
 
-VITE_SERVER_BASEURL = 'http://192.168.0.53:48080/appapi'
-# VITE_SERVER_BASEURL = 'http://127.0.0.1:48080/appapi'
+# VITE_SERVER_BASEURL = 'http://192.168.0.53:48080/appapi'
+VITE_SERVER_BASEURL = 'http://127.0.0.1:48080/appapi'
 
 # VITE_FILE_URL = 'https://youdao.hofo.co/dexdev'
 VITE_FILE_URL = 'http://192.168.0.53:9110/dexdev'

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

@@ -87,3 +87,7 @@ export const getBoilerOrderForm = (params: any) => {
 export const addBoilerMajorIssues = (data: any) => {
   return httpPost('/pressure2/boiler-task-order/order-item/addMajorIssues', data)
 }
+
+export const updateBoilerOrderItemChecker = (data: any) => {
+  return httpPUT('pressure2/boiler-task-order/boiler-order-item/report/update-users', data)
+}

+ 8 - 0
src/api/boiler/boilerTaskOrderReport.ts

@@ -3,3 +3,11 @@ import { httpGet, httpPost, httpPUT, httpDelete } from '@/utils/http'
 export const getBoilerTaskOrderItemReport = (params: any) => {
   return httpGet('/pressure2/boiler-task-order-item-report/get', params)
 }
+
+export const updateBoilerTaskOrderItemReport = (data: any) => {
+  return httpPUT('/pressure2/boiler-task-order-item-report/update', data)
+}
+
+export const updateBoilerTaskOrderItemReportConclusion = (data: any) => {
+  return httpPUT('/pressure2/boiler-task-order-item-report/conclusion', data)
+}

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

@@ -88,3 +88,8 @@ export const getPipeOrderForm = (params: any) => {
 export const addPipeMajorIssues = (data: any) => {
   return httpPost('/pressure2/pipe-task-order/order-item/addMajorIssues', data)
 }
+
+
+export const updatePipeOrderItemChecker = (data: any) => {
+  return httpPUT('pressure2/pipe-task-order/pipe-order-item/report/update-users', data)
+}

+ 8 - 0
src/api/pipe/pipeTaskOrderReport.ts

@@ -3,3 +3,11 @@ import { httpGet, httpPost, httpPUT, httpDelete } from '@/utils/http'
 export const getPipeTaskOrderItemReport = (params: any) => {
   return httpGet('/pressure2/pipe-task-order-item-report/get', params)
 }
+
+export const updatePipeTaskOrderItemReport = (data: any) => {
+  return httpPUT('/pressure2/Pipe-task-order-item-report/update', data)
+}
+
+export const updatePipeTaskOrderItemReportConclusion = (data: any) => {
+  return httpPUT('/pressure2/pipe-task-order-item-report/conclusion', data)
+}

+ 231 - 0
src/components/BackendPDFViewer/index.vue

@@ -0,0 +1,231 @@
+<template>
+  <view class="backend-pdf-viewer">
+    <view v-if="loading" class="pdf-loading">
+      <wd-loading />
+      <text class="loading-text">PDF加载中...</text>
+    </view>
+    <view v-else-if="errorMsg" class="pdf-error">
+      <text class="error-text">{{ errorMsg }}</text>
+    </view>
+    <PDFViewer
+      v-show="!loading && !errorMsg"
+      ref="pdfViewerRef"
+      :source="pdfSource"
+      :viewer-height="viewerHeight"
+      @loaded="handlePDFLoaded"
+      @error="handlePDFError"
+    />
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch } from 'vue'
+import { getEnvBaseUrl } from '@/utils/index'
+import PDFViewer from '@/components/PDFViewer/index.vue'
+
+interface Props {
+  refId?: string | number
+  templateId?: string | number
+  viewerHeight?: string
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  refId: '',
+  templateId: '',
+  viewerHeight: '95vh',
+})
+
+const emit = defineEmits<{
+  (e: 'pdf-loaded', data: ArrayBuffer): void
+  (e: 'error', message: string): void
+}>()
+
+const pdfViewerRef = ref<any>(null)
+const pdfSource = ref<ArrayBuffer | Blob | null>(null)
+
+const loading = ref(false)
+const errorMsg = ref('')
+
+const fetchPDF = async () => {
+  if (!props.refId || !props.templateId) {
+    pdfSource.value = null
+    loading.value = false
+    return
+  }
+
+  loading.value = true
+  errorMsg.value = ''
+
+  try {
+    const excelBlob = await fetchExcel(props.refId, props.templateId)
+    const fd = new FormData()
+    fd.append('file', excelBlob)
+    const pdfData = await convertToPDF(fd)
+    if (pdfData) {
+      pdfSource.value = pdfData
+      emit('pdf-loaded', pdfData)
+    }
+  } catch (error) {
+    console.error('[BackendPDFViewer] PDF获取失败:', error)
+    errorMsg.value = 'PDF获取失败'
+    loading.value = false
+    emit('error', 'PDF获取失败')
+  }
+}
+
+const handlePDFLoaded = () => {
+  loading.value = false
+}
+
+const handlePDFError = (message: string) => {
+  errorMsg.value = message
+  loading.value = false
+  emit('error', message)
+}
+
+const fetchExcel = async (refId: string | number, templateId: string | number): Promise<Blob> => {
+  // #ifdef H5
+  return fetchExcelForH5(refId, templateId)
+  // #endif
+}
+
+const fetchExcelForH5 = async (refId: string | number, templateId: string | number): Promise<Blob> => {
+  const apiPath = '/pressure2/excel/excel'
+  let requestUrl = apiPath
+  if (JSON.parse(import.meta.env.VITE_APP_PROXY) && import.meta.env.MODE === 'development') {
+    requestUrl = import.meta.env.VITE_APP_PROXY_PREFIX + apiPath
+  } else {
+    requestUrl = getEnvBaseUrl() + apiPath
+  }
+
+  const token = uni.getStorageSync('APP_ACCESS_TOKEN')
+  const headers: Record<string, string> = { 'Content-Type': 'application/json' }
+  if (token) {
+    headers.Authorization = `Bearer ${token}`
+  }
+
+  const response = await fetch(requestUrl, {
+    method: 'POST',
+    headers,
+    body: JSON.stringify({ refId, templateId }),
+  })
+
+  if (!response.ok) {
+    throw new Error(`获取Excel失败, status ${response.status}`)
+  }
+
+  return await response.blob()
+}
+
+const convertToPDF = (formData: FormData): Promise<ArrayBuffer> => {
+  const apiPath = '/pressure2/standard-file/getPdf'
+  // #ifdef H5
+  return getPDFForH5(apiPath, formData)
+  // #endif
+}
+
+const getPDFForH5 = async (url: string, formData: FormData): Promise<ArrayBuffer> => {
+  let requestUrl = url
+  if (JSON.parse(import.meta.env.VITE_APP_PROXY) && import.meta.env.MODE === 'development') {
+    requestUrl = import.meta.env.VITE_APP_PROXY_PREFIX + url
+  } else {
+    requestUrl = getEnvBaseUrl() + url
+  }
+
+  const token = uni.getStorageSync('APP_ACCESS_TOKEN')
+  const headers: Record<string, string> = {}
+  if (token) {
+    headers.Authorization = `Bearer ${token}`
+  }
+
+  const response = await fetch(requestUrl, {
+    method: 'POST',
+    headers,
+    body: formData,
+  })
+
+  if (!response.ok) {
+    throw new Error(`PDF转换失败, status ${response.status}`)
+  }
+
+  return await response.arrayBuffer()
+}
+
+const downloadFileAsBase64 = (fileUrl: string): Promise<string> => {
+  return new Promise((resolve, reject) => {
+    uni.request({
+      url: fileUrl,
+      method: 'GET',
+      responseType: 'arraybuffer',
+      success: (res) => {
+        if (res.statusCode === 200) {
+          const arrayBuffer = res.data as ArrayBuffer
+          const uint8Array = new Uint8Array(arrayBuffer)
+          const binaryString = uint8Array.reduce((data, byte) => {
+            return data + String.fromCharCode(byte)
+          }, '')
+          const base64 = btoa(binaryString)
+          resolve(base64)
+        } else {
+          reject(new Error(`Request failed with status ${res.statusCode}`))
+        }
+      },
+      fail: (err) => {
+        reject(err)
+      },
+    })
+  })
+}
+
+watch(
+  () => [props.refId, props.templateId],
+  () => {
+    fetchPDF()
+  },
+  { immediate: true },
+)
+
+defineExpose({
+  downloadFileAsBase64,
+  pdfViewerRef,
+  pdfSource,
+})
+</script>
+
+<style lang="scss" scoped>
+.backend-pdf-viewer {
+  width: 100%;
+}
+
+.pdf-loading {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 12px;
+  width: 100%;
+  height: 300px;
+  background-color: #f5f5f5;
+  border-radius: 8px;
+
+  .loading-text {
+    font-size: 14px;
+    color: #999;
+  }
+}
+
+.pdf-error {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 200px;
+  background-color: #fff5f5;
+  border-radius: 8px;
+
+  .error-text {
+    font-size: 14px;
+    color: #f56c6c;
+  }
+}
+</style>

+ 20 - 10
src/components/QueryViewItem/EquipTypeCom.vue

@@ -5,7 +5,7 @@
       class="equip-type-picker"
       v-model="selectValue"
       :columns="columns"
-      @change="onChange"
+      @confirm="onChange"
     >
       <view class="equip-type-input">
         <text :class="['equip-type-text', { 'equip-type-text--placeholder': !displayLabel }]">
@@ -17,7 +17,7 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, computed } from 'vue'
+import { ref, computed, watch } from 'vue'
 
 interface EquipTypeItem {
   label: string
@@ -27,6 +27,7 @@ interface EquipTypeItem {
 interface Props {
   title: string
   type?: string
+  modelValue?: string | number
   defaultValue?: string | number
   textStyle?: any
   columns?: EquipTypeItem[]
@@ -35,20 +36,26 @@ interface Props {
 const props = withDefaults(defineProps<Props>(), {
   title: '设备类别:',
   type: 'equipType',
+  modelValue: undefined,
   defaultValue: '',
   columns: () => [
-    { label: '全部', value: '' },
-    { label: '压力容器', value: '1' },
-    { label: '锅炉', value: '2' },
-    { label: '管道', value: '3' },
+    { label: '锅炉', value: '200' },
+    { label: '管道', value: '300' },
   ],
 })
 
 const emit = defineEmits<{
+  'update:modelValue': [value: string | number]
   change: [value: string | number, type: string]
 }>()
 
-const selectValue = ref<string | number>(props.defaultValue)
+const selectValue = ref<string | number>(props.modelValue ?? props.defaultValue)
+
+watch(() => props.modelValue, (val) => {
+  if (val !== undefined) {
+    selectValue.value = val
+  }
+})
 
 const displayLabel = computed(() => {
   if (!selectValue.value) return ''
@@ -56,19 +63,22 @@ const displayLabel = computed(() => {
   return item?.label || ''
 })
 
-const onChange = ({ selectedItem }: { selectedItem: EquipTypeItem }) => {
-  selectValue.value = selectedItem.value
-  emit('change', selectedItem.value, props.type)
+const onChange = ({value, selectedItems}) => {
+  selectValue.value = selectedItems.value
+  emit('update:modelValue', selectedItems.value)
+  emit('change', selectedItems.value, props.type)
 }
 
 defineExpose({
   reset: () => {
     selectValue.value = ''
+    emit('update:modelValue', '')
   },
   inputContent: computed(() => selectValue.value || ''),
   getValue: () => selectValue.value,
   setValue: (value: string | number) => {
     selectValue.value = value
+    emit('update:modelValue', value)
   },
 })
 </script>

+ 14 - 2
src/components/QueryViewItem/InputCom.vue

@@ -13,11 +13,12 @@
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
+import { ref, computed, watch } from 'vue'
 
 interface Props {
   title: string
   type: string
+  modelValue?: string
   defaultValue?: string
   placeholder?: string
   style?: any
@@ -25,31 +26,42 @@ interface Props {
 }
 
 const props = withDefaults(defineProps<Props>(), {
+  modelValue: undefined,
   defaultValue: '',
   placeholder: '请输入',
 })
 
 const emit = defineEmits<{
+  'update:modelValue': [value: string]
   change: [text: string, type: string]
 }>()
 
-const inputContent = ref(props.defaultValue)
+const inputContent = ref(props.modelValue ?? props.defaultValue)
 const inputRef = ref<any>(null)
 
+watch(() => props.modelValue, (val) => {
+  if (val !== undefined) {
+    inputContent.value = val
+  }
+})
+
 defineExpose({
   reset: () => {
     inputContent.value = ''
+    emit('update:modelValue', '')
   },
   inputContent: computed(() => inputContent.value),
   getValue: () => inputContent.value,
   setValue: (value: string) => {
     inputContent.value = value
+    emit('update:modelValue', value)
   },
 })
 
 const handleInput = (e: any) => {
   const text = e.detail.value
   inputContent.value = text
+  emit('update:modelValue', text)
   emit('change', text, props.type)
 }
 </script>

+ 1 - 1
src/components/SpreadDesigner/SpreadPDFViewer.vue

@@ -92,7 +92,7 @@ const handlePDFError = (message: string) => {
 }
 
 const getPDF = (formData: FormData): Promise<ArrayBuffer> => {
-  const apiPath = '/pressure2/standard-file/getPdfLocal'
+  const apiPath = '/pressure2/standard-file/getPdf'
   // #ifdef H5
   return getPDFForH5(apiPath, formData)
   // #endif

+ 9 - 0
src/pages.json

@@ -412,6 +412,15 @@
         "disableScroll": true
       }
     },
+    {
+      "path": "pages/securityCheck/detail/index copy",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationBarTitleText": "安全检查记录详情",
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/securityCheck/detail/index",
       "type": "page",

+ 101 - 65
src/pages/deviceExam/components/query/QueryView.vue

@@ -4,68 +4,87 @@
     <image class="arrow-icon" :src="iconMap.ArrowDown" :class="{ 'arrow-rotate': isOpen }" />
   </view>
   <view class="query-form" :style="contentStyle">
-    <EquipTypeCom
-      ref="equipTypeRef"
-      title="设备类别:"
-      type="type"
-      :columns="equipmentTypes"
-      :text-style="{ width: '100px' }"
-      :style="{ marginLeft: 0 }"
-      @change="handleEquipTypeChange"
-    />
+    <view class="equip-type-row">
+      <text class="equip-type-label" :style="{ width: '100px' }">设备类型:</text>
+      <RadioFilterBar
+        v-model="equipTypeCodeModel"
+        :options="equipmentTypes"
+        type="radio"
+        :margin-left="'20px'"
+        class="equip-type-radio"
+      />
+    </view>
     <InputCom
-      ref="equipNameRef"
+      v-if="equipTypeCode == '200'"
+      v-model="params.equipName"
       title="设备名称:"
       type="equipName"
       :text-style="{ width: '100px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入设备名称"
-      @change="handleChange('equipName', $event)"
     />
     <InputCom
-      ref="equipCodeRef"
+      v-if="equipTypeCode == '300'"
+      v-model="params.projectName"
+      title="工程名称:"
+      type="projectName"
+      :text-style="{ width: '100px', textAlign: 'right' }"
+      :style="{ marginLeft: 0 }"
+      placeholder="请输入工程名称"
+    />
+    <InputCom
+      v-if="equipTypeCode == '200'"
+      v-model="params.equipCode"
       title="设备注册代码:"
       type="equipCode"
       :text-style="{ width: '100px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入设备注册代码"
-      @change="handleChange('equipCode', $event)"
     />
     <InputCom
-      ref="productNoRef"
+      v-if="equipTypeCode == '300'"
+      v-model="params.projectNo"
+      title="工程号:"
+      type="projectNo"
+      :text-style="{ width: '100px', textAlign: 'right' }"
+      :style="{ marginLeft: 0 }"
+      placeholder="请输入工程号"
+    />
+    <InputCom
+      v-model="params.factoryCode"
+      v-if="equipTypeCode == '200'"
       title="出厂编号:"
-      type="productNo"
+      type="factoryCode"
       :text-style="{ width: '100px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入出厂编号"
-      @change="handleChange('productNo', $event)"
     />
     <InputCom
-      ref="unitNameRef"
+      v-model="params.unitName"
+      v-if="['200', '300'].includes(equipTypeCode)"
       title="使用单位:"
       type="unitName"
       :text-style="{ width: '100px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入使用单位"
-      @change="handleChange('unitName', $event)"
     />
     <InputCom
-      ref="unitInnerNoRef"
+      v-model="params.unitInternalCode"
+      v-if="equipTypeCode == '200'"
       title="单位内编号:"
-      type="unitInnerNo"
+      type="unitInternalCode"
       :text-style="{ width: '100px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入单位内编号"
-      @change="handleChange('unitInnerNo', $event)"
     />
     <InputCom
-      ref="useRegisterNoRef"
+      v-model="params.useRegisterNo"
+      v-if="['200', '300'].includes(equipTypeCode)"
       title="使用证编号:"
       type="useRegisterNo"
       :text-style="{ width: '100px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入使用证编号"
-      @change="handleChange('useRegisterNo', $event)"
     />
     <view class="form-actions">
       <button class="action-btn reset-btn" @click.stop="handleReset">重置</button>
@@ -77,30 +96,38 @@
 <script lang="ts" setup>
 import { ref, reactive, computed } from 'vue'
 import { InputCom } from '@/components/QueryViewItem'
-import { EquipTypeCom } from '@/components/QueryViewItem'
+import RadioFilterBar from '@/components/RadioFilterBar/RadioFilterBar.vue'
 import iconMap from '@/utils/imagesMap'
 
+const props = defineProps<{
+  equipTypeCode: any;
+}>()
+
 const emit = defineEmits<{
   queryAction: [params: Record<string, any>]
-  resetAction: []
+  resetAction: [],
+  'update:equipTypeCode': [val: any]
 }>()
 
 const isOpen = ref(true)
 const params = reactive({
-  type: '',
-  equipName: '',
-  equipCode: '',
-  productNo: '',
-  unitName: '',
-  unitInnerNo: '',
-  useRegisterNo: '',
+    equipName: '',
+    projectName: '',
+
+    equipCode: '',
+    projectNo: '',
+
+    factoryCode: '',
+    unitName: '',
+    unitInternalCode: '',
+
+    useRegisterNo: '',
+    certificateNo: '',
 })
 
 const equipmentTypes = [
-  { label: '全部', value: '' },
-  { label: '压力容器', value: '1' },
-  { label: '锅炉', value: '2' },
-  { label: '管道', value: '3' },
+  { id: '200', value: '锅炉' },
+  { id: '300', value: '管道' },
 ]
 
 const contentStyle = computed(() => ({
@@ -111,43 +138,31 @@ const contentStyle = computed(() => ({
   transition: 'all 0.5s',
 }))
 
-const equipTypeRef = ref<any>(null)
-const equipNameRef = ref<any>(null)
-const equipCodeRef = ref<any>(null)
-const productNoRef = ref<any>(null)
-const unitNameRef = ref<any>(null)
-const unitInnerNoRef = ref<any>(null)
-const useRegisterNoRef = ref<any>(null)
+const equipTypeCodeModel = computed({
+  get: () => props.equipTypeCode,
+  set: (val) => emit('update:equipTypeCode', val),
+})
 
 const toggleFilter = () => {
   isOpen.value = !isOpen.value
 }
 
-const handleEquipTypeChange = (value: string | number) => {
-  params.type = value as string
-}
+const handleReset = () => {
+  Object.assign(params, {
+    equipName: '',
+    projectName: '',
 
-const handleChange = (propName: string, value: string) => {
-  params[propName as keyof typeof params] = value
-}
+    equipCode: '',
+    projectNo: '',
 
-const handleReset = () => {
-  params.type = ''
-  params.equipName = ''
-  params.equipCode = ''
-  params.productNo = ''
-  params.unitName = ''
-  params.unitInnerNo = ''
-  params.useRegisterNo = ''
-
-  equipTypeRef.value?.reset()
-  equipNameRef.value?.reset()
-  equipCodeRef.value?.reset()
-  productNoRef.value?.reset()
-  unitNameRef.value?.reset()
-  unitInnerNoRef.value?.reset()
-  useRegisterNoRef.value?.reset()
+    factoryCode: '',
+    unitName: '',
+    unitInternalCode: '',
 
+    useRegisterNo: '',
+    certificateNo: '',
+  })
+  emit('update:equipTypeCode', null)
   emit('resetAction')
 }
 
@@ -157,6 +172,7 @@ const handleQuery = () => {
 
 defineExpose({
   refresh: () => handleQuery(),
+  getParams: () => ({ ...params, certificateNo: params.useRegisterNo }),
 })
 </script>
 
@@ -223,4 +239,24 @@ defineExpose({
 .arrow-rotate {
   transform: rotate(180deg);
 }
+
+.equip-type-row {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  margin-bottom: 10px;
+}
+
+.equip-type-label {
+  color: rgb(51, 51, 51);
+  font-size: 14px;
+  text-align: right;
+  flex-shrink: 0;
+}
+
+.equip-type-radio {
+  flex: 1;
+  padding: 0;
+  border-bottom: none;
+}
 </style>

+ 82 - 28
src/pages/deviceExam/deviceExam.vue

@@ -18,34 +18,70 @@
 
     <QueryView
       ref="queryViewRef"
+      v-model:equipTypeCode="initEquipTypeCode"
       @query-action="handleQueryAction"
       @reset-action="handleResetAction"
     />
 
     <scroll-view class="list-scroll" scroll-y @scrolltolower="loadMore">
-      <view v-for="item in listData" :key="item.id" class="item-box" @click="handleItemClick(item)">
-        <view class="item-header">
-          <text class="item-title">设备注册代码:{{ item.equipCode }}</text>
-        </view>
-        <view class="item-content">
-          <view class="info-item">
-            <text class="info-label">设备名称:</text>
-            <text class="info-value">{{ item.equipName }}</text>
+      <view v-if="equipType == EquipmentType.BOILER">
+        <view
+          v-for="item in listData"
+          :key="item.id"
+          class="item-box"
+          @click="handleItemClick(item)"
+        >
+          <view class="item-header">
+            <text class="item-title">注册代码:{{ item.equipCode }}</text>
+          </view>
+          <view class="item-content">
+            <view class="info-item">
+              <text class="info-label">设备名称:</text>
+              <text class="info-value">{{ item.equipName }}</text>
+            </view>
+            <view class="info-item">
+              <text class="info-label">出厂编号:</text>
+              <text class="info-value">{{ item.factoryCode || '--' }}</text>
+            </view>
+            <view class="info-item">
+              <text class="info-label">使用单位:</text>
+              <text class="info-value">{{ item.unitName }}</text>
+            </view>
           </view>
-          <view class="info-item">
-            <text class="info-label">出厂编号:</text>
-            <text class="info-value">{{ item.productNo || '--' }}</text>
+        </view>
+      </view>
+      <view v-if="equipType == EquipmentType.PIPE">
+        <view
+          v-for="item in listData"
+          :key="item.id"
+          class="item-box"
+          @click="handleItemClick(item)"
+        >
+          <view class="item-header">
+            <text class="item-title">工程号:{{ item.projectNo }}</text>
           </view>
-          <view class="info-item">
-            <text class="info-label">使用单位:</text>
-            <text class="info-value">{{ item.unitName }}</text>
+          <view class="item-content">
+            <view class="info-item">
+              <text class="info-label">工程名称:</text>
+              <text class="info-value">{{ item.projectName }}</text>
+            </view>
+            <!-- <view class="info-item">
+              <text class="info-label">出厂编号:</text>
+              <text class="info-value">{{ item.factoryCode || '--' }}</text>
+            </view> -->
+            <view class="info-item">
+              <text class="info-label">使用单位:</text>
+              <text class="info-value">{{ item.unitName }}</text>
+            </view>
           </view>
         </view>
       </view>
 
       <view v-if="loading" class="loading-text">加载中...</view>
-      <view v-if="!hasMore && listData.length > 0" class="no-more-text">没有更多了</view>
-      <view v-if="!loading && listData.length === 0" class="empty-view">
+      <view v-if="!initEquipTypeCode && listData.length === 0" class="empty-view">
+        <text class="empty-text">请先选择设备类型</text>
+      </view>
+      <view v-if="initEquipTypeCode && hasQueried && !loading && listData.length === 0" class="empty-view">
         <text class="empty-text">暂无数据</text>
       </view>
     </scroll-view>
@@ -53,35 +89,55 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, reactive } from 'vue'
-import { useConfigStore } from '@/store/config'
+import { ref, reactive, watch } from 'vue'
 import { EquipFuncName, requestFunc } from '@/api/ApiRouter/equipment'
 import NavBar from '@/components/NavBar/NavBar.vue'
 import QueryView from '@/pages/deviceExam/components/query/QueryView.vue'
-import { onShow } from '@dcloudio/uni-app'
+import { EquipmentType } from '@/utils/dictMap'
 
 defineOptions({
   name: 'deviceExam',
 })
 
-const equipType = useConfigStore().getEquipType()
+let equipType = ''
+const initEquipTypeCode = ref('')
+watch(initEquipTypeCode, (newVal) => {
+  switch (newVal) {
+    case '200':
+      equipType = EquipmentType.BOILER
+      break
+    case '300':
+      equipType = EquipmentType.PIPE
+      break
+    default:
+      equipType = null
+  }
+  listData.value = []
+  hasMore.value = true
+  hasQueried.value = false
+})
 
 const listData = ref<any[]>([])
 const loading = ref(false)
 const hasMore = ref(true)
+const hasQueried = ref(false)
 const queryViewRef = ref<any>(null)
 const params = reactive({
   pageNo: 1,
   pageSize: 10,
 })
 
-const fetchList = async (refresh = false, queryParams?: Record<string, any>) => {
+const fetchList = async (refresh = false) => {
   if (loading.value) return
+  if (!equipType) {
+    uni.showToast({ title: '请先选择设备类型', icon: 'error' })
+  }
 
   params.pageNo = refresh ? 1 : params.pageNo + 1
 
   loading.value = true
   try {
+    const queryParams = queryViewRef.value?.getParams() || {}
     const queryData = {
       ...params,
       ...queryParams,
@@ -114,13 +170,15 @@ const refreshList = () => {
   fetchList(true)
 }
 
-const handleQueryAction = (queryParams: Record<string, any>) => {
+const handleQueryAction = () => {
+  hasQueried.value = true
   params.pageNo = 1
-  fetchList(true, queryParams)
+  fetchList(true)
 }
 
 const handleResetAction = () => {
-  refreshList()
+  // refreshList()
+  listData.value = []
 }
 
 const handleItemClick = (item: any) => {
@@ -128,10 +186,6 @@ const handleItemClick = (item: any) => {
     url: `/pages/deviceExam/deviceExamDetail?id=${item.id}`,
   })
 }
-
-onShow(() => {
-  refreshList()
-})
 </script>
 
 <style lang="scss" scoped>

+ 31 - 60
src/pages/equipment/detail/components/BoilerInspectProject.vue

@@ -264,11 +264,11 @@
                     class="conclusion-text"
                     :class="{
                       'conclusion-error-text': isConclusionError(
-                        getRecordJson(item).reportConclusion,
+                        item.reportConclusion
                       ),
                     }"
                   >
-                    {{ getRecordJson(item).reportConclusion || '无' }}
+                    {{ item.reportConclusion || '无' }}
                   </text>
                 </view>
 
@@ -309,7 +309,7 @@
               <view class="center-row">
                 <text class="label">检验结果:</text>
                 <view class="result-box" @click="handleUpdateConclusion(item, 'reportResult')">
-                  <text class="result-text">{{ getRecordJson(item).reportResult || '-' }}</text>
+                  <text class="result-text">{{ item.reportResult || '-' }}</text>
                 </view>
               </view>
 
@@ -501,8 +501,9 @@
     <ExchangeChecker
       v-if="showExchangePopup"
       ref="exchangeCheckerPopupRef"
-      :checker="editCheckItem?.checkUsers"
-      :popup-data="{ checItemId: currentCheckItemId }"
+      :check-users="dataSource?.checkUsers"
+      :current-checker="editCheckItem?.checkUsers?.[0]"
+      :check-item-id="currentCheckItemId"
       @hide="hideExchangePopup"
       @update-check-item-checker="handleUpdateChecker"
     />
@@ -564,6 +565,7 @@ import {
 } from '@/api/task'
 import { getReportTemplateDetail } from '@/api'
 import { cancelBoilerInSpectProject } from '@/api/boiler/boilerTaskOrder'
+import { updateBoilerTaskOrderItemReportConclusion } from '@/api/boiler/boilerTaskOrderReport'
 import TipsPopup from './inspectProjectComponent/TipsPopup.vue'
 import CalcCheckItemPopup from './inspectProject/component/calcCheckItemPopup.vue'
 import ExchangeChecker from './inspectProject/component/ExchangeChecker.vue'
@@ -688,6 +690,7 @@ const canModifyChecker = computed(() => {
 
 const isNetworkConnected = ref(true)
 onMounted(() => {
+  console.log('datasource.......', props.dataSource)
   checkNetworkStatus()
   initSelected()
   setupEventListeners()
@@ -956,25 +959,26 @@ const hideExchangePopup = () => {
 }
 
 const handleUpdateChecker = async (checker: any, checkItemId: string) => {
-  try {
-    const newChecker = typeof checker === 'string' ? JSON.parse(checker) : checker
-    if (!newChecker.id) {
-      console.warn('Checker missing id')
-      return
-    }
-
-    const { handleUpdateReportCheckUsers } = await import('@/api/task')
-    const result = await handleUpdateReportCheckUsers(newChecker, checkItemId)
-
-    if (result) {
-      uni.showToast({ title: '更新检验员成功' })
-      hideExchangePopup()
-      props.refreshDetail?.()
-    }
-  } catch (error) {
-    console.error('更新检验员失败:', error)
-    uni.showToast({ title: '更新检验员失败', icon: 'error' })
-  }
+  console.log('checker......', checker, checkItemId)
+  // try {
+  //   const newChecker = typeof checker === 'string' ? JSON.parse(checker) : checker
+  //   if (!newChecker.id) {
+  //     console.warn('Checker missing id')
+  //     return
+  //   }
+
+  //   const { handleUpdateReportCheckUsers } = await import('@/api/task')
+  //   const result = await handleUpdateReportCheckUsers(newChecker, checkItemId)
+
+  //   if (result) {
+  //     uni.showToast({ title: '更新检验员成功' })
+  //     hideExchangePopup()
+  //     props.refreshDetail?.()
+  //   }
+  // } catch (error) {
+  //   console.error('更新检验员失败:', error)
+  //   uni.showToast({ title: '更新检验员失败', icon: 'error' })
+  // }
 }
 
 const handleUpdateConclusion = (item: CheckItem, fieldKey: 'reportConclusion' | 'reportResult') => {
@@ -1000,42 +1004,9 @@ const hideConclusionPopup = () => {
   currentFieldKey.value = 'reportConclusion'
 }
 
-const handleUpdateConclusionConfirm = async (value: string) => {
-  if (!currentCheckItem.value || !value) {
-    uni.showToast({ title: '请输入内容', icon: 'error' })
-    return
-  }
-
-  try {
-    const { saveTaskReportTemplate } = await import('@/api/task')
-
-    const prepareJson = currentCheckItem.value.recordJson
-      ? JSON.parse(currentCheckItem.value.recordJson)
-      : {}
-
-    prepareJson[currentFieldKey.value] = value
-
-    const params = {
-      id: currentCheckItem.value.id,
-      prepareJson: JSON.stringify(prepareJson),
-    }
-
-    uni.showLoading({ title: '提交中...' })
-    const result = await saveTaskReportTemplate(params)
-    uni.hideLoading()
-
-    if (result?.code == 0) {
-      uni.showToast({ title: '修改成功', icon: 'success' })
-      hideConclusionPopup()
-      props.refreshDetail?.()
-    } else {
-      uni.showToast({ title: result?.msg || '修改失败', icon: 'error' })
-    }
-  } catch (error) {
-    uni.hideLoading()
-    console.error('修改失败:', error)
-    uni.showToast({ title: '修改失败', icon: 'error' })
-  }
+const handleUpdateConclusionConfirm = async (conclusionData: any) => {
+  await updateBoilerTaskOrderItemReportConclusion(conclusionData)
+  showConclusionPopup.value = false
 }
 
 const handleSubmitCheck = (item: CheckItem) => {

+ 35 - 60
src/pages/equipment/detail/components/PipeInspectProject.vue

@@ -37,11 +37,11 @@
                   class="conclusion-text"
                   :class="{
                     'conclusion-error-text': isConclusionError(
-                      getRecordJson(item).reportConclusion,
+                      item.reportConclusion,
                     ),
                   }"
                 >
-                  {{ getRecordJson(item).reportConclusion || '无' }}
+                  {{ item.reportConclusion || '无' }}
                 </text>
               </view>
 
@@ -80,7 +80,7 @@
             <view class="center-row">
               <text class="label">检验结果:</text>
               <view class="result-box" @click="handleUpdateConclusion(item, 'reportResult')">
-                <text class="result-text">{{ getRecordJson(item).reportResult || '-' }}</text>
+                <text class="result-text">{{ item.reportResult || '-' }}</text>
               </view>
             </view>
 
@@ -263,8 +263,9 @@
     <ExchangeChecker
       v-if="showExchangePopup"
       ref="exchangeCheckerPopupRef"
-      :checker="editCheckItem?.checkUsers"
-      :popup-data="{ checItemId: currentCheckItemId }"
+      :check-users="dataSource?.checkUsers"
+      :current-checker="editCheckItem?.checkUsers?.[0]"
+      :check-item-id="currentCheckItemId"
       @hide="hideExchangePopup"
       @update-check-item-checker="handleUpdateChecker"
     />
@@ -325,6 +326,7 @@ import {
   addMajorIssuesApi,
 } from '@/api/task'
 import { cancelPipeInSpectProject } from '@/api/pipe/pipeTaskOrder'
+import { updatePipeTaskOrderItemReportConclusion } from '@/api/pipe/pipeTaskOrderReport'
 import { getReportTemplateDetail } from '@/api'
 import TipsPopup from './inspectProjectComponent/TipsPopup.vue'
 import CalcCheckItemPopup from './inspectProject/component/calcCheckItemPopup.vue'
@@ -413,7 +415,12 @@ const canModifyChecker = computed(() => {
 })
 
 const isNetworkConnected = ref(true)
+watch(props.dataSource, (newVal) => {
+  console.log('datasourcechange......', newVal)
+})
+
 onMounted(() => {
+  console.log('datasource.......', props.dataSource)
   checkNetworkStatus()
   initSelected()
   setupEventListeners()
@@ -682,25 +689,26 @@ const hideExchangePopup = () => {
 }
 
 const handleUpdateChecker = async (checker: any, checkItemId: string) => {
-  try {
-    const newChecker = typeof checker === 'string' ? JSON.parse(checker) : checker
-    if (!newChecker.id) {
-      console.warn('Checker missing id')
-      return
-    }
-
-    const { handleUpdateReportCheckUsers } = await import('@/api/task')
-    const result = await handleUpdateReportCheckUsers(newChecker, checkItemId)
-
-    if (result) {
-      uni.showToast({ title: '更新检验员成功' })
-      hideExchangePopup()
-      props.refreshDetail?.()
-    }
-  } catch (error) {
-    console.error('更新检验员失败:', error)
-    uni.showToast({ title: '更新检验员失败', icon: 'error' })
-  }
+  console.log('checker......', checker, checkItemId)
+  // try {
+  //   const newChecker = typeof checker === 'string' ? JSON.parse(checker) : checker
+  //   if (!newChecker.id) {
+  //     console.warn('Checker missing id')
+  //     return
+  //   }
+
+  //   const { handleUpdateReportCheckUsers } = await import('@/api/task')
+  //   const result = await handleUpdateReportCheckUsers(newChecker, checkItemId)
+
+  //   if (result) {
+  //     uni.showToast({ title: '更新检验员成功' })
+  //     hideExchangePopup()
+  //     props.refreshDetail?.()
+  //   }
+  // } catch (error) {
+  //   console.error('更新检验员失败:', error)
+  //   uni.showToast({ title: '更新检验员失败', icon: 'error' })
+  // }
 }
 
 const handleUpdateConclusion = (item: CheckItem, fieldKey: 'reportConclusion' | 'reportResult') => {
@@ -726,42 +734,9 @@ const hideConclusionPopup = () => {
   currentFieldKey.value = 'reportConclusion'
 }
 
-const handleUpdateConclusionConfirm = async (value: string) => {
-  if (!currentCheckItem.value || !value) {
-    uni.showToast({ title: '请输入内容', icon: 'error' })
-    return
-  }
-
-  try {
-    const { saveTaskReportTemplate } = await import('@/api/task')
-
-    const prepareJson = currentCheckItem.value.recordJson
-      ? JSON.parse(currentCheckItem.value.recordJson)
-      : {}
-
-    prepareJson[currentFieldKey.value] = value
-
-    const params = {
-      id: currentCheckItem.value.id,
-      prepareJson: JSON.stringify(prepareJson),
-    }
-
-    uni.showLoading({ title: '提交中...' })
-    const result = await saveTaskReportTemplate(params)
-    uni.hideLoading()
-
-    if (result?.code == 0) {
-      uni.showToast({ title: '修改成功', icon: 'success' })
-      hideConclusionPopup()
-      props.refreshDetail?.()
-    } else {
-      uni.showToast({ title: result?.msg || '修改失败', icon: 'error' })
-    }
-  } catch (error) {
-    uni.hideLoading()
-    console.error('修改失败:', error)
-    uni.showToast({ title: '修改失败', icon: 'error' })
-  }
+const handleUpdateConclusionConfirm = async (conclusionData: any) => {
+  await updatePipeTaskOrderItemReportConclusion(conclusionData)
+  showConclusionPopup.value = false
 }
 
 const handleSubmitCheck = (item: CheckItem) => {

+ 51 - 168
src/pages/equipment/detail/components/inspectProject/component/ExchangeChecker.vue

@@ -6,32 +6,23 @@
       </view>
 
       <scroll-view class="scroll-content" scroll-y>
-        <view v-for="group in groupList" :key="group.deptGroupId" class="group-section">
-          <view class="group-header" @click="toggleGroup(group.deptGroupId)">
-            <text class="group-title">{{ group.deptGroupName }}</text>
-            <text class="group-arrow">{{ group.expanded ? '▼' : '▶' }}</text>
-          </view>
-
-          <view v-if="group.expanded" class="member-list">
-            <radio-group @change="handleRadioChange(group.deptGroupId, $event)">
-              <label
-                v-for="member in group.memberList"
-                :key="member.member?.userId"
-                class="member-item"
-              >
-                <radio
-                  :value="JSON.stringify(member)"
-                  :checked="selectedChecker === JSON.stringify(member)"
-                  color="#4B8CD9"
-                />
-                <text class="member-name">{{ member.member?.nickname }}</text>
-              </label>
-            </radio-group>
-          </view>
-        </view>
-
-        <view v-if="!groupList.length" class="empty-state">
-          <text>暂无检验组信息</text>
+        <radio-group @change="handleRadioChange">
+          <label
+            v-for="user in checkUsers"
+            :key="user.id"
+            class="member-item"
+          >
+            <radio
+              :value="user.id"
+              :checked="selectedUserId === user.id"
+              color="#4B8CD9"
+            />
+            <text class="member-name">{{ user.nickname }}</text>
+          </label>
+        </radio-group>
+
+        <view v-if="!checkUsers?.length" class="empty-state">
+          <text>暂无检验员信息</text>
         </view>
       </scroll-view>
 
@@ -41,7 +32,6 @@
         </button>
         <button class="confirm-btn" @click="handleConfirm">
           <text>确定</text>
-          <view v-if="isLoading" class="loading-indicator"></view>
         </button>
       </view>
     </view>
@@ -49,97 +39,51 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, onMounted } from 'vue'
-import { getCheckerGroupByDeptId } from '@/api/task'
-
-interface Member {
-  member: {
-    userId: string
-    nickname: string
-    [key: string]: any
-  }
-  [key: string]: any
-}
+import { ref, watch, onMounted } from 'vue'
 
-interface Group {
-  deptGroupId: string
-  deptGroupName: string
-  memberList: Member[]
-  expanded: boolean
+interface CheckerUser {
+  id: string
+  nickname: string
   [key: string]: any
 }
 
 interface Props {
-  checker?: any
-  popupData?: {
-    checItemId: string
-  }
+  checkUsers?: CheckerUser[]
+  currentChecker?: CheckerUser
+  checkItemId?: string
 }
 
+const props = withDefaults(defineProps<Props>(), {
+  checkUsers: () => [],
+  currentChecker: undefined,
+  checkItemId: '',
+})
+
+watch(props.checkUsers, (newVal) => {
+  console.log('checkusers.....', newVal)
+})
+
 interface Emits {
   (e: 'hide'): void
-  (e: 'updateCheckItemChecker', data: any, checItemId: string): void
+  (e: 'updateCheckItemChecker', data: any, checkItemId: string): void
 }
 
-const props = defineProps<Props>()
 const emit = defineEmits<Emits>()
 
-const selectedChecker = ref<string | null>(null)
-const groupList = ref<Group[]>([])
-const isLoading = ref(false)
+const selectedUserId = ref<string | null>(null)
 
 onMounted(() => {
-  fetchCheckerGroupByDeptId()
-})
-
-const fetchCheckerGroupByDeptId = async () => {
-  try {
-    const userInfo = uni.getStorageSync('userInfo')
-    const deptIds = userInfo?.deptId || ''
-
-    const res = await getCheckerGroupByDeptId({ deptIds })
-    const data = res?.data || []
-
-    groupList.value = data
-      .map((item: any) => item.teamList || [])
-      .flat()
-      .map((group: any) => ({
-        ...group,
-        expanded: true
-      }))
-
-    if (props.checker) {
-      selectedChecker.value = typeof props.checker === 'string'
-        ? props.checker
-        : JSON.stringify(props.checker)
-    }
-  } catch (error) {
-    console.error('获取检验组信息失败:', error)
-    uni.showToast({
-      title: '获取检验组信息失败',
-      icon: 'none'
-    })
+  if (props.currentChecker?.id) {
+    selectedUserId.value = props.currentChecker.id
   }
-}
-
-const toggleGroup = (groupId: string) => {
-  groupList.value = groupList.value.map(group => {
-    if (group.deptGroupId === groupId) {
-      return {
-        ...group,
-        expanded: !group.expanded
-      }
-    }
-    return group
-  })
-}
+})
 
-const handleRadioChange = (groupId: string, event: any) => {
-  selectedChecker.value = event.detail.value
+const handleRadioChange = (event: any) => {
+  selectedUserId.value = event.detail.value
 }
 
 const handleConfirm = () => {
-  if (!selectedChecker.value) {
+  if (!selectedUserId.value) {
     uni.showToast({
       title: '请选择检验员',
       icon: 'none'
@@ -147,20 +91,10 @@ const handleConfirm = () => {
     return
   }
 
-  isLoading.value = true
-
-  try {
-    const memberData = JSON.parse(selectedChecker.value)
-    emit('updateCheckItemChecker', memberData, props.popupData?.checItemId)
+  const selectedUser = props.checkUsers.find(user => user.id === selectedUserId.value)
+  if (selectedUser) {
+    emit('updateCheckItemChecker', selectedUser, props.checkItemId)
     emit('hide')
-  } catch (error) {
-    console.error('解析检验员数据失败:', error)
-    uni.showToast({
-      title: '选择失败',
-      icon: 'none'
-    })
-  } finally {
-    isLoading.value = false
   }
 }
 
@@ -211,54 +145,19 @@ const handleClose = () => {
   padding: 12px;
 }
 
-.group-section {
-  margin-bottom: 12px;
-  background-color: #fff;
-  border-radius: 4px;
-  overflow: hidden;
-}
-
-.group-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 12px 16px;
-  background-color: #fff;
-  border-bottom: 1px solid #f2f2f2;
-}
-
-.group-title {
-  font-size: 16px;
-  color: #333;
-}
-
-.group-arrow {
-  font-size: 12px;
-  color: #999;
-}
-
-.member-list {
-  padding: 12px;
-  background-color: #f4f4f4;
-  border-radius: 4px;
-  margin: 12px;
-  margin-top: 0;
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-}
-
 .member-item {
-  width: 120px;
   display: flex;
   flex-direction: row;
   align-items: center;
-  margin-right: 8px;
+  padding: 10px 16px;
+  margin-bottom: 4px;
+  background-color: #fff;
+  border-radius: 4px;
 }
 
 .member-name {
-  margin-left: 4px;
-  font-size: 14px;
+  margin-left: 8px;
+  font-size: 15px;
   color: #333;
 }
 
@@ -301,20 +200,4 @@ const handleClose = () => {
   background-color: #2f8aff;
   color: #f1f7ff;
 }
-
-.loading-indicator {
-  width: 16px;
-  height: 16px;
-  border: 2px solid #fff;
-  border-top-color: transparent;
-  border-radius: 50%;
-  margin-left: 8px;
-  animation: spin 1s linear infinite;
-}
-
-@keyframes spin {
-  to {
-    transform: rotate(360deg);
-  }
-}
 </style>

+ 114 - 101
src/pages/equipment/detail/components/inspectProject/component/UpdateConclusionPopup.vue

@@ -1,34 +1,47 @@
 <template>
-  <view v-if="!isConclusion" class="popup-overlay" @click="handleCancel">
-    <view class="popup-content" @click.stop="preventClose">
-      <view class="popup-header">
-        <text class="popup-title">修改检验结果</text>
-        <view class="close-btn" @click="handleCancel">
-          <text class="close-icon">✕</text>
-        </view>
+  <wd-popup
+    v-model="showPopup"
+    position="center"
+    closable
+    custom-style="border-radius: 8px; width: 70%; max-width: 300px;"
+    @close="handleClose"
+  >
+    <view class="popup-header">
+      <text class="popup-title">{{ isConclusion ? '修改检验结论' : '修改检验结果' }}</text>
+    </view>
+
+    <view v-if="isConclusion" class="popup-body">
+      <view
+        v-for="option in conclusionOptions"
+        :key="option.label"
+        class="option-item"
+        :class="{ 'option-active': selectedConclusion === option.value }"
+        @click="handleSelectConclusion(option.value)"
+      >
+        <text class="option-text" :class="{ 'option-active-text': selectedConclusion === option.value }">{{ option.label }}</text>
       </view>
+    </view>
 
-      <view class="popup-body">
-        <view class="input-section">
-          <textarea
-            v-model="inputValue"
-            class="input-textarea"
-            placeholder="请输入检验结果"
-            maxlength="200"
-          />
-        </view>
+    <view v-else class="popup-body">
+      <view class="input-section">
+        <textarea
+          v-model="inputValue"
+          class="input-textarea"
+          placeholder="请输入检验结果"
+          maxlength="200"
+        />
       </view>
+    </view>
 
-      <view class="popup-footer">
-        <view class="footer-btn cancel-btn" @click="handleCancel">
-          <text class="footer-btn-text">取消</text>
-        </view>
-        <view class="footer-btn confirm-btn" @click="handleConfirm">
-          <text class="footer-btn-text confirm-text">确定</text>
-        </view>
+    <view class="popup-footer">
+      <view class="footer-btn cancel-btn" @click="handleCancel">
+        <text class="footer-btn-text">取消</text>
+      </view>
+      <view class="footer-btn confirm-btn" @click="handleConfirm">
+        <text class="footer-btn-text confirm-text">确定</text>
       </view>
     </view>
-  </view>
+  </wd-popup>
 </template>
 
 <script lang="ts" setup>
@@ -45,37 +58,25 @@ const props = withDefaults(defineProps<Props>(), {
 
 const emit = defineEmits<{
   hide: []
-  confirm: [value: string]
+  confirm: [conclusionData: any]
 }>()
 
 const inputValue = ref('')
+const selectedConclusion = ref('')
+const showPopup = ref(false)
 
 const isConclusion = computed(() => props.fieldKey === 'reportConclusion')
 
-const conclusionOptions = ['', '符合', '不符合']
+const conclusionOptions = [
+  { label: '无', value: '' },
+  { label: '符合', value: '符合' },
+  { label: '不符合', value: '不符合' },
+]
 
 onMounted(() => {
-  if (isConclusion.value) {
-    showConclusionActionSheet()
-  }
+  showPopup.value = true
 })
 
-const showConclusionActionSheet = () => {
-  const itemList = ['空白', '符合', '不符合']
-
-  uni.showActionSheet({
-    itemList,
-    success: (res) => {
-      const selectedValue = conclusionOptions[res.tapIndex]
-      emit('confirm', selectedValue)
-      emit('hide')
-    },
-    fail: () => {
-      emit('hide')
-    },
-  })
-}
-
 watch(
   () => props.checkItem,
   (newItem) => {
@@ -86,85 +87,102 @@ watch(
             ? JSON.parse(newItem.recordJson)
             : newItem.recordJson
         inputValue.value = recordJson[props.fieldKey] || ''
+        selectedConclusion.value = recordJson[props.fieldKey] || ''
       } catch {
         inputValue.value = ''
+        selectedConclusion.value = ''
       }
     } else {
       inputValue.value = ''
+      selectedConclusion.value = ''
     }
   },
   { immediate: true },
 )
 
-const handleConfirm = () => {
-  if (!inputValue.value.trim()) {
-    uni.showToast({
-      title: '请输入检验结果',
-      icon: 'none',
-    })
-    return
-  }
-  emit('confirm', inputValue.value.trim())
+const handleSelectConclusion = (value: string) => {
+  selectedConclusion.value = value
 }
 
-const handleCancel = () => {
+const handleClose = () => {
   emit('hide')
 }
 
-const preventClose = () => {}
-</script>
-
-<style lang="scss" scoped>
-.popup-overlay {
-  position: fixed;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  z-index: 9999;
-  display: flex;
-  align-items: flex-end;
-  justify-content: center;
-  background-color: rgba(0, 0, 0, 0.5);
+const handleCancel = () => {
+  showPopup.value = false
+  emit('hide')
 }
 
-.popup-content {
-  width: 100%;
-  overflow: hidden;
-  background-color: #ffffff;
-  border-radius: 16rpx 16rpx 0 0;
+const handleConfirm = () => {
+  if (isConclusion.value) {
+    emit('confirm', {
+      id: props.checkItem?.id,
+      [props.fieldKey]: selectedConclusion.value,
+    })
+  } else {
+    if (!inputValue.value.trim()) {
+      uni.showToast({
+        title: '请输入检验结果',
+        icon: 'none',
+      })
+      return
+    }
+    emit('confirm', {
+      id: props.checkItem?.id,
+      [props.fieldKey]: inputValue.value.trim(),
+    })
+  }
 }
+</script>
 
+<style lang="scss" scoped>
 .popup-header {
   display: flex;
   flex-direction: row;
   align-items: center;
-  justify-content: space-between;
-  padding: 32rpx;
-  border-bottom: 1rpx solid #eeeeee;
+  justify-content: center;
+  padding: 12px;
+  border-bottom: 1px solid #eeeeee;
 }
 
 .popup-title {
-  font-size: 32rpx;
+  font-size: 15px;
   font-weight: 600;
   color: #333333;
 }
 
-.close-btn {
+.popup-body {
+  padding: 12px;
+}
+
+.option-item {
   display: flex;
   align-items: center;
   justify-content: center;
-  width: 48rpx;
-  height: 48rpx;
+  height: 36px;
+  margin-bottom: 8px;
+  background-color: #f7f8fa;
+  border: 1px solid #e8e8e8;
+  border-radius: 4px;
 }
 
-.close-icon {
-  font-size: 32rpx;
-  color: #999999;
+.option-item:last-child {
+  margin-bottom: 0;
 }
 
-.popup-body {
-  padding: 32rpx;
+.option-active {
+  background-color: #ecf5ff;
+  border-color: #4b8cd9;
+}
+
+.option-text {
+  font-size: 13px;
+  color: #333333;
+}
+
+.option-active-text {
+  color: #4b8cd9;
+  font-weight: 500;
 }
 
 .input-section {
@@ -174,14 +192,14 @@ const preventClose = () => {}
 .input-textarea {
   box-sizing: border-box;
   width: 100%;
-  height: 200rpx;
-  padding: 24rpx;
-  font-size: 28rpx;
+  height: 80px;
+  padding: 8px;
+  font-size: 13px;
   color: #333333;
   resize: none;
   background-color: #ffffff;
-  border: 1rpx solid #dddddd;
-  border-radius: 8rpx;
+  border: 1px solid #dddddd;
+  border-radius: 4px;
 }
 
 .input-textarea:focus {
@@ -191,7 +209,7 @@ const preventClose = () => {}
 .popup-footer {
   display: flex;
   flex-direction: row;
-  border-top: 1rpx solid #eeeeee;
+  border-top: 1px solid #eeeeee;
 }
 
 .footer-btn {
@@ -199,8 +217,7 @@ const preventClose = () => {}
   flex: 1;
   align-items: center;
   justify-content: center;
-  height: 96rpx;
-  margin: 0 5%;
+  height: 40px;
 }
 
 .footer-btn:active {
@@ -208,15 +225,11 @@ const preventClose = () => {}
 }
 
 .cancel-btn {
-  border-right: 1rpx solid #eeeeee;
-}
-
-.confirm-btn {
-  border-right: 1rpx solid #eeeeee;
+  border-right: 1px solid #eeeeee;
 }
 
 .footer-btn-text {
-  font-size: 28rpx;
+  font-size: 14px;
   color: #666666;
 }
 

+ 4 - 1
src/pages/equipment/detail/equipmentDetail.vue

@@ -347,7 +347,10 @@ const fetchGetOnlineEquipmentDetail = async () => {
       const equipResp: any = await equipRequestFunc(EquipFuncName.EquipDetail, equipType, { id: equip.equipId })
       const taskInfo = {
         ...result.data,
-        equipment: equipResp.data,
+        equipment: {
+          ...equipResp.data,
+          mainCheckerUser: equipment.mainCheckerUser
+        },
         reportList: filteredReportList,
         otherReportList: reportDic.otherReportList,
         isMainChecker: isMain,

+ 247 - 0
src/pages/securityCheck/detail/index copy.vue

@@ -0,0 +1,247 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationBarTitleText: '安全检查记录详情',
+    navigationStyle: 'custom',
+  },
+}
+</route>
+
+<template>
+  <view class="detail-container">
+    <!-- 导航栏 -->
+    <NavBar>
+      <template #title>
+        <HeadView v-if="renderCenter" title="操作指导书批准详情" :btn-array="btnArray" />
+        <text v-else class="nav-title">安全检查记录详情</text>
+      </template>
+    </NavBar>
+
+    <!-- PDF 预览区域 -->
+    <view class="pdf-container">
+      <view v-if="loading" class="loading-container">
+        <text class="loading-text">加载中...</text>
+      </view>
+      <SpreadPDFViewer
+        ref="spreadPdfViewerRef"
+        :businessConfig="businessConfig"
+        :templateData="templateData"
+        :templateBlob="templateBlob"
+      />
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed, onMounted } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import HeadView from '../components/HeadView.vue'
+import NavBar from '@/components/NavBar/NavBar.vue'
+import SpreadPDFViewer from '@/components/SpreadDesigner/SpreadPDFViewer.vue'
+
+import { useConfigStore } from '@/store/config'
+
+import { buildFileUrl } from '@/utils/index'
+import { requestFunc, SecurityCheckFuncName } from '@/api/ApiRouter/taskOrderSecurityCheck'
+import { getDynamicTbVal } from '@/api/task'
+import { getStandardTemplate } from '@/api'
+
+
+const configStore = useConfigStore()
+const equipType = configStore.getEquipType()
+
+// 路由参数
+const detailItem = ref<any>({})
+const orderId = ref('')
+const unitContact = ref('')
+const unitPhone = ref('')
+const receiverEmail = ref('')
+const templateId = ref('')
+const spreadPdfViewerRef = ref<any>(null)
+
+const templateBlob = ref<any>(null)
+const businessConfig = ref<any>({
+  businessType: 'AQJC',
+  title: '安全检查记录编辑',
+  disableNavigate: true, // 是否禁用跳转,默认不禁用
+
+  ui: {
+    title: '安全检查记录',
+    saveButtonText: '保存',
+    cancelButtonText: '取消',
+    showAdditionalToolbar: true,
+    customButtons: [],
+  },
+})
+const templateData = ref<any>({})
+
+onLoad((options) => {
+  orderId.value = options.orderId || ''
+  unitContact.value = options.unitContact || ''
+  unitPhone.value = options.unitPhone || ''
+  receiverEmail.value = options.receiverEmail || ''
+  templateId.value = options.templateId || ''
+
+  if (options.detailItem) {
+    try {
+      detailItem.value = JSON.parse(decodeURIComponent(options.detailItem))
+    } catch (e) {
+      console.error('解析 detailItem 失败:', e)
+    }
+  }
+})
+
+// 状态
+const loading = ref(false)
+
+// 是否显示操作按钮 (对应 PJ 中的 useMemo 计算)
+const renderCenter = computed(() => {
+  const { id } = detailItem.value || {}
+  if (!id) return false
+  return true
+})
+
+// 操作按钮数组 (与 PJ 版本一致,空数组)
+const btnArray = computed(() => {
+  const { signFilePdf, id } = detailItem.value || {}
+  if (!id) return []
+
+  return [
+    // { value: signFilePdf ? '重新签名' : '签名', color: 'rgb(47,142,255)', click: handleToSign },
+  ]
+})
+
+// 初始化
+const init = async () => {
+  loading.value = true
+  try {
+    // const tplParams = await handleGetTemplate()
+    // await getAppServiceOrderPDF(tplParams)
+    const id = detailItem.value?.id || ''
+    if (!id) return
+    const defaultTemplateResp = await requestFunc(SecurityCheckFuncName.getTemplate, equipType, { orderId: orderId.value })
+    const res = await getStandardTemplate({ id: defaultTemplateResp.data?.templateId })
+    const resData = (res as any).data
+    const dataMap: any = {}
+    const dynamicTbValResp = await getDynamicTbVal({ refId: id })
+    const dynamicTb: any = dynamicTbValResp.data
+    for (let i = 0; i < dynamicTb.dynamicTbValRespVOList.length; i++) {
+      const item = dynamicTb.dynamicTbValRespVOList[i]
+      dataMap[item.colCode] = item.valValue
+    }
+    // 组装templateData
+    templateData.value = {
+      schema: resData.bindingPathSchema ? JSON.parse(resData.bindingPathSchema) : {},
+      data: {
+        ...dataMap,
+        templateId,
+        templateUrl: resData.fileUrl,
+      },
+      pathNameMapping: JSON.parse(resData.bindingPathNameJson),
+      template: templateId,
+      templateUrl: resData.fileUrl,
+    }
+
+    console.log(templateData.value)
+    // 获取 template 文件
+    const fileUri = resData.fileUrl
+    const fileUrl = buildFileUrl(fileUri)
+    const fileBase64 = await spreadPdfViewerRef.value.downloadFileAsBase64(fileUrl)
+    templateBlob.value = fileBase64
+  } catch (error) {
+    console.error('[Page] 初始化失败:', error)
+    uni.showToast({ title: 'PDF 加载失败', icon: 'none' })
+  } finally {
+    loading.value = false
+  }
+}
+
+onMounted(async () => {
+  await init()
+})
+</script>
+
+<style lang="scss" scoped>
+.detail-container {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  background-color: #f5f5f5;
+}
+
+.navigate-view {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  height: 44px;
+  padding: 0 15px;
+  background-color: #fff;
+  border-bottom: 1px solid #eee;
+}
+
+.navigate-left {
+  display: flex;
+  align-items: center;
+  width: 44px;
+}
+
+.back-icon {
+  width: 20px;
+  height: 20px;
+}
+
+.navigate-center {
+  display: flex;
+  flex: 1;
+  justify-content: center;
+}
+
+.navigate-title {
+  font-size: 17px;
+  font-weight: 500;
+  color: #333;
+}
+
+.navigate-right {
+  display: flex;
+  align-items: center;
+  width: 44px;
+}
+
+.nav-title {
+  font-size: 17px;
+  font-weight: 500;
+  color: #333;
+}
+
+.pdf-container {
+  flex: 1;
+  background-color: #f5f5f5;
+}
+/* #ifdef H5 */
+.pdf-viewer-container {
+  box-sizing: border-box;
+  width: 100%;
+  height: 100%;
+  padding: 10px;
+  text-align: center;
+  background-color: #fff;
+}
+/* #endif */
+
+.loading-container,
+.empty-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+}
+
+.loading-text,
+.empty-text {
+  font-size: 14px;
+  color: #999;
+}
+</style>

+ 2 - 11
src/pages/sign/index.vue

@@ -298,12 +298,6 @@ onLoad((options) => {
 const orderReportId = ref('')
 const isSigned = ref('0')
 const getPreviewData = async () => {
-  // orderId --> templateId + refId --> templateBlob 和 templateData
-  /* 
-    orderId: 1fd8970b25933aee9486431bfbc10751
-    templateId: dce2478ef6a153fbd2874b1f2ea33389
-  */
-  // orderId 查询服务单,获取 templateId + refId
   if (!routeType.value) {
     uni.showToast({ title: '必须选择签字文件类型', icon: 'error' })
     return
@@ -600,15 +594,12 @@ const submitConfirm = async () => {
     const result: any = await requestFunc(SignFuncName.SubmitSign, equipType, params)
 
     if (result?.code === 0) {
+      isSigned.value = '1'
       uni.showToast({
         title: routeType.value === 'ZXXX' ? '已自动提交审核' : '签名成功',
         icon: 'success',
       })
-      // setTimeout(() => {
-      //   uni.redirectTo({
-      //     url: `/pages/orderDetail/detail?orderId=${orderId.value}&type=${routeType.value}`,
-      //   })
-      // }, 1500)
+      getPreviewData()
     } else {
       uni.showToast({ title: result?.msg || '签名失败', icon: 'error' })
     }

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

@@ -127,6 +127,7 @@ const getQueryParams = () => {
   const queryData = queryViewRef.value?.getQueryParams() || {}
   const statusData = handleRightRefresh()
   return {
+    taskStatusList: defaultTypeValue.value.taskStatusList,
     ...params,
     ...queryData,
     ...statusData,

+ 13 - 29
src/pages/unitQuery/components/query/QueryView.vue

@@ -5,40 +5,36 @@
   </view>
   <view class="query-form" :style="contentStyle">
     <InputCom
-      ref="nameRef"
+      v-model="params.name"
       title="单位名称:"
       type="name"
       :text-style="{ width: '140px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入单位名称"
-      @change="handleChange('name', $event)"
     />
     <InputCom
-      ref="socialCreditCodeRef"
+      v-model="params.socialCreditCode"
       title="统一社会信用代码:"
       type="socialCreditCode"
       :text-style="{ width: '140px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入统一社会信用代码"
-      @change="handleChange('socialCreditCode', $event)"
     />
     <InputCom
-      ref="contactRef"
+      v-model="params.contact"
       title="默认联系人:"
       type="contact"
       :text-style="{ width: '140px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入默认联系人"
-      @change="handleChange('contact', $event)"
     />
     <InputCom
-      ref="telRef"
+      v-model="params.tel"
       title="默认联系人电话:"
       type="tel"
       :text-style="{ width: '140px', textAlign: 'right' }"
       :style="{ marginLeft: 0 }"
       placeholder="请输入默认联系人电话"
-      @change="handleChange('tel', $event)"
     />
     <view class="form-actions">
       <button class="action-btn reset-btn" @click.stop="handleReset">重置</button>
@@ -53,7 +49,7 @@ import { InputCom } from '@/components/QueryViewItem'
 import iconMap from '@/utils/imagesMap'
 
 const emit = defineEmits<{
-  queryAction: [params: Record<string, any>]
+  queryAction: []
   resetAction: []
 }>()
 
@@ -73,39 +69,27 @@ const contentStyle = computed(() => ({
   transition: 'all 0.5s',
 }))
 
-const nameRef = ref<any>(null)
-const socialCreditCodeRef = ref<any>(null)
-const contactRef = ref<any>(null)
-const telRef = ref<any>(null)
-
 const toggleFilter = () => {
   isOpen.value = !isOpen.value
 }
 
-const handleChange = (propName: string, value: string) => {
-  params[propName as keyof typeof params] = value
-}
-
 const handleReset = () => {
-  params.name = ''
-  params.socialCreditCode = ''
-  params.contact = ''
-  params.tel = ''
-
-  nameRef.value?.reset()
-  socialCreditCodeRef.value?.reset()
-  contactRef.value?.reset()
-  telRef.value?.reset()
-
+  Object.assign(params, {
+    name: '',
+    socialCreditCode: '',
+    contact: '',
+    tel: '',
+  })
   emit('resetAction')
 }
 
 const handleQuery = () => {
-  emit('queryAction', { ...params })
+  emit('queryAction')
 }
 
 defineExpose({
   refresh: () => handleQuery(),
+  getParams: () => ({ ...params }),
 })
 </script>
 

+ 4 - 3
src/pages/unitQuery/unitQuery.vue

@@ -87,13 +87,14 @@ const params = reactive({
   pageSize: 10,
 })
 
-const fetchList = async (refresh = false, queryParams?: Record<string, any>) => {
+const fetchList = async (refresh = false) => {
   if (loading.value) return
 
   params.pageNo = refresh ? 1 : params.pageNo + 1
 
   loading.value = true
   try {
+    const queryParams = queryViewRef.value?.getParams() || {}
     const queryData = {
       ...params,
       ...queryParams,
@@ -126,9 +127,9 @@ const refreshList = () => {
   fetchList(true)
 }
 
-const handleQueryAction = (queryParams: Record<string, any>) => {
+const handleQueryAction = () => {
   params.pageNo = 1
-  fetchList(true, queryParams)
+  fetchList(true)
 }
 
 const handleResetAction = () => {

+ 6 - 39
src/pages/unitQuery/unitQueryDetail.vue

@@ -40,19 +40,6 @@
           </view>
         </view>
       </view>
-
-      <!-- 财务信息 -->
-      <view class="section">
-        <view class="section-header">
-          <text class="section-title">财务信息</text>
-        </view>
-        <view class="info-list">
-          <view v-for="item in unitFinancialInfos" :key="item.prop" class="info-item">
-            <text class="info-label">{{ item.label }}:</text>
-            <text class="info-value">{{ item.value || '--' }}</text>
-          </view>
-        </view>
-      </view>
     </scroll-view>
   </view>
 </template>
@@ -74,7 +61,6 @@ interface InfoItem {
 
 const unitBasicInfos = ref<InfoItem[]>([])
 const unitContractInfos = ref<InfoItem[]>([])
-const unitFinancialInfos = ref<InfoItem[]>([])
 
 let id = ''
 
@@ -218,36 +204,20 @@ const initContractInfos = () => {
       prop: 'paymentPerson',
       label: '缴费人员',
       value: ''
-    }
-  ]
-}
-
-// 初始化财务信息字段
-const initFinancialInfos = () => {
-  return [
-    {
-      prop: 'financeContact',
-      label: '财务联系人',
-      value: ''
-    },
-    {
-      prop: 'financeMobile',
-      label: '财务联系人电话',
-      value: ''
     },
     {
-      prop: 'financeEmail',
-      label: '财务邮箱',
+      prop: 'paymentPersonTel',
+      label: '缴费人员电话',
       value: ''
     },
     {
-      prop: 'invoiceTitle',
-      label: '发票抬头',
+      prop: 'billingPerson',
+      label: '开票人员',
       value: ''
     },
     {
-      prop: 'taxNo',
-      label: '税号',
+      prop: 'billingPersonTel',
+      label: '开票人员电话',
       value: ''
     }
   ]
@@ -280,15 +250,12 @@ const getUnitDetail = async () => {
     
     const basicFields = initBasicInfos()
     const contractFields = initContractInfos()
-    const financialFields = initFinancialInfos()
     
     fillFieldValues(basicFields, data)
     fillFieldValues(contractFields, data)
-    fillFieldValues(financialFields, data)
     
     unitBasicInfos.value = basicFields
     unitContractInfos.value = contractFields
-    unitFinancialInfos.value = financialFields
   } catch (error) {
     console.error('获取单位详情失败:', error)
     uni.showToast({ title: '加载失败', icon: 'error' })

+ 1 - 0
src/types/uni-pages.d.ts

@@ -41,6 +41,7 @@ interface NavigateToOptions {
        "/pages/pendingVerification/list/PendingVerificationList" |
        "/pages/pendingVerification/list/PipeItem" |
        "/pages/pendingVerification/preViewReport/index" |
+       "/pages/securityCheck/detail/index copy" |
        "/pages/securityCheck/detail/index" |
        "/pages/workInstructionAudit/list/WorkInstructionAuditList" |
        "/pages/workInstructionAudit/preViewReport/index";