<!-- 在用软件详情 --> <script name="SoftwareInfoDetail" lang="ts" setup> import { ElLoading, ElMessage, ElMessageBox, dayjs } from 'element-plus' import type { IReviewReport } from '../review/software-review' import type { IRevisionApply } from '../revision/software-revision' import type { IChangeLog, ISoftwareInfo } from './software-info' import type { TableColumn } from '@/components/NormalTable/table_interface' import { UploadFile } from '@/api/file' import { detailSoftware, getSoftwareChangeLog, updateSoftwareInfo } from '@/api/resource/software' import { getPhotoUrl } from '@/api/system/tool' import type { deptType, dictType } from '@/global' import { getDictByCode } from '@/api/system/dict' // 从路由中传过来的参数 const type = ref<string>('') const id = ref<string>('') const route = useRoute() const router = useRouter() const title = ref('') const radioItems = ref([ { name: '基本信息', value: 'software-basic' }, { name: '变更记录', value: 'software-changeLog' }, ]) const current = ref('') const currentLabel = ref('') const basicFormRef = ref() const softwareInfo = ref<ISoftwareInfo>({ id: '', reportNo: '', // 软件评审报告 softwareName: '', // 软件名称 softwareVersion: '', // 版本号 softwareDocument: [], // 软件文档 functionDocument: [], // 功能文档 sourceCode: [], // 源代码 dataValidationRecord: [], // 数据验证记录 remark: '', // 备注 createTime: '', // 创建时间 createUserName: '', // 创建人 }) const reviewList = ref<Array<IReviewReport>>([]) const changeList = ref<Array<IChangeLog>>([]) const reviewColumn = ref<TableColumn[]>([ // 评审报告 { text: '评审报告编号', value: 'reportNo', align: 'center', width: '180' }, { text: '评审类别', value: 'reviewTypeStr', align: 'center', width: '180' }, { text: '修订申请编号', value: 'revisionApplyNo', align: 'center', width: '180' }, { text: '软件名称', value: 'softwareName', align: 'center' }, { text: '版本号', value: 'softwareVersion', align: 'center', width: '120' }, { text: '申请部门', value: 'createDept', align: 'center', width: '160' }, { text: '申请人', value: 'createUserName', align: 'center', width: '120' }, { text: '申请时间', value: 'createTime', align: 'center', width: '180' }, ]) const changeColumn = ref<TableColumn[]>([ // { text: '变更类型', value: 'changeType', align: 'center', width: '160' }, { text: '创建人', value: 'createUserName', align: 'center', width: '120' }, { text: '更新时间', value: 'updateTime', align: 'center', width: '180' }, { text: '软件名称', value: 'softwareName', align: 'center' }, { text: '版本号', value: 'softwareVersion', align: 'center', width: '120' }, { text: '软件文档', value: 'softwareDocument', align: 'center', width: '180' }, { text: '功能文档', value: 'functionDocument', align: 'center', width: '180' }, { text: '源代码', value: 'sourceCode', align: 'center', width: '180' }, { text: '数据验证记录', value: 'dataValidationRecord', align: 'center', width: '180' }, ]) const softwareInfoRules = ref({ softwareDocument: [{ required: true, message: '软件文档不能为空', trigger: ['blur', 'change'] }], functionDocument: [{ required: true, message: '功能文档不能为空', trigger: ['blur', 'change'] }], sourceCode: [{ required: true, message: '源代码不能为空', trigger: ['blur', 'change'] }], dataValidationRecord: [{ required: true, message: '数据验证记录不能为空', trigger: ['blur', 'change'] }], }) // 表单验证规则 // --------------------------------------字典----------------------------------------------- const useDeptList = ref<deptType[]>([]) // 部门 const labDeptList = ref<deptType[]>([]) // 实验室 // 查询字典 const getDict = () => { // 实验室 getDictByCode('bizLabCode').then((response) => { labDeptList.value = response.data }) // 部门 getDictByCode('bizGroupCode').then((response) => { useDeptList.value = response.data }) } getDict() // ------------------------------------------------------------------------------------- // 详情页的各个tab切换操作 const radioChangeHandler = (newVal: string | number | boolean) => { const radioTarget = radioItems.value.filter(item => item.name === newVal) if (radioTarget.length > 0) { currentLabel.value = radioTarget[0].name current.value = radioTarget[0].value } else { currentLabel.value = radioItems.value[0].name current.value = radioItems.value[0].value } } const resetForm = () => { sessionStorage.removeItem('sealInfo') // 返回列表时 将缓存中的数据删除 router.go(-1) } // 编辑 const updateById = () => { const params = { ...softwareInfo.value, updateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'), softwareDocument: Array.isArray(softwareInfo.value.softwareDocument) ? softwareInfo.value.softwareDocument?.join(',') : softwareInfo.value.softwareDocument, functionDocument: Array.isArray(softwareInfo.value.functionDocument) ? softwareInfo.value.functionDocument?.join(',') : softwareInfo.value.functionDocument, sourceCode: Array.isArray(softwareInfo.value.sourceCode) ? softwareInfo.value.sourceCode?.join(',') : softwareInfo.value.sourceCode, dataValidationRecord: Array.isArray(softwareInfo.value.dataValidationRecord) ? softwareInfo.value.dataValidationRecord?.join(',') : softwareInfo.value.dataValidationRecord, } const loading = ElLoading.service({ lock: true, text: '加载中...', background: 'rgba(255, 255, 255, 0.6)', }) updateSoftwareInfo(params).then((res) => { if (res.code === 200) { ElMessage.success('保存成功') } else { ElMessage.error(`保存失败:${res.message}`) } loading.close() }) } // 查询详情 const detail = (softwareId: string) => { const loading = ElLoading.service({ lock: true, text: '加载中...', background: 'rgba(255, 255, 255, 0.6)', }) detailSoftware({ id: softwareId }).then((res) => { if (res.code === 200) { softwareInfo.value = res.data softwareInfo.value.softwareDocument = res.data.softwareDocument.split(',') softwareInfo.value.functionDocument = res.data.functionDocument.split(',') softwareInfo.value.sourceCode = res.data.sourceCode.split(',') softwareInfo.value.dataValidationRecord = res.data.dataValidationRecord.split(',') } loading.close() }) } // 查询变更记录 const getChangeLogs = (softwareId: string) => { getSoftwareChangeLog({ id: softwareId }).then((res) => { if (res.code === 200) { reviewList.value = res.data.reviewReportList.map((item: { revisionApplyNo: string }) => { return { ...item, revisionApplyNo: item.revisionApplyNo ? item.revisionApplyNo : '/', } }) // 评审报告 changeList.value = res.data.changeLogList // 变更记录 } }) } // 保存 const saveForm = async () => { if (!basicFormRef) { return } await basicFormRef.value.validate((valid: boolean, fields: any) => { if (valid === true) { ElMessageBox.confirm( '确认保存吗?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }, ).then(() => { updateById() }) } }) } const initDialog = (params: any) => { // 从路由中获取参数 type.value = params.type id.value = params.id !== undefined ? params.id : '' // 默认显示第一个tab内容 current.value = radioItems.value[0].value currentLabel.value = radioItems.value[0].name switch (params.type) { case 'update': title.value = '软件一览表(编辑)' id.value = params.id radioItems.value = [ { name: '基本信息', value: 'software-basic' }, ] detail(id.value) break case 'detail': title.value = '软件一览表(详情)' id.value = params.id radioItems.value = [ { name: '基本信息', value: 'software-basic' }, { name: '变更记录', value: 'software-changeLog' }, ] detail(id.value) getChangeLogs(id.value) break default: title.value = '' break } } // -------------------------------------------文件上传-------------------------------------- // 文件上传 const softwareDocumentRef = ref() // 软件文档 const functionDocumentRef = ref() // 功能文档 const sourceCodeRef = ref() // 源代码 const dataValidationRecordRef = ref() // 数据验证记录 // 软件文档,在 Input 值改变时触发 const softwareDocumentChange = (event: any) => { if (event.target.files[0].type !== 'application/msword' && event.target.files[0].type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' && event.target.files[0].type !== 'application/pdf') { ElMessage.warning('请上传doc、docx、pdf文件格式') return } UploadFileFn(event, 'softwareDocument', '文件上传成功') } // 功能文档 const functionDocumentChange = (event: any) => { if (event.target.files[0].type !== 'application/msword' && event.target.files[0].type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' && event.target.files[0].type !== 'application/pdf') { ElMessage.warning('请上传doc、docx、pdf文件格式') return } UploadFileFn(event, 'functionDocument', '文件上传成功') } // 功能文档 const dataValidationRecordChange = (event: any) => { if (event.target.files[0].type !== 'application/msword' && event.target.files[0].type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' && event.target.files[0].type !== 'application/pdf') { ElMessage.warning('请上传doc、docx、pdf文件格式') return } UploadFileFn(event, 'dataValidationRecord', '文件上传成功') } // 源代码 const sourceCodeChange = (event: any) => { if (event.target.files[0].name.substring(event.target.files[0].name.lastIndexOf('.')) !== '.zip' && event.target.files[0].name.substring(event.target.files[0].name.lastIndexOf('.')) !== '.rar') { ElMessage.warning('请上传zip、rar文件格式') return } UploadFileFn(event, 'sourceCode', '文件上传成功') } // 上传文件操作 function UploadFileFn(event: any, prop: string, message: string) { if (event.target.files?.length !== 0) { // 创建formdata对象 const fd = new FormData() fd.append('multipartFile', event.target.files[0]) const loading = ElLoading.service({ lock: true, background: 'rgba(255, 255, 255, 0.8)', }) UploadFile(fd).then((res) => { if (res.code === 200) { softwareInfo.value[prop]!.push(res.data[0]) event.target.value = null // 重置当前验证 ElMessage.success(message) loading.close() } else { ElMessage.error(res.message) loading.close() } }) } } const upload = (fileRef: any) => { fileRef.click() } /** * 删除附件 * @param index 索引 * @param type 操作的类型 */ const delFile = (index: number, type: 'softwareDocument' | 'functionDocument' | 'sourceCode' | 'dataValidationRecord') => { switch (type) { case 'softwareDocument':// 软件文档 softwareInfo.value.softwareDocument.splice(index, 1) break case 'functionDocument': // 功能文档 softwareInfo.value.functionDocument.splice(index, 1) break case 'sourceCode': // 源代码 softwareInfo.value.sourceCode.splice(index, 1) break case 'dataValidationRecord':// 数据验证记录 softwareInfo.value.dataValidationRecord.splice(index, 1) break } } // 变更记录点击软件评审报告 const clickReportNo = (row: any) => { router.push({ query: { type: 'review', id: row.id, status: '0', }, path: '/software/review/approved', }) } // -------------------------------------字典----------------------------------------------------------------- const labCodeDict = ref<dictType[]>([]) const groupCodeDict = ref<dictType[]>([]) const getLabCodeDict = async () => { // 先从缓存中获取 if (sessionStorage.getItem('bizLabCode') === null || sessionStorage.getItem('bizLabCode') === undefined) { // 缓存中没有则调用接口查询 await getDictByCode('bizLabCode').then((res) => { if (res.code === 200) { labCodeDict.value = res.data } }) } else { labCodeDict.value = JSON.parse(sessionStorage.getItem('bizLabCode')!) } } const getGroupCodeDict = async () => { // 先从缓存中获取 if (sessionStorage.getItem('bizGroupCode') === null || sessionStorage.getItem('bizLabbizGroupCodeCode') === undefined) { // 缓存中没有则调用接口查询 await getDictByCode('bizGroupCode').then((res) => { if (res.code === 200) { groupCodeDict.value = res.data } }) } else { groupCodeDict.value = JSON.parse(sessionStorage.getItem('bizGroupCode')!) } } const initDict = async () => { await getLabCodeDict() await getGroupCodeDict() } // ------------------------------------------------------------------------------------------------- onMounted(async () => { initDict() initDialog(route.query) }) </script> <template> <app-container> <detail-page :title="`${title}`"> <template #btns> <el-button v-if="type !== 'detail'" type="primary" @click="saveForm()"> 保存 </el-button> <el-button type="info" @click="resetForm()"> 关闭 </el-button> </template> <el-radio-group v-model="currentLabel" @change="radioChangeHandler"> <el-radio-button v-for="item in radioItems" :key="item.value" :label="item.name" :disabled="id === ''" /> </el-radio-group> </detail-page> <detail-block v-if="current === 'software-basic'"> <el-form ref="basicFormRef" :model="softwareInfo" :rules="softwareInfoRules" label-position="right" label-width="110px" border stripe> <el-row :gutter="24"> <el-col :span="6"> <el-form-item label="实验室" prop="labCode"> <el-select v-model="softwareInfo.labCode" placeholder="请选择实验室" disabled style="width: 100%;"> <el-option v-for="dict in labCodeDict" :key="dict.id" :label="dict.name" :value="dict.value" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="部门" prop="groupCode"> <el-select v-model="softwareInfo.groupCode" placeholder="请选择部门" disabled style="width: 100%;"> <el-option v-for="dict in groupCodeDict" :key="dict.id" :label="dict.name" :value="dict.value" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="软件评审报告"> <el-input v-model="softwareInfo.reportNo" :disabled="true" /> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="软件名称"> <el-input v-model="softwareInfo.softwareName" :disabled="true" /> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="版本号"> <el-input v-model="softwareInfo.softwareVersion" :disabled="true" /> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="创建人"> <el-input v-model="softwareInfo.createUserName" :disabled="true" /> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="软件文档:" prop="softwareDocument"> <el-button v-if="type !== 'detail'" id="file" type="primary" :disabled="type === 'detail'" :style="{ 'margin-right': '20px' }" @click="upload(softwareDocumentRef)"> {{ !softwareInfo.softwareDocument.length ? '上传' : '上传' }} </el-button> <div v-for="(item, index) in softwareInfo.softwareDocument" :key="index" style="display: flex;"> <show-photo :minio-file-name="item"> <span v-if="type !== 'detail' && item" class="photo-close" @click="delFile(index, 'softwareDocument')">×</span> </show-photo> </div> <span v-if="type === 'detail' && !softwareInfo.softwareDocument.length">无</span> <input v-show="type === ''" ref="softwareDocumentRef" type="file" name="upload" multiple @change="softwareDocumentChange"> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="功能文档:" prop="functionDocument"> <el-button v-if="type !== 'detail'" id="file" type="primary" :disabled="type === 'detail'" :style="{ 'margin-right': '20px' }" @click="upload(functionDocumentRef)"> {{ !softwareInfo.functionDocument.length ? '上传' : '上传' }} </el-button> <div v-for="(item, index) in softwareInfo.functionDocument" :key="index" style="display: flex;"> <show-photo :minio-file-name="item"> <span v-if="type !== 'detail' && item" class="photo-close" @click="delFile(index, 'functionDocument')">×</span> </show-photo> </div> <span v-if="type === 'detail' && !softwareInfo.functionDocument.length">无</span> <input v-show="type === ''" ref="functionDocumentRef" type="file" name="upload" multiple @change="functionDocumentChange"> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="源代码:" prop="sourceCode"> <el-button v-if="type !== 'detail'" id="file" type="primary" :disabled="type === 'detail'" :style="{ 'margin-right': '20px' }" @click="upload(sourceCodeRef)"> {{ !softwareInfo.sourceCode.length ? '上传' : '上传' }} </el-button> <div v-for="(item, index) in softwareInfo.sourceCode" :key="index" style="display: flex;"> <show-photo :minio-file-name="item"> <span v-if="type !== 'detail' && item" class="photo-close" @click="delFile(index, 'sourceCode')">×</span> </show-photo> </div> <span v-if="type === 'detail' && !softwareInfo.sourceCode.length">无</span> <input v-show="type === ''" ref="sourceCodeRef" type="file" name="upload" multiple @change="sourceCodeChange"> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="数据验证记录:" prop="dataValidationRecord"> <el-button v-if="type !== 'detail'" id="file" type="primary" :disabled="type === 'detail'" :style="{ 'margin-right': '20px' }" @click="upload(dataValidationRecordRef)"> {{ !softwareInfo.dataValidationRecord.length ? '上传' : '上传' }} </el-button> <div v-for="(item, index) in softwareInfo.dataValidationRecord" :key="index" style="display: flex;"> <show-photo :minio-file-name="item"> <span v-if="type !== 'detail' && item" class="photo-close" @click="delFile(index, 'dataValidationRecord')">×</span> </show-photo> </div> <span v-if="type === 'detail' && !softwareInfo.dataValidationRecord.length">无</span> <input v-show="type === ''" ref="dataValidationRecordRef" type="file" name="upload" multiple @change="dataValidationRecordChange"> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="12"> <el-form-item label="备注"> <el-input v-model="softwareInfo.remark" :disabled="type === 'detail'" type="textarea" autosize /> </el-form-item> </el-col> </el-row> </el-form> </detail-block> <detail-block v-if="current === 'software-changeLog' && type === 'detail'"> <table-container title="评审报告"> <!-- 表格区域 --> <el-table :data="reviewList" :columns="reviewColumn" border stripe> <el-table-column align="center" label="序号" width="55" type="index" /> <el-table-column v-for="col in reviewColumn" :key="col.value" show-overflow-tooltip :label="col.text" :align="col.align" :width="col.width"> <template #default="scope"> <span v-if="col.value === 'reportNo'" class="link" @click="clickReportNo(scope.row)">{{ scope.row[col.value] }}</span> <span v-else> {{ scope.row[col.value] }} </span> </template> </el-table-column> </el-table> </table-container> </detail-block> <detail-block v-if="current === 'software-changeLog' && type === 'detail'"> <table-container title="历史变更记录"> <el-table :data="changeList" :columns="changeColumn" border stripe> <el-table-column align="center" label="序号" width="55" type="index" /> <el-table-column v-for="col in changeColumn" :key="col.value" show-overflow-tooltip :label="col.text" :align="col.align" :width="col.width"> <template #default="scope"> {{ scope.row[col.value] }} </template> </el-table-column> </el-table> </table-container> </detail-block> </app-container> </template> <style lang="scss" scoped> .link { color: #5da0ff; text-decoration: underline; cursor: pointer; margin-right: 8px; } </style>