|
|
@@ -0,0 +1,453 @@
|
|
|
+<template>
|
|
|
+ <Dialog :title="dialogTitle" v-model="dialogVisible" width="50%">
|
|
|
+ <el-form
|
|
|
+ ref="formRef"
|
|
|
+ :model="formData"
|
|
|
+ :rules="formRules"
|
|
|
+ label-width="auto"
|
|
|
+ v-loading="formLoading">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="关联物资" prop="emergencyId">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.emergencyName"
|
|
|
+ disabled
|
|
|
+ placeholder="请选择关联物资"
|
|
|
+ style="width: 100%">
|
|
|
+ <template #append>
|
|
|
+ <el-link @click="selectDevice" :underline="false">选择</el-link>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="证书编号" prop="certNo">
|
|
|
+ <el-input v-model="formData.certNo" placeholder="请输入证书编号" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="检测单位" prop="certOrg">
|
|
|
+ <el-input v-model="formData.certOrg" placeholder="请输入检测单位" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="检测日期" prop="certTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="formData.certTime"
|
|
|
+ type="date"
|
|
|
+ value-format="x"
|
|
|
+ placeholder="请选择检测日期"
|
|
|
+ style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="有效期至" prop="certExpire">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="formData.certExpire"
|
|
|
+ type="date"
|
|
|
+ value-format="x"
|
|
|
+ placeholder="请选择有效期至"
|
|
|
+ style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- <el-col :span="12">
|
|
|
+ <el-form-item label="部门" prop="deptId">
|
|
|
+ <el-tree-select
|
|
|
+ v-model="formData.deptId"
|
|
|
+ :data="deptList"
|
|
|
+ :props="defaultProps"
|
|
|
+ node-key="id"
|
|
|
+ filterable
|
|
|
+ :check-strictly="deptType !== '3'"
|
|
|
+ clearable
|
|
|
+ placeholder="请选择部门"
|
|
|
+ style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col> -->
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="附件" prop="file">
|
|
|
+ <UploadFile
|
|
|
+ v-model="formData.file"
|
|
|
+ :file-type="['doc', 'docx', 'pdf', 'jpg', 'png', 'jpeg', 'xls', 'xlsx']"
|
|
|
+ :limit="3"
|
|
|
+ :file-size="100"
|
|
|
+ class="min-w-80px" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="备注" prop="remark">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.remark"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入备注" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
|
|
+ <el-button @click="dialogVisible = false">取 消</el-button>
|
|
|
+ </template>
|
|
|
+ </Dialog>
|
|
|
+
|
|
|
+ <Dialog title="选择设备" v-model="deviceDialogVisible" width="70%">
|
|
|
+ <ContentWrap>
|
|
|
+ <el-form class="-mb-15px" :model="deviceQueryParams" :inline="true">
|
|
|
+ <el-form-item label="物资编号" prop="emergencyCode">
|
|
|
+ <el-input
|
|
|
+ v-model="deviceQueryParams.emergencyCode"
|
|
|
+ placeholder="请输入物资编号"
|
|
|
+ clearable
|
|
|
+ class="!w-150px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="物资名称" prop="emergencyName">
|
|
|
+ <el-input
|
|
|
+ v-model="deviceQueryParams.emergencyName"
|
|
|
+ placeholder="请输入设备名称"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleDeviceQuery"
|
|
|
+ class="!w-150px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="责任人" prop="dutyPerson">
|
|
|
+ <el-input
|
|
|
+ v-model="deviceQueryParams.dutyPerson"
|
|
|
+ placeholder="请输入责任人"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleDeviceQuery"
|
|
|
+ class="!w-150px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button @click="handleDeviceQuery">
|
|
|
+ <Icon icon="ep:search" class="mr-5px" /> {{ t('devicePerson.search') }}
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="resetDeviceQuery">
|
|
|
+ <Icon icon="ep:refresh" class="mr-5px" /> {{ t('devicePerson.reset') }}
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <div class="pb-10">
|
|
|
+ <zm-table
|
|
|
+ :loading="deviceLoading"
|
|
|
+ :data="deviceList"
|
|
|
+ :stripe="true"
|
|
|
+ :show-overflow-tooltip="true">
|
|
|
+ <zm-table-column width="50" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-radio
|
|
|
+ :model-value="selectedDeviceId"
|
|
|
+ :label="scope.row.id"
|
|
|
+ @change="handleDeviceRadioChange(scope.row)">
|
|
|
+
|
|
|
+ </el-radio>
|
|
|
+ </template>
|
|
|
+ </zm-table-column>
|
|
|
+ <zm-table-column :label="t('monitor.serial')" width="70" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.$index + 1 }}
|
|
|
+ </template>
|
|
|
+ </zm-table-column>
|
|
|
+ <zm-table-column label="应急物资编号" align="center" prop="emergencyCode" />
|
|
|
+ <zm-table-column label="应急物资名称" align="center" prop="emergencyName" min-width="140" />
|
|
|
+ <zm-table-column label="应急物资单位" align="center" prop="emergencyUnit" min-width="120" />
|
|
|
+ <zm-table-column label="数量" align="center" prop="emergencyNumber" width="90" />
|
|
|
+ <zm-table-column label="存放位置" align="center" prop="emergencyLocation" min-width="140" />
|
|
|
+ <zm-table-column label="责任人" align="center" prop="dutyPerson" width="100" />
|
|
|
+ <zm-table-column label="联系电话" align="center" prop="mobile" min-width="120" />
|
|
|
+ <!-- <zm-table-column label="部门名称" align="center" prop="deptName" min-width="140" /> -->
|
|
|
+ <zm-table-column
|
|
|
+ label="备注"
|
|
|
+ align="center"
|
|
|
+ prop="remark"
|
|
|
+ min-width="160"
|
|
|
+ show-overflow-tooltip />
|
|
|
+ <zm-table-column label="附件" align="center" prop="file" min-width="90">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button v-if="scope.row.file" link type="primary" @click="viewFile(scope.row.file)">
|
|
|
+ 查看
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </zm-table-column>
|
|
|
+ </zm-table>
|
|
|
+
|
|
|
+ <Pagination
|
|
|
+ :total="deviceTotal"
|
|
|
+ v-model:page="deviceQueryParams.pageNo"
|
|
|
+ v-model:limit="deviceQueryParams.pageSize"
|
|
|
+ @pagination="getDeviceList" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="confirmSelectDevice" type="primary">确 定</el-button>
|
|
|
+ <el-button @click="closeDeviceDialog">取 消</el-button>
|
|
|
+ </template>
|
|
|
+ </Dialog>
|
|
|
+
|
|
|
+ <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 { EmergencyDrillCertApi, EmergencyDrillApi } from '@/api/pms/qhse/index'
|
|
|
+import { defaultProps, handleTree } from '@/utils/tree'
|
|
|
+import { onMounted, reactive, ref } from 'vue'
|
|
|
+import * as DeptApi from '@/api/system/dept'
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
+import { getUserProfile } from '@/api/system/user/profile'
|
|
|
+defineOptions({ name: 'QHSEDeviceCertForm' })
|
|
|
+
|
|
|
+const { t } = useI18n()
|
|
|
+const message = useMessage()
|
|
|
+const userStore = useUserStore()
|
|
|
+const dialogVisible = ref(false)
|
|
|
+const dialogTitle = ref('')
|
|
|
+const formLoading = ref(false)
|
|
|
+const formType = ref('')
|
|
|
+const formRef = ref()
|
|
|
+const deptList = ref<Tree[]>([])
|
|
|
+
|
|
|
+const createDefaultFormData = () => ({
|
|
|
+ id: undefined,
|
|
|
+ emergencyId: undefined,
|
|
|
+ emergencyName: '',
|
|
|
+ deptId: undefined,
|
|
|
+ certNo: '',
|
|
|
+ certOrg: '',
|
|
|
+ certTime: undefined,
|
|
|
+ certExpire: undefined,
|
|
|
+ file: '',
|
|
|
+ remark: ''
|
|
|
+})
|
|
|
+
|
|
|
+const formData = ref(createDefaultFormData())
|
|
|
+
|
|
|
+const formRules = reactive({
|
|
|
+ emergencyId: [{ required: true, message: '关联设备不能为空', trigger: 'blur' }],
|
|
|
+ certNo: [{ required: true, message: '证书编号不能为空', trigger: 'blur' }],
|
|
|
+ certOrg: [{ required: true, message: '检测单位不能为空', trigger: 'blur' }],
|
|
|
+ certTime: [{ required: true, message: '检测日期不能为空', trigger: 'blur' }],
|
|
|
+ certExpire: [{ required: true, message: '有效期不能为空', trigger: 'blur' }],
|
|
|
+ file: [{ required: true, message: '请上传附件', trigger: 'blur' }],
|
|
|
+ deptId: [{ required: true, message: '所在部门不能为空', trigger: 'blur' }]
|
|
|
+})
|
|
|
+
|
|
|
+const emit = defineEmits(['success'])
|
|
|
+
|
|
|
+const resetForm = () => {
|
|
|
+ formData.value = createDefaultFormData()
|
|
|
+ formRef.value?.resetFields()
|
|
|
+}
|
|
|
+
|
|
|
+const open = async (type: string, id?: number) => {
|
|
|
+ dialogVisible.value = true
|
|
|
+ dialogTitle.value = t('action.' + type)
|
|
|
+ formType.value = type
|
|
|
+ resetForm()
|
|
|
+ formData.value.deptId = userStore.getUser.deptId
|
|
|
+ if (id) {
|
|
|
+ formLoading.value = true
|
|
|
+ try {
|
|
|
+ const res = await EmergencyDrillCertApi.getEmergencyDrillCert(id)
|
|
|
+ formData.value = {
|
|
|
+ ...createDefaultFormData(),
|
|
|
+ ...res,
|
|
|
+ certTime: res.certTime ? Number(res.certTime) : undefined,
|
|
|
+ certExpire: res.certExpire ? Number(res.certExpire) : undefined
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+defineExpose({ open })
|
|
|
+
|
|
|
+const submitForm = async () => {
|
|
|
+ await formRef.value.validate()
|
|
|
+ formLoading.value = true
|
|
|
+ try {
|
|
|
+ const data = { ...formData.value } as any
|
|
|
+ if (data.file instanceof Array) {
|
|
|
+ data.file = data.file.join(',')
|
|
|
+ }
|
|
|
+
|
|
|
+ if (formType.value === 'create') {
|
|
|
+ await EmergencyDrillCertApi.createEmergencyDrillCert(data)
|
|
|
+ message.success(t('common.createSuccess'))
|
|
|
+ } else {
|
|
|
+ await EmergencyDrillCertApi.updateEmergencyDrillCert(data)
|
|
|
+ message.success(t('common.updateSuccess'))
|
|
|
+ }
|
|
|
+
|
|
|
+ dialogVisible.value = false
|
|
|
+ emit('success')
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const deviceDialogVisible = ref(false)
|
|
|
+const deviceLoading = ref(false)
|
|
|
+const deviceList = ref<any[]>([])
|
|
|
+const deviceTotal = ref(0)
|
|
|
+const selectedDeviceId = ref<number | undefined>(undefined)
|
|
|
+const selectedDevice = ref<any>(null)
|
|
|
+
|
|
|
+const deviceQueryParams = reactive({
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ emergencyName: undefined as string | undefined,
|
|
|
+ dutyPerson: undefined as string | undefined,
|
|
|
+ emergencyCode: undefined as string | undefined
|
|
|
+})
|
|
|
+
|
|
|
+const getDeviceList = async () => {
|
|
|
+ deviceLoading.value = true
|
|
|
+ try {
|
|
|
+ const data = await EmergencyDrillApi.getEmergencyDrillList(deviceQueryParams)
|
|
|
+ deviceList.value = data.list
|
|
|
+ deviceTotal.value = data.total
|
|
|
+
|
|
|
+ if (selectedDeviceId.value) {
|
|
|
+ selectedDevice.value =
|
|
|
+ deviceList.value.find((item) => item.id === selectedDeviceId.value) || selectedDevice.value
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ deviceLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleDeviceQuery = () => {
|
|
|
+ deviceQueryParams.pageNo = 1
|
|
|
+ getDeviceList()
|
|
|
+}
|
|
|
+
|
|
|
+const resetDeviceQuery = () => {
|
|
|
+ deviceQueryParams.emergencyName = undefined
|
|
|
+ deviceQueryParams.dutyPerson = undefined
|
|
|
+ deviceQueryParams.emergencyCode = undefined
|
|
|
+ handleDeviceQuery()
|
|
|
+}
|
|
|
+
|
|
|
+const handleDeviceRadioChange = (row: any) => {
|
|
|
+ selectedDeviceId.value = row.id
|
|
|
+ selectedDevice.value = row
|
|
|
+}
|
|
|
+
|
|
|
+const selectDevice = () => {
|
|
|
+ deviceDialogVisible.value = true
|
|
|
+ selectedDeviceId.value = formData.value.emergencyId as number | undefined
|
|
|
+ selectedDevice.value =
|
|
|
+ selectedDeviceId.value && formData.value.emergencyName
|
|
|
+ ? {
|
|
|
+ id: formData.value.emergencyId,
|
|
|
+ emergencyName: formData.value.emergencyName,
|
|
|
+ deptId: formData.value.deptId
|
|
|
+ }
|
|
|
+ : null
|
|
|
+ getDeviceList()
|
|
|
+}
|
|
|
+
|
|
|
+const closeDeviceDialog = () => {
|
|
|
+ deviceDialogVisible.value = false
|
|
|
+ selectedDeviceId.value = undefined
|
|
|
+ selectedDevice.value = null
|
|
|
+}
|
|
|
+
|
|
|
+const confirmSelectDevice = () => {
|
|
|
+ if (!selectedDevice.value) {
|
|
|
+ message.warning('请先选择一个设备')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ formData.value.emergencyId = selectedDevice.value.id
|
|
|
+ formData.value.emergencyName = selectedDevice.value.emergencyName
|
|
|
+ formData.value.deptId = selectedDevice.value.deptId
|
|
|
+ closeDeviceDialog()
|
|
|
+}
|
|
|
+
|
|
|
+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)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+let deptType = ref('')
|
|
|
+onMounted(async () => {
|
|
|
+ deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
|
+
|
|
|
+ const users = await getUserProfile()
|
|
|
+ deptType.value = users.dept.type
|
|
|
+})
|
|
|
+</script>
|