소스 검색

Merge branch 'qhse_person' of shuzhihua/pms-iot-vue into master

yanghao 3 일 전
부모
커밋
dd4aac3f87
3개의 변경된 파일338개의 추가작업 그리고 2개의 파일을 삭제
  1. 312 0
      src/views/pms/qhse/MeasureCertDrawer.vue
  2. 16 1
      src/views/pms/qhse/index.vue
  3. 10 1
      src/views/pms/qhse/iotmeasuredetect/IotMeasureDetectForm.vue

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

@@ -0,0 +1,312 @@
+<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="measureCertNo">
+            <el-input
+              v-model="queryParams.measureCertNo"
+              placeholder="请输入证书编码"
+              clearable
+              class="!w-180px" />
+          </el-form-item>
+          <el-form-item label="检测/校准日期" prop="detectDate">
+            <el-date-picker
+              v-model="queryParams.detectDate"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="!w-200px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" plain @click="handleCreate">
+              <Icon icon="ep:plus" class="mr-5px" /> 新增
+            </el-button>
+            <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="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="detectContent">
+                <template #default="scope">
+                  <div class="detect-content" v-html="scope.row.detectContent"></div>
+                </template>
+              </zm-table-column>
+              <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="校准金额" align="center" prop="detectAmount" />
+              <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>
+
+  <IotMeasureDetectForm ref="formRef" @success="getList" />
+</template>
+
+<script setup lang="ts">
+import { IotMeasureDetectApi } from '@/api/pms/qhse/index'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+import { formatDate } from '@/utils/formatTime'
+import IotMeasureDetectForm from './iotmeasuredetect/IotMeasureDetectForm.vue'
+
+const { ZmTable, ZmTableColumn } = useTableComponents()
+
+defineOptions({ name: 'MeasureCertDrawer' })
+
+const { t } = useI18n()
+
+type MeasureRow = {
+  id: number
+  measureName?: string
+  measureCode?: string
+  deptId?: number | string
+}
+
+const drawerVisible = ref(false)
+const loading = ref(false)
+const list = ref<any[]>([])
+const total = ref(0)
+const currentMeasure = ref<MeasureRow | null>(null)
+
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  measureId: undefined as number | undefined,
+  detectDate: undefined as string | undefined,
+  measureCertNo: undefined as string | undefined
+})
+
+const queryFormRef = ref()
+const formRef = ref()
+
+const getList = async () => {
+  if (!currentMeasure.value?.id) 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 = currentMeasure.value?.id
+  handleQuery()
+}
+
+const handleCreate = () => {
+  if (!currentMeasure.value?.id) return
+
+  formRef.value?.open('create', undefined, {
+    measureId: currentMeasure.value.id,
+    measureName: currentMeasure.value.measureName || currentMeasure.value.measureCode || '',
+    deptId: currentMeasure.value.deptId
+  })
+}
+
+const open = async (measureRow: MeasureRow) => {
+  currentMeasure.value = measureRow
+  queryParams.measureId = measureRow.id
+  queryParams.measureName = undefined
+  queryParams.measureCertNo = undefined
+  queryParams.pageNo = 1
+  drawerVisible.value = true
+  await getList()
+}
+
+const handleClose = () => {
+  drawerVisible.value = false
+  currentMeasure.value = null
+  list.value = []
+  total.value = 0
+}
+
+const formatDateCorrectly = (timestamp: string | number) => {
+  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>

+ 16 - 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,17 @@ const closeDrawer = () => {
   measureDetectionData.value = []
 }
 
+const certDrawerRef = ref()
+
+const viewCert = (row) => {
+  certDrawerRef.value?.open({
+    id: row.id,
+    measureName: row.measureName,
+    measureCode: row.measureCode,
+    deptId: row.deptId
+  })
+}
+
 // 抽屉相关响应式数据
 const drawerVisible = ref(false)
 const drawerLoading = ref(false)

+ 10 - 1
src/views/pms/qhse/iotmeasuredetect/IotMeasureDetectForm.vue

@@ -255,11 +255,20 @@ const formRef = ref() // 表单 Ref
 const measureList = ref([])
 
 /** 打开弹窗 */
-const open = async (type: string, id?: number) => {
+const open = async (
+  type: string,
+  id?: number,
+  presetData?: { measureId?: number | string; measureName?: string; deptId?: number | string }
+) => {
   dialogVisible.value = true
   dialogTitle.value = t('action.' + type)
   formType.value = type
   resetForm()
+  if (type === 'create' && presetData) {
+    formData.value.measureId = presetData.measureId || ''
+    formData.value.measureName = presetData.measureName || ''
+    formData.value.deptId = presetData.deptId
+  }
   // 修改时,设置数据
   if (id) {
     formLoading.value = true