| | |
| | | </div> |
| | | </template> |
| | | |
| | | |
| | | <!-- 用印申请管理 --> |
| | | <div class="tab-content"> |
| | | <el-row :gutter="20" class="mb-20 "> |
| | |
| | | <el-form-item label="申请原因" prop="reason"> |
| | | <el-input v-model="sealForm.reason" type="textarea" :rows="4" placeholder="请详细说明用印原因" /> |
| | | </el-form-item> |
| | | <el-form-item label="审批人" prop="approveUserId"> |
| | | <el-select v-model="sealForm.approveUserId" placeholder="请选择审批人" style="width: 100%" filterable> |
| | | <el-option |
| | | v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="紧急程度" prop="urgency"> |
| | | <el-radio-group v-model="sealForm.urgency"> |
| | | <el-radio label="normal">普通</el-radio> |
| | | <el-radio label="urgent">紧急</el-radio> |
| | | <el-radio label="very-urgent">特急</el-radio> |
| | | <el-radio value="normal">普通</el-radio> |
| | | <el-radio value="urgent">紧急</el-radio> |
| | | <el-radio value="very-urgent">特急</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="附件上传"> |
| | | <el-upload |
| | | multiple |
| | | :show-file-list="false" |
| | | :action="sealUploadUrl" |
| | | name="files" |
| | | :headers="uploadHeaders" |
| | | :limit="10" |
| | | :before-upload="handleBeforeUpload" |
| | | :on-success="handleUploadSuccess" |
| | | :on-error="handleUploadError" |
| | | :on-exceed="handleUploadExceed" |
| | | > |
| | | <el-button type="primary">点击上传附件</el-button> |
| | | </el-upload> |
| | | <el-table v-if="sealForm.attachmentList.length" :data="sealForm.attachmentList" border class="attachment-table"> |
| | | <el-table-column label="附件名称" show-overflow-tooltip> |
| | | <template #default="scope">{{ scope.row.name }}</template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="140" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="previewFile(scope.row)">预览</el-button> |
| | | <el-button link type="danger" size="small" @click="removeAttachment(scope.$index)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form-item> |
| | | </el-form> |
| | | </FormDialog> |
| | |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="申请原因" :span="2">{{ currentSealDetail.reason }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <!-- 附件列表 --> |
| | | <div v-if="currentSealDetail.commonFileList?.length" class="attachment-section"> |
| | | <div class="attachment-title">附件列表:</div> |
| | | <el-table :data="currentSealDetail.commonFileList" border class="attachment-table"> |
| | | <el-table-column label="附件名称" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | {{ scope.row.originalFilename || scope.row.name || scope.row.fileName || '未命名文件' }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column fixed="right" label="操作" width="150" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="previewFile(scope.row)">预览</el-button> |
| | | <el-button link type="primary" size="small" @click="downloadFile(scope.row)">下载</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </FormDialog> |
| | | <!-- 文件预览组件 --> |
| | | <FilePreview ref="filePreviewRef" /> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue' |
| | | import { ref, reactive, onMounted, getCurrentInstance } from 'vue' |
| | | import { useRoute } from 'vue-router' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { listSealApplication, addSealApplication, updateSealApplication } from '@/api/collaborativeApproval/sealManagement.js' |
| | | import { userListNoPageByTenantId } from '@/api/system/user.js' |
| | | import { ElMessage } from 'element-plus' |
| | | import { listSealApplication, addSealApplication } from '@/api/collaborativeApproval/sealManagement.js' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { getToken } from '@/utils/auth' |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import FilePreview from '@/components/filePreview/index.vue' |
| | | |
| | | // 响应式数据 |
| | | // 用印申请相关 |
| | |
| | | const tableLoading = ref(false) |
| | | const showSealDetailDialog = ref(false) |
| | | const currentSealDetail = ref(null) |
| | | const filePreviewRef = ref(null) |
| | | const sealFormRef = ref() |
| | | const userList = ref([]) |
| | | // 附件上传(本地上传,非 minio,返回 [{ name, url }]) |
| | | const sealUploadUrl = import.meta.env.VITE_APP_BASE_API + '/common/uploadsList' |
| | | const uploadHeaders = ref({ Authorization: 'Bearer ' + getToken() }) |
| | | const sealForm = reactive({ |
| | | id: null, |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | status: 'pending', |
| | | attachmentList: [] |
| | | }) |
| | | |
| | | const sealRules = { |
| | | applicationNum: [{ required: true, message: '请输入申请编号', trigger: 'blur' }], |
| | | title: [{ required: true, message: '请输入申请标题', trigger: 'blur' }], |
| | | sealType: [{ required: true, message: '请选择用印类型', trigger: 'change' }], |
| | | reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }], |
| | | approveUserId: [{ required: true, message: '请选择审批人', trigger: 'change' }] |
| | | reason: [{ required: true, message: '请输入申请原因', trigger: 'blur' }] |
| | | } |
| | | |
| | | const sealSearchForm = reactive({ |
| | |
| | | |
| | | // 用印申请表格列配置(需在 getStatusText/getSealTypeText 等之后定义) |
| | | const sealTableColumn = ref([ |
| | | { label: '申请编号', prop: 'applicationNum',}, |
| | | { label: '申请编号', prop: 'applicationNum' }, |
| | | { label: '申请标题', prop: 'title', showOverflowTooltip: true }, |
| | | { label: '申请人', prop: 'createUserName', }, |
| | | { label: '申请人', prop: 'createUserName' }, |
| | | { label: '所属部门', prop: 'department', width: 150 }, |
| | | { |
| | | label: '用印类型', |
| | |
| | | formatData: (v) => getStatusText(v), |
| | | formatType: (v) => getStatusType(v) |
| | | }, |
| | | { label: '审批人', prop: 'approveUserName', width: 120 }, |
| | | { |
| | | dataType: 'action', |
| | | label: '操作', |
| | | width: 250, |
| | | width: 150, |
| | | fixed: 'right', |
| | | align: 'center', |
| | | operation: [ |
| | | { name: '查看', clickFun: (row) => viewSealDetail(row) }, |
| | | { |
| | | name: '审批', |
| | | clickFun: (row) => approveSeal(row), |
| | | showHide: (row) => row.status === 'pending' && row.approveUserId === userStore.id |
| | | }, |
| | | { |
| | | name: '拒绝', |
| | | clickFun: (row) => rejectSeal(row), |
| | | showHide: (row) => row.status === 'pending' && row.approveUserId === userStore.id |
| | | }, |
| | | { |
| | | name: '重新申请', |
| | | clickFun: (row) => reapplySeal(row), |
| | | showHide: (row) => row.status === 'rejected' && row.createUser === userStore.id |
| | | } |
| | | showHide: (row) => row.status === 'rejected' && Number(userStore.id) === row.createUser |
| | | }, |
| | | { name: '详情', clickFun: (row) => viewSealDetail(row) } |
| | | ] |
| | | } |
| | | ]) |
| | |
| | | const searchSealApplications = () => { |
| | | page.current=1 |
| | | getSealApplicationList() |
| | | |
| | | // ElMessage.success('搜索完成') |
| | | } |
| | | // 重置印章申请搜索 |
| | | const resetSealSearch = () => { |
| | |
| | | sealSearchForm.applicationNum = '' |
| | | searchSealApplications() |
| | | } |
| | | // 提交用印申请 |
| | | |
| | | // 重新申请用印 |
| | | const reapplySeal = (row) => { |
| | | // 预填表单数据 |
| | | Object.assign(sealForm, { |
| | | id: row.id, |
| | | applicationNum: row.applicationNum, |
| | | title: row.title, |
| | | sealType: row.sealType, |
| | | reason: row.reason, |
| | | urgency: row.urgency || 'normal', |
| | | status: 'pending', |
| | | attachmentList: row.commonFileList || [] |
| | | }) |
| | | showSealApplyDialog.value = true |
| | | } |
| | | |
| | | // 提交用印申请(新增与重新申请均走 add 接口:重新申请会重新发起协同审批流程) |
| | | const submitSealApplication = async () => { |
| | | try { |
| | | await sealFormRef.value.validate() |
| | | addSealApplication(sealForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('申请提交成功') |
| | | ElMessage.success(sealForm.id ? '重新申请成功' : '申请提交成功') |
| | | closeSealApplyDialog() |
| | | getSealApplicationList() |
| | | Object.assign(sealForm, { |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | }) |
| | | } |
| | | }).catch(err => { |
| | | console.log(err.msg) |
| | | }) |
| | | |
| | | } catch (error) { |
| | | } |
| | | } |
| | | |
| | | // 关闭用印申请对话框 |
| | | const closeSealApplyDialog = () => { |
| | | // 清空表单数据 |
| | | Object.assign(sealForm, { |
| | | id: null, |
| | | applicationNum: '', |
| | | title: '', |
| | | sealType: '', |
| | | reason: '', |
| | | approveUserId: '', |
| | | urgency: 'normal', |
| | | status: 'pending' |
| | | status: 'pending', |
| | | attachmentList: [] |
| | | }) |
| | | // 清除表单验证状态 |
| | | if (sealFormRef.value) { |
| | |
| | | currentSealDetail.value = row |
| | | showSealDetailDialog.value = true |
| | | } |
| | | // 审批用印申请 |
| | | const approveSeal = (row) => { |
| | | ElMessageBox.confirm('确认通过该用印申请?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | row.status = 'approved' |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批通过') |
| | | getSealApplicationList() |
| | | |
| | | // 附件上传前校验(单个文件不超过 50MB) |
| | | const handleBeforeUpload = (file) => { |
| | | if (file.size / 1024 / 1024 > 50) { |
| | | ElMessage.error('文件大小不能超过 50MB') |
| | | return false |
| | | } |
| | | }) |
| | | }) |
| | | return true |
| | | } |
| | | // 拒绝用印申请 |
| | | const rejectSeal = (row) => { |
| | | ElMessageBox.prompt('请输入拒绝原因', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | inputPattern: /\S+/, |
| | | inputErrorMessage: '拒绝原因不能为空' |
| | | }).then(({ value }) => { |
| | | row.status = 'rejected' |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('已拒绝申请') |
| | | getSealApplicationList() |
| | | // 附件上传成功回调 |
| | | const handleUploadSuccess = (res) => { |
| | | if (res.code === 200) { |
| | | const list = res.data || [] |
| | | list.forEach(item => sealForm.attachmentList.push({ name: item.name, url: item.url })) |
| | | ElMessage.success('上传成功') |
| | | } else { |
| | | ElMessage.error(res.msg || '上传失败') |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | // 附件上传失败回调 |
| | | const handleUploadError = () => { |
| | | ElMessage.error('附件上传失败') |
| | | } |
| | | // 附件数量超出 |
| | | const handleUploadExceed = () => { |
| | | ElMessage.warning('最多上传 10 个附件') |
| | | } |
| | | // 删除待提交附件 |
| | | const removeAttachment = (index) => { |
| | | sealForm.attachmentList.splice(index, 1) |
| | | } |
| | | // 预览文件(完整可访问 URL,直接打开,不拼 host) |
| | | const previewFile = (row) => { |
| | | const url = row.previewURL || row.previewUrl || row.url |
| | | if (url && filePreviewRef.value) { |
| | | filePreviewRef.value.openUrl(url) |
| | | } else { |
| | | ElMessage.warning('文件地址无效,无法预览') |
| | | } |
| | | } |
| | | |
| | | // 重新申请用印 |
| | | const reapplySeal = (row) => { |
| | | // 预填表单数据 |
| | | Object.assign(sealForm, { |
| | | applicationNum: row.applicationNum, |
| | | title: row.title, |
| | | sealType: row.sealType, |
| | | reason: row.reason, |
| | | approveUserId: row.approveUserId, |
| | | urgency: row.urgency || 'normal', |
| | | status: 'pending' |
| | | }) |
| | | showSealApplyDialog.value = true |
| | | // 下载文件 |
| | | const downloadFile = (row) => { |
| | | const url = row.downloadURL || row.downloadUrl || row.url |
| | | if (url) { |
| | | const filename = row.originalFilename || row.name || row.fileName || 'download' |
| | | const a = document.createElement('a') |
| | | a.href = url |
| | | a.download = filename |
| | | a.target = '_blank' |
| | | a.rel = 'noopener noreferrer' |
| | | document.body.appendChild(a) |
| | | a.click() |
| | | document.body.removeChild(a) |
| | | } else { |
| | | ElMessage.warning('文件地址无效,无法下载') |
| | | } |
| | | |
| | | } |
| | | // 导出用印申请 |
| | | const { proxy } = getCurrentInstance() |
| | | const handleExport = () => { |
| | |
| | | page.size = obj.limit; |
| | | getSealApplicationList(); |
| | | }; |
| | | |
| | | // 监听对话框打开,获取用户列表 |
| | | watch(showSealApplyDialog, (newVal) => { |
| | | if (newVal) { |
| | | userListNoPageByTenantId().then((res) => { |
| | | userList.value = res.data; |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | // 路由携带 applicationNum 时,预填并查询 |
| | |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .attachment-section { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .attachment-title { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | margin-bottom: 10px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .attachment-table { |
| | | border-radius: 4px; |
| | | } |
| | | </style> |