Newer
Older
xc-business-system / src / views / resource / software / info / detail.vue
<!-- 在用软件详情 -->
<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>