yanghao 3 روز پیش
والد
کامیت
7478b37b4f
2فایلهای تغییر یافته به همراه295 افزوده شده و 1 حذف شده
  1. 284 0
      src/views/pms/qhse/MeasureCertDrawer.vue
  2. 11 1
      src/views/pms/qhse/index.vue

+ 284 - 0
src/views/pms/qhse/MeasureCertDrawer.vue

@@ -0,0 +1,284 @@
+<template>
+  <el-drawer
+    v-model="drawerVisible"
+    title="查看证书"
+    size="60%"
+    direction="rtl"
+    :before-close="handleClose">
+    <div class="cert-drawer-content">
+      <el-form
+        ref="queryFormRef"
+        :model="queryParams"
+        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-4 pt-4 mb-4">
+        <div class="flex items-center gap-4 flex-wrap">
+          <!-- <el-form-item label="仪器名称" prop="measureName">
+            <el-input
+              v-model="queryParams.measureName"
+              placeholder="请输入仪器名称"
+              clearable
+              class="!w-150px" />
+          </el-form-item> -->
+          <el-form-item label="证书编码" prop="measureCertNo">
+            <el-input
+              v-model="queryParams.measureCertNo"
+              placeholder="请输入证书编码"
+              clearable
+              class="!w-180px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button @click="handleQuery">
+              <Icon icon="ep:search" class="mr-5px" /> 搜索
+            </el-button>
+            <el-button @click="resetQuery">
+              <Icon icon="ep:refresh" class="mr-5px" /> 重置
+            </el-button>
+          </el-form-item>
+        </div>
+      </el-form>
+
+      <div class="cert-table-wrapper" v-loading="loading">
+        <el-auto-resizer>
+          <template #default="{ width, height }">
+            <zm-table
+              :data="list"
+              :width="width"
+              :height="height"
+              :max-height="height"
+              :show-overflow-tooltip="true">
+              <zm-table-column :label="t('monitor.serial')" width="70" align="center" fixed="left">
+                <template #default="scope">
+                  {{ scope.$index + 1 }}
+                </template>
+              </zm-table-column>
+              <zm-table-column
+                label="仪器名称"
+                align="center"
+                prop="measureName"
+                min-width="160"
+                fixed="left" />
+              <zm-table-column
+                label="证书编码"
+                align="center"
+                prop="measureCertNo"
+                min-width="140" />
+              <zm-table-column label="检测日期" align="center" prop="detectDate" width="140">
+                <template #default="scope">
+                  <span class="iot-md-date">{{ formatDateCorrectly(scope.row.detectDate) }}</span>
+                </template>
+              </zm-table-column>
+              <zm-table-column label="检测机构" align="center" prop="detectOrg" min-width="160" />
+              <zm-table-column
+                label="检测标准"
+                align="center"
+                prop="detectStandard"
+                min-width="160" />
+              <zm-table-column label="有效期" align="center" prop="validityPeriod" width="140">
+                <template #default="scope">
+                  <span class="iot-md-date">{{
+                    formatDateCorrectly(scope.row.validityPeriod)
+                  }}</span>
+                </template>
+              </zm-table-column>
+              <zm-table-column label="附件" fixed="right" align="center" prop="file" width="90">
+                <template #default="scope">
+                  <el-button
+                    v-if="scope.row.file"
+                    link
+                    type="primary"
+                    @click="viewFile(scope.row.file)">
+                    查看
+                  </el-button>
+                  <span v-else class="text-[#999ca1]">暂无</span>
+                </template>
+              </zm-table-column>
+            </zm-table>
+          </template>
+        </el-auto-resizer>
+      </div>
+
+      <div class="h-8 mt-2 flex items-center justify-end">
+        <Pagination
+          :total="total"
+          v-model:page="queryParams.pageNo"
+          v-model:limit="queryParams.pageSize"
+          @pagination="getList" />
+      </div>
+    </div>
+  </el-drawer>
+
+  <Dialog v-model="dialogFileView" title="附件" width="500">
+    <div
+      v-for="(file, index) in fileList"
+      :key="index"
+      class="flex items-center justify-between mt-5">
+      <span class="file-name-text">{{ extractFileName(file) }}</span>
+      <div>
+        <el-button link type="primary" @click="viewFileInfo(file)">
+          <Icon icon="ep:view" class="mr-2px" />查看
+        </el-button>
+        <el-button link type="primary" @click="handleDownload(file)">
+          <Icon icon="ep:download" class="mr-2px" />下载
+        </el-button>
+      </div>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer mt-10">
+        <el-button type="primary" @click="dialogFileView = false">确认</el-button>
+      </div>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts">
+import { IotMeasureDetectApi } from '@/api/pms/qhse/index'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+import { formatDate } from '@/utils/formatTime'
+
+const { ZmTable, ZmTableColumn } = useTableComponents()
+
+defineOptions({ name: 'MeasureCertDrawer' })
+
+const { t } = useI18n()
+
+const drawerVisible = ref(false)
+const loading = ref(false)
+const list = ref<any[]>([])
+const total = ref(0)
+const currentMeasureId = ref<number | undefined>(undefined)
+
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  measureId: undefined as number | undefined,
+  measureName: undefined as string | undefined,
+  measureCertNo: undefined as string | undefined
+})
+
+const queryFormRef = ref()
+
+const getList = async () => {
+  if (!currentMeasureId.value) return
+
+  loading.value = true
+  try {
+    const data = await IotMeasureDetectApi.getIotMeasureDetectPage(queryParams)
+    list.value = data.list || []
+    total.value = data.total || 0
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+const resetQuery = () => {
+  queryFormRef.value?.resetFields()
+  queryParams.measureId = currentMeasureId.value
+  handleQuery()
+}
+
+const open = async (measureId: number) => {
+  currentMeasureId.value = measureId
+  queryParams.measureId = measureId
+  queryParams.measureName = undefined
+  queryParams.measureCertNo = undefined
+  queryParams.pageNo = 1
+  drawerVisible.value = true
+  await getList()
+}
+
+const handleClose = () => {
+  drawerVisible.value = false
+  currentMeasureId.value = undefined
+  list.value = []
+  total.value = 0
+}
+
+const formatDateCorrectly = (timestamp) => {
+  if (!timestamp) return ''
+
+  let time = Number(timestamp)
+  if (time < 10000000000) {
+    time = time * 1000
+  }
+
+  const date = new Date(time)
+  if (isNaN(date.getTime()) || date.getFullYear() < 1900) {
+    return ''
+  }
+
+  return formatDate(time).substring(0, 10)
+}
+
+const dialogFileView = ref(false)
+const fileList = ref<string[]>([])
+
+const viewFile = (file: string) => {
+  fileList.value = file.split(',')
+  dialogFileView.value = true
+}
+
+const viewFileInfo = (file: string) => {
+  window.open(
+    'http://doc.deepoil.cc:8012/onlinePreview?url=' + encodeURIComponent(Base64.encode(file))
+  )
+}
+
+const extractFileName = (url: string): string => {
+  try {
+    const cleanUrl = url.split('?')[0].split('#')[0]
+    const parts = cleanUrl.split('/')
+    const fileName = parts[parts.length - 1]
+    return decodeURIComponent(fileName) || url
+  } catch {
+    return url
+  }
+}
+
+const handleDownload = async (url: string) => {
+  try {
+    const response = await fetch(url)
+    const blob = await response.blob()
+    const downloadUrl = window.URL.createObjectURL(blob)
+    const link = document.createElement('a')
+    link.href = downloadUrl
+    link.download = url.split('/').pop() || 'file'
+    link.click()
+    URL.revokeObjectURL(downloadUrl)
+  } catch (error) {
+    console.error('下载失败:', error)
+  }
+}
+
+defineExpose({
+  open
+})
+</script>
+
+<style scoped>
+.cert-drawer-content {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  margin-top: -10px;
+}
+
+.cert-table-wrapper {
+  flex: 1;
+  min-height: 0;
+  position: relative;
+}
+
+.file-name-text {
+  flex: 1;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  margin-right: 12px;
+  color: var(--el-text-color-primary);
+}
+</style>

+ 11 - 1
src/views/pms/qhse/index.vue

@@ -167,8 +167,9 @@
                 align="center"
                 fixed="right"
                 action
-                min-width="120px">
+                min-width="180px">
                 <template #default="scope">
+                  <el-button link type="primary" @click="viewCert(scope.row)">查看证书</el-button>
                   <el-button link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
                   <el-button link type="danger" @click="handleDelete(scope.row.id)">
                     删除
@@ -440,11 +441,14 @@
       </div>
     </template>
   </Dialog>
+
+  <MeasureCertDrawer ref="certDrawerRef" />
 </template>
 
 <script setup lang="ts">
 import * as echarts from 'echarts'
 import { IotInstrumentApi, IotMeasureDetectApi } from '@/api/pms/qhse/index'
+import MeasureCertDrawer from './MeasureCertDrawer.vue'
 import DeptTreeSelect from '@/components/DeptTreeSelect/index.vue'
 import { defaultProps } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
@@ -665,6 +669,12 @@ const closeDrawer = () => {
   measureDetectionData.value = []
 }
 
+const certDrawerRef = ref()
+
+const viewCert = (row) => {
+  certDrawerRef.value?.open(row.id)
+}
+
 // 抽屉相关响应式数据
 const drawerVisible = ref(false)
 const drawerLoading = ref(false)