This commit is contained in:
大黑 2025-12-24 11:25:06 +08:00
parent bc33619a91
commit 0d2457af5b
11 changed files with 2851 additions and 527 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -48,6 +48,10 @@ export default {
url: '/custom/template/export', url: '/custom/template/export',
method: 'get' method: 'get'
}, },
DOWNLODE_ALL_MODEL: {
url: '/custom/template/exportMultiple',
method: 'get'
},
//获取部门 //获取部门
GET_DEPARTMENT: { GET_DEPARTMENT: {
url: '/custom/template/getDept', url: '/custom/template/getDept',

View File

@ -276,17 +276,7 @@
</div> </div>
<!-- 流程主题 --> <!-- 流程主题 -->
<div class="approval-process-title">
<div class="description-label">
流程主题 <span class="required-mark">*</span>
</div>
<el-input
v-model="approvalFormData.processTitle"
maxlength="100"
show-word-limit
placeholder="请输入流程主题"
/>
</div>
<!-- 说明 --> <!-- 说明 -->
<div class="approval-description"> <div class="approval-description">
@ -305,6 +295,17 @@
<div class="approval-workflow"> <div class="approval-workflow">
<div class="workflow-title"> <div class="workflow-title">
审批环节 审批环节
</div>
<div class="approval-process-title">
<div class="description-label">
流程主题 <span class="required-mark">*</span>
</div>
<el-input
v-model="approvalFormData.processTitle"
maxlength="100"
show-word-limit
placeholder="请输入流程主题"
/>
</div> </div>
<div class="workflow-table"> <div class="workflow-table">
<table class="workflow-table-content"> <table class="workflow-table-content">
@ -603,7 +604,7 @@ export default {
// } // }
// initialize version data for v2/v3 as example variations // initialize version data for v2/v3 as example variations
// //
this.loadUserSelfRole() // this.loadUserSelfRole()
// //
this.loadResponsiblePersons() this.loadResponsiblePersons()
this.loadFillerPersons() this.loadFillerPersons()

View File

@ -544,7 +544,7 @@ export default {
}, },
handleDownloadImportTemplate() { handleDownloadImportTemplate() {
const request = { const request = {
...api.DOWNLODE_MODEL, ...api.DOWNLODE_ALL_MODEL,
disableSuccessMsg: true disableSuccessMsg: true
} }
this.$request(request) this.$request(request)

View File

@ -198,6 +198,16 @@
:rows="2" :rows="2"
placeholder="请输入" placeholder="请输入"
/> />
</div>
<!-- 审批环节 -->
<div class="approval-workflow">
<div class="workflow-title">
审批环节
</div>
<div>
<div class="approval-process-title">
<div class="description-label"> <div class="description-label">
流程主题 <span class="required-mark">*</span> 流程主题 <span class="required-mark">*</span>
</div> </div>
@ -208,11 +218,6 @@
placeholder="请输入流程主题" placeholder="请输入流程主题"
/> />
</div> </div>
<!-- 审批环节 -->
<div class="approval-workflow">
<div class="workflow-title">
审批环节
</div> </div>
<div class="workflow-table"> <div class="workflow-table">
<table class="workflow-table-content"> <table class="workflow-table-content">
@ -612,7 +617,7 @@ export default {
// GET_MODEL_DETAIL status // GET_MODEL_DETAIL status
// DRAFT COMPLETE // DRAFT COMPLETE
const status = this.currentStatus || '' const status = this.currentStatus || ''
return (status === 'DRAFT' || status === 'COMPLETE') && this.isLatestVersion return (status === 'DRAFT' || status === 'COMPLETE')
}, },
// DRAFT COMPLETE // DRAFT COMPLETE
shouldShowMaintainButton() { shouldShowMaintainButton() {
@ -974,7 +979,7 @@ export default {
} }
}, },
created() { created() {
this.loadUserSelfRole() // this.loadUserSelfRole()
// 使 props使 // 使 props使
if (this.templateId) { if (this.templateId) {
this.internalTemplateId = this.templateId this.internalTemplateId = this.templateId
@ -2919,7 +2924,7 @@ export default {
} }
} }
} }
.required-mark{color:#f56c6c;margin-left:4px}
// //
.approval-content { .approval-content {
display: flex; display: flex;

View File

@ -34,6 +34,12 @@
align="center" align="center"
min-width="150" min-width="150"
/> />
<el-table-column
prop="roleCode"
label="角色代码"
align="center"
min-width="150"
/>
<el-table-column <el-table-column
prop="roleDescription" prop="roleDescription"
label="角色描述" label="角色描述"
@ -312,8 +318,7 @@
ref="addRoleForm" ref="addRoleForm"
:model="addRoleFormData" :model="addRoleFormData"
:rules="addRoleFormRules" :rules="addRoleFormRules"
label-width="100px" label-width="100px">
>
<el-form-item label="角色名称" prop="roleName"> <el-form-item label="角色名称" prop="roleName">
<el-input <el-input
v-model="addRoleFormData.roleName" v-model="addRoleFormData.roleName"
@ -322,6 +327,14 @@
show-word-limit show-word-limit
/> />
</el-form-item> </el-form-item>
<el-form-item label="角色代码" prop="roleCode">
<el-input
v-model="addRoleFormData.roleCode"
placeholder="请输入角色代码"
maxlength="50"
show-word-limit
/>
</el-form-item>
<el-form-item label="角色描述" prop="roleDescription"> <el-form-item label="角色描述" prop="roleDescription">
<el-input <el-input
v-model="addRoleFormData.roleDescription" v-model="addRoleFormData.roleDescription"
@ -332,7 +345,7 @@
show-word-limit show-word-limit
/> />
</el-form-item> </el-form-item>
</el-form> </el-form-item>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="handleAddRoleDialogClose"> <el-button @click="handleAddRoleDialogClose">
取消 取消
@ -864,10 +877,12 @@ export default {
? { ? {
id: this.addRoleFormData.roleId, id: this.addRoleFormData.roleId,
roleName: this.addRoleFormData.roleName, roleName: this.addRoleFormData.roleName,
roleCode: this.addRoleFormData.roleCode,
roleDescription: this.addRoleFormData.roleDescription roleDescription: this.addRoleFormData.roleDescription
} }
: { : {
roleName: this.addRoleFormData.roleName, roleName: this.addRoleFormData.roleName,
roleCode: this.addRoleFormData.roleCode,
roleDescription: this.addRoleFormData.roleDescription roleDescription: this.addRoleFormData.roleDescription
}, },
disableSuccessMsg: true disableSuccessMsg: true
@ -1105,7 +1120,7 @@ export default {
// //
loadDepartmentList() { loadDepartmentList() {
const request = { const request = {
...api.GET_SUB_DEPT, ...api.QUERY_ORG_TREE,
disableSuccessMsg: true disableSuccessMsg: true
} }
this.$request(request) this.$request(request)

View File

@ -84,7 +84,9 @@
</div> </div>
<div class="toolbar"> <div class="toolbar">
<el-button type="primary" @click="handleCreate">新增</el-button> <el-button type="primary" @click="handleCreate">
新增
</el-button>
<!-- <el-button @click="handleDownloadTemplate">下载模板</el-button> --> <!-- <el-button @click="handleDownloadTemplate">下载模板</el-button> -->
</div> </div>
<div class="page-data-list"> <div class="page-data-list">
@ -111,7 +113,9 @@
v-for="(colConfig, index) in tableConfig.colConfigs" v-for="(colConfig, index) in tableConfig.colConfigs"
v-slot:[colConfig.customHeadSlot]="{ column }" v-slot:[colConfig.customHeadSlot]="{ column }"
> >
<div v-if="colConfig.customHeadSlot" :key="index"> {{ column }} {{ colConfig }} </div> <div v-if="colConfig.customHeadSlot" :key="index">
{{ column }} {{ colConfig }}
</div>
</template> </template>
<template <template
@ -122,52 +126,60 @@
<span <span
v-if="row[colConfig.prop] === 'APPROVING'" v-if="row[colConfig.prop] === 'APPROVING'"
style="background: #E6A23C; padding: 4px 8px; color: white; border-radius: 20px;" style="background: #E6A23C; padding: 4px 8px; color: white; border-radius: 20px;"
>审核中</span
> >
审核中
</span>
<span <span
v-else-if="row[colConfig.prop] === 'COMPLETE'" v-else-if="row[colConfig.prop] === 'COMPLETE'"
style="background: #67C23A; padding: 4px 8px; color: white; border-radius: 20px;" style="background: #67C23A; padding: 4px 8px; color: white; border-radius: 20px;"
>已完成</span
> >
已完成
</span>
<span <span
v-else-if="row[colConfig.prop] === 'DRAFT'" v-else-if="row[colConfig.prop] === 'DRAFT'"
style="background: #909399; padding: 4px 8px; color: white; border-radius: 20px;" style="background: #909399; padding: 4px 8px; color: white; border-radius: 20px;"
>草稿</span
> >
草稿
</span>
<span <span
v-else-if="row[colConfig.prop] === 'REJECTED'" v-else-if="row[colConfig.prop] === 'REJECTED'"
style="background: #F56C6C; padding: 4px 8px; color: white; border-radius: 20px;" style="background: #F56C6C; padding: 4px 8px; color: white; border-radius: 20px;"
>已拒绝</span
> >
已拒绝
</span>
<span <span
v-else v-else
style="background: #909399; padding: 4px 8px; color: white; border-radius: 20px;" style="background: #909399; padding: 4px 8px; color: white; border-radius: 20px;"
>-</span
> >
-
</span>
</div> </div>
<div v-if="colConfig.customSlot === 'options'" :key="'op' + index + '' + rowIndex"> <div v-if="colConfig.customSlot === 'options'" :key="'op' + index + '' + rowIndex">
<a <a
href="javascript:void(0)" href="javascript:void(0)"
class="el-link el-link--primary" class="el-link el-link--primary"
@click.stop="handleRowView(row)" @click.stop="handleRowView(row)"
>查看</a
> >
查看
</a>
<span style="width: 6px; display: inline-block"></span> <span style="width: 6px; display: inline-block"></span>
<a <a
v-if="row.status !== 'APPROVING' && row.status !== 'COMPLETE'" v-if="false"
href="javascript:void(0)" href="javascript:void(0)"
class="el-link el-link--primary" class="el-link el-link--primary"
@click.stop="handleEdit(row)" @click.stop="handleEdit(row)"
>修改</a
> >
修改
</a>
<span style="width: 6px; display: inline-block"></span> <span style="width: 6px; display: inline-block"></span>
<a <a
v-if="row.status === 'APPROVING'" v-if="row.status === 'APPROVING'"
href="javascript:void(0)" href="javascript:void(0)"
class="el-link el-link--primary" class="el-link el-link--primary"
@click.stop="handleApprove(row)" @click.stop="handleApprove(row)"
>审批</a
> >
审批
</a>
<span <span
v-if="row.status === 'APPROVING'" v-if="row.status === 'APPROVING'"
style="width: 6px; display: inline-block" style="width: 6px; display: inline-block"
@ -177,8 +189,9 @@
href="javascript:void(0)" href="javascript:void(0)"
class="el-link el-link--primary" class="el-link el-link--primary"
@click.stop="handleLaunch(row)" @click.stop="handleLaunch(row)"
>发起</a
> >
发起
</a>
<span <span
v-if="!row.status || row.status === '' || row.status === 'DRAFT'" v-if="!row.status || row.status === '' || row.status === 'DRAFT'"
style="width: 6px; display: inline-block" style="width: 6px; display: inline-block"
@ -188,8 +201,9 @@
href="javascript:void(0)" href="javascript:void(0)"
class="el-link el-link--primary" class="el-link el-link--primary"
@click.stop="handleRestart(row)" @click.stop="handleRestart(row)"
>重新发起</a
> >
重新发起
</a>
<span <span
v-if="row.status === 'REJECTED'" v-if="row.status === 'REJECTED'"
style="width: 6px; display: inline-block" style="width: 6px; display: inline-block"
@ -199,8 +213,9 @@
href="javascript:void(0)" href="javascript:void(0)"
class="el-link el-link--primary" class="el-link el-link--primary"
@click.stop="handleDownload(row)" @click.stop="handleDownload(row)"
>下载</a
> >
下载
</a>
</div> </div>
</template> </template>
@ -344,8 +359,12 @@
</el-form> </el-form>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="handleDialogClose">取消</el-button> <el-button @click="handleDialogClose">
<el-button type="primary" @click="handleSubmit">确定</el-button> 取消
</el-button>
<el-button type="primary" @click="handleSubmit">
确定
</el-button>
</div> </div>
</el-dialog> </el-dialog>
@ -359,6 +378,18 @@
@close="handleLaunchDialogClose" @close="handleLaunchDialogClose"
> >
<div class="dialog-body-wrapper"> <div class="dialog-body-wrapper">
<div class="approval-process-title">
<div class="description-label">
流程主题 <span class="required-mark">*</span>
</div>
<el-input
v-model="launchProcessTitle"
maxlength="100"
show-word-limit
placeholder="请输入流程主题"
/>
</div>
<div class="approval-workflow"> <div class="approval-workflow">
<div class="workflow-title"> <div class="workflow-title">
审批环节 审批环节
@ -414,8 +445,12 @@
</div> </div>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="handleLaunchDialogClose">取消</el-button> <el-button @click="handleLaunchDialogClose">
<el-button type="primary" :loading="launching" @click="handleLaunchSubmit">确定</el-button> 取消
</el-button>
<el-button type="primary" :loading="launching" @click="handleLaunchSubmit">
确定
</el-button>
</div> </div>
</el-dialog> </el-dialog>
@ -503,6 +538,7 @@ export default {
currentEditRow: null, // currentEditRow: null, //
launchDialogVisible: false, // launchDialogVisible: false, //
currentLaunchRow: null, // currentLaunchRow: null, //
launchProcessTitle: '',
launchWorkflowSteps: [ launchWorkflowSteps: [
{ {
name: '校对', name: '校对',
@ -717,6 +753,7 @@ export default {
}, },
handleLaunch(row) { handleLaunch(row) {
this.currentLaunchRow = row this.currentLaunchRow = row
this.launchProcessTitle = row.title || ''
// //
this.launchWorkflowSteps = [ this.launchWorkflowSteps = [
{ {
@ -746,6 +783,7 @@ export default {
handleLaunchDialogClose() { handleLaunchDialogClose() {
this.launchDialogVisible = false this.launchDialogVisible = false
this.currentLaunchRow = null this.currentLaunchRow = null
this.launchProcessTitle = ''
// //
this.launchWorkflowSteps = [ this.launchWorkflowSteps = [
{ {
@ -787,6 +825,13 @@ export default {
return return
} }
//
const processTitle = (this.launchProcessTitle || '').trim()
if (!processTitle) {
this.$message.warning('请填写流程主题')
return
}
const reportId = this.currentLaunchRow.reportId || this.currentLaunchRow.id const reportId = this.currentLaunchRow.reportId || this.currentLaunchRow.id
if (!reportId) { if (!reportId) {
this.$message.error('缺少报告ID') this.$message.error('缺少报告ID')
@ -797,6 +842,7 @@ export default {
const submitData = { const submitData = {
reportId: reportId, reportId: reportId,
userId: getUserId(), userId: getUserId(),
processTitle: processTitle,
proofreadUsers: this.launchWorkflowSteps[0].handlers, proofreadUsers: this.launchWorkflowSteps[0].handlers,
reviewUsers: this.launchWorkflowSteps[1].handlers, reviewUsers: this.launchWorkflowSteps[1].handlers,
countersignUsers: [], countersignUsers: [],

View File

@ -18,8 +18,27 @@
</div> </div>
</div> </div>
<div class="header-right"> <div class="header-right">
<!-- 草稿状态显示发起和修改按钮 -->
<template v-if="reportDetail.status === 'DRAFT' && !isEditMode">
<el-button @click="handleLaunch">
发起
</el-button>
<el-button type="primary" @click="handleEdit">
修改
</el-button>
</template>
<!-- 编辑模式显示保存和返回按钮 -->
<template v-if="isEditMode">
<el-button @click="handleCancelEdit">
返回
</el-button>
<el-button type="primary" :loading="saving" @click="handleSave">
保存
</el-button>
</template>
<!-- 审批中状态显示审批按钮 -->
<el-button <el-button
v-if="reportDetail.status === 'APPROVING'" v-if="reportDetail.status === 'APPROVING' && !isEditMode"
type="primary" type="primary"
@click="handleApprove" @click="handleApprove"
> >
@ -33,6 +52,8 @@
<div class="section-title"> <div class="section-title">
报告信息 报告信息
</div> </div>
<!-- 只读模式 -->
<template v-if="!isEditMode">
<div class="info-grid"> <div class="info-grid">
<div class="info-item"> <div class="info-item">
<span class="label">标题名称</span> <span class="label">标题名称</span>
@ -97,7 +118,7 @@
<div class="info-item"> <div class="info-item">
<span class="label">文件</span> <span class="label">文件</span>
<span class="value"> <span class="value">
<a v-if="reportDetail.filePath" href="javascript:void(0)" class="el-link el-link--primary" @click.stop="handleDownload(reportDetail)">下载</a> <a v-if="reportDetail.filePath" href="javascript:void(0)" class="el-link el-link--primary" @click.stop="handleDownload(reportDetail)">{{ reportDetail.fileName }}</a>
</span> </span>
</div> </div>
</div> </div>
@ -105,10 +126,121 @@
<span class="label">说明</span> <span class="label">说明</span>
<span class="value">{{ reportDetail.explanation }}</span> <span class="value">{{ reportDetail.explanation }}</span>
</div> </div>
</template>
<!-- 编辑模式 -->
<template v-else>
<el-form ref="editForm" :model="editFormData" label-width="150px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="标题名称" prop="title">
<el-input v-model="editFormData.title" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="厂家名称(全称)" prop="manufacturerName">
<el-input v-model="editFormData.manufacturerName" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品型号(全称)" prop="modelName">
<el-input v-model="editFormData.modelName" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="厂家简称" prop="manufacturerAbbreviation">
<el-input v-model="editFormData.manufacturerAbbreviation" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="产品型号简称" prop="modelAbbreviation">
<el-input v-model="editFormData.modelAbbreviation" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品编号" prop="productNumber">
<el-input v-model="editFormData.productNumber" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="领域/模块" prop="module">
<el-input v-model="editFormData.module" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="板块" prop="plate">
<el-input v-model="editFormData.plate" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="平台" prop="platform">
<el-input v-model="editFormData.platform" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="系列" prop="series">
<el-input v-model="editFormData.series" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="细分市场" prop="marketSegment">
<el-input v-model="editFormData.marketSegment" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="editFormData.projectName" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目编号" prop="projectNumber">
<el-input v-model="editFormData.projectNumber" placeholder="请输入" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="附件上传" prop="attachments">
<el-upload
ref="upload"
:auto-upload="false"
:on-change="handleFileChange"
:on-remove="handleFileRemove"
:file-list="fileList"
:limit="1"
accept=".doc,.pdf,.xls,.xlsx,.ppt,.pptx"
>
<el-button type="primary" icon="el-icon-upload">
选择文件
</el-button>
<div slot="tip" class="el-upload__tip">
支持扩展名: .doc .pdf .xls .xlsx .ppt .pptx仅支持上传一个文件
</div>
</el-upload>
</el-form-item>
<el-form-item label="说明" prop="explanation">
<el-input
v-model="editFormData.explanation"
type="textarea"
:rows="4"
placeholder="请输入"
/>
</el-form-item>
</el-form>
</template>
</section> </section>
<!-- 审批流程 --> <!-- 审批流程 -->
<section v-if="reportDetail.status && reportDetail.status !== 'DRAFT'" class="block-section"> <section v-if="reportDetail.status && reportDetail.status !== 'DRAFT' && reportDetail.flowId" class="block-section">
<div class="section-title"> <div class="section-title">
审批流程 审批流程
</div> </div>
@ -150,6 +282,92 @@
</div> </div>
</div> </div>
<!-- 发起弹窗 -->
<el-dialog
title="发起"
:visible.sync="launchDialogVisible"
width="600px"
:close-on-click-modal="false"
append-to-body
@close="handleLaunchDialogClose"
>
<div class="dialog-body-wrapper">
<div class="approval-process-title">
<div class="description-label">
流程主题 <span class="required-mark">*</span>
</div>
<el-input
v-model="launchProcessTitle"
maxlength="100"
show-word-limit
placeholder="请输入流程主题"
/>
</div>
<div class="approval-workflow">
<div class="workflow-title">
审批环节
</div>
<div class="workflow-table">
<table class="workflow-table-content">
<thead>
<tr>
<th class="step-name-col">
环节名称
</th>
<th class="handler-col">
处理人
</th>
</tr>
</thead>
<tbody>
<tr v-for="(step, index) in launchWorkflowSteps" :key="index" class="workflow-row">
<td class="step-name-cell">
<span class="step-name">
{{ step.name }}
<span v-if="step.required" class="required-mark">*</span>
</span>
</td>
<td class="handler-cell">
<el-select
v-model="step.handlers"
multiple
filterable
remote
reserve-keyword
placeholder="请选择"
:remote-method="(query) => handleLaunchRemoteSearch(query, index)"
:loading="step.loading"
class="handler-select"
@change="(value) => handleLaunchHandlerChange(index, value)"
>
<el-option
v-for="person in step.options"
:key="person.userNumber"
:value="person.userId"
:label="person.username"
>
<span class="username-text">{{ person.username }}</span>
<span class="user-id-text">{{ person.userNumber }}</span>
</el-option>
</el-select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleLaunchDialogClose">
取消
</el-button>
<el-button type="primary" :loading="launching" @click="handleLaunchSubmit">
确定
</el-button>
</div>
</el-dialog>
<!-- 审批弹窗 --> <!-- 审批弹窗 -->
<el-dialog <el-dialog
title="审核" title="审核"
@ -216,7 +434,57 @@ export default {
result: '', result: '',
opinion: '' opinion: ''
}, },
approvalSubmitting: false approvalSubmitting: false,
isEditMode: false, //
editFormData: {
title: '',
manufacturerName: '',
modelName: '',
manufacturerAbbreviation: '',
modelAbbreviation: '',
productNumber: '',
module: '',
plate: '',
platform: '',
series: '',
marketSegment: '',
projectName: '',
projectNumber: '',
explanation: '',
fileUrl: '',
fileName: ''
},
originalReportDetail: {}, //
selectedFile: null, //
fileList: [], //
uploading: false, //
saving: false, //
launchDialogVisible: false, //
launchProcessTitle: '', //
launchWorkflowSteps: [
{
name: '校对',
required: true,
handlers: [],
options: [],
loading: false
},
{
name: '审核',
required: true,
handlers: [],
options: [],
loading: false
},
{
name: '批准',
required: true,
handlers: [],
options: [],
loading: false
}
],
launching: false //
} }
}, },
computed: { computed: {
@ -374,6 +642,8 @@ export default {
this.$request(request) this.$request(request)
.asyncThen((resp) => { .asyncThen((resp) => {
this.reportDetail = resp.data || resp this.reportDetail = resp.data || resp
//
this.originalReportDetail = JSON.parse(JSON.stringify(this.reportDetail))
// flowId稿 // flowId稿
if (flowId && this.reportDetail.status && this.reportDetail.status !== 'DRAFT') { if (flowId && this.reportDetail.status && this.reportDetail.status !== 'DRAFT') {
this.getApprovalSpet(flowId) this.getApprovalSpet(flowId)
@ -610,6 +880,414 @@ export default {
} }
return formattedStep return formattedStep
}) })
},
//
handleLaunch() {
this.launchProcessTitle = this.reportDetail.title || ''
//
this.launchWorkflowSteps = [
{
name: '校对',
required: true,
handlers: [],
options: [],
loading: false
},
{
name: '审核',
required: true,
handlers: [],
options: [],
loading: false
},
{
name: '批准',
required: true,
handlers: [],
options: [],
loading: false
}
]
this.launchDialogVisible = true
},
//
handleLaunchDialogClose() {
this.launchDialogVisible = false
this.launchProcessTitle = ''
//
this.launchWorkflowSteps = [
{
name: '校对',
required: true,
handlers: [],
options: [],
loading: false
},
{
name: '审核',
required: true,
handlers: [],
options: [],
loading: false
},
{
name: '批准',
required: true,
handlers: [],
options: [],
loading: false
}
]
},
//
handleLaunchSubmit() {
//
const requiredStepsWithoutHandlers = this.launchWorkflowSteps.filter(
(step) => step.required && (!step.handlers || step.handlers.length === 0)
)
if (requiredStepsWithoutHandlers.length > 0) {
const stepNames = requiredStepsWithoutHandlers.map((step) => step.name).join('、')
this.$message.warning(`请为必填环节"${stepNames}"选择处理人`)
return
}
if (!this.reportDetail.reportId && !this.reportDetail.id) {
this.$message.error('缺少报告信息')
return
}
//
const processTitle = (this.launchProcessTitle || '').trim()
if (!processTitle) {
this.$message.warning('请填写流程主题')
return
}
const reportId = this.reportDetail.reportId || this.reportDetail.id
if (!reportId) {
this.$message.error('缺少报告ID')
return
}
//
const submitData = {
reportId: reportId,
userId: getUserId(),
processTitle: processTitle,
proofreadUsers: this.launchWorkflowSteps[0].handlers,
reviewUsers: this.launchWorkflowSteps[1].handlers,
countersignUsers: [],
approveUsers: this.launchWorkflowSteps[2].handlers
}
this.launching = true
const request = {
...api.ADD_REPORT,
params: submitData,
disableSuccessMsg: true
}
this.$request(request)
.asyncThen((resp) => {
this.launching = false
this.handleLaunchDialogClose()
//
this.loadReportDetailFromApi(reportId, this.detail.flowId)
this.$message({
type: 'success',
message: '发起成功'
})
//
this.$emit('refresh')
})
.asyncErrorCatch((err) => {
console.error('发起失败:', err)
this.launching = false
this.$message.error('发起失败,请重试')
})
},
//
handleLaunchRemoteSearch(query, stepIndex) {
const step = this.launchWorkflowSteps[stepIndex]
if (!query || query.trim() === '') {
if (step.handlers && step.handlers.length > 0) {
const selectedOptions = step.options.filter((opt) => step.handlers.includes(opt.userId))
step.options = selectedOptions
} else {
step.options = []
}
return
}
step.loading = true
const trimmedQuery = query.trim()
const request = {
...api.GET_ALLUSERS,
params: {
name: trimmedQuery
},
disableSuccessMsg: true
}
this.$request(request)
.asyncThen((resp) => {
const respData = resp && resp.data
let result = []
if (Array.isArray(respData)) {
result = respData
} else if (respData && Array.isArray(respData.result)) {
result = respData.result
} else if (respData && respData.data && Array.isArray(respData.data)) {
result = respData.data
}
const users = result
.map((user) => ({
userId: user.userId || user.id,
username: user.username || user.name,
userNumber: user.userNumber || user.userId || user.id
}))
.filter((user) => user.userId && user.userNumber)
const existingIds = new Set(step.handlers || [])
const filteredUsers = users.filter((user) => !existingIds.has(user.userId))
const selectedOptions = (step.options || []).filter(
(opt) => opt.userId && step.handlers && step.handlers.includes(opt.userId)
)
const optionsMap = new Map()
selectedOptions.forEach((opt) => {
if (opt.userNumber) {
optionsMap.set(opt.userNumber, opt)
}
})
filteredUsers.forEach((user) => {
if (user.userNumber && !optionsMap.has(user.userNumber)) {
optionsMap.set(user.userNumber, user)
}
})
step.options = Array.from(optionsMap.values())
step.loading = false
})
.asyncErrorCatch((err) => {
console.error('搜索人员失败:', err)
if (step.handlers && step.handlers.length > 0) {
const selectedOptions = step.options.filter((opt) => step.handlers.includes(opt.userId))
step.options = selectedOptions
} else {
step.options = []
}
step.loading = false
})
},
handleLaunchHandlerChange(stepIndex, selectedIds) {
//
},
//
handleEdit() {
//
this.originalReportDetail = JSON.parse(JSON.stringify(this.reportDetail))
//
this.editFormData = {
title: this.reportDetail.title || '',
manufacturerName: this.reportDetail.manufacturerName || '',
modelName: this.reportDetail.modelName || '',
manufacturerAbbreviation: this.reportDetail.manufacturerAbbreviation || '',
modelAbbreviation: this.reportDetail.modelAbbreviation || '',
productNumber: this.reportDetail.productNumber || '',
module: this.reportDetail.module || '',
plate: this.reportDetail.plate || '',
platform: this.reportDetail.platform || '',
series: this.reportDetail.series || '',
marketSegment: this.reportDetail.marketSegment || '',
projectName: this.reportDetail.projectName || '',
projectNumber: this.reportDetail.projectNumber || '',
explanation: this.reportDetail.explanation || '',
fileUrl: this.reportDetail.fileUrl || this.reportDetail.filePath || '',
fileName: this.reportDetail.fileName || ''
}
//
if (this.reportDetail.fileUrl || this.reportDetail.filePath) {
this.fileList = [
{
name: this.reportDetail.fileName || '文件',
url: this.reportDetail.fileUrl || this.reportDetail.filePath
}
]
} else {
this.fileList = []
}
this.selectedFile = null
this.isEditMode = true
},
//
handleCancelEdit() {
//
this.reportDetail = JSON.parse(JSON.stringify(this.originalReportDetail))
//
this.editFormData = {
title: '',
manufacturerName: '',
modelName: '',
manufacturerAbbreviation: '',
modelAbbreviation: '',
productNumber: '',
module: '',
plate: '',
platform: '',
series: '',
marketSegment: '',
projectName: '',
projectNumber: '',
explanation: '',
fileUrl: '',
fileName: ''
}
this.fileList = []
this.selectedFile = null
this.isEditMode = false
//
if (this.$refs.editForm) {
this.$refs.editForm.clearValidate()
}
},
//
handleFileChange(file, fileList) {
const allowedTypes = ['.doc', '.pdf', '.xls', '.xlsx', '.ppt', '.pptx']
const fileName = file.name.toLowerCase()
const isValidType = allowedTypes.some((type) => fileName.endsWith(type))
if (!isValidType) {
this.$message.error('不支持的文件类型,仅支持 .doc .pdf .xls .xlsx .ppt .pptx')
this.$refs.upload.handleRemove(file)
return false
}
this.selectedFile = file && file.raw ? file.raw : null
this.fileList = fileList.slice(-1)
},
//
handleFileRemove(file, fileList) {
this.selectedFile = null
this.fileList = []
},
//
handleFileUpload() {
if (this.uploading || !this.selectedFile) {
if (!this.selectedFile) {
return Promise.resolve(null)
}
return Promise.resolve(null)
}
const file = this.selectedFile
const formData = new FormData()
formData.append('file', file)
formData.append('createBy', getUserId())
const request = {
...api.TEST_FILE_UPLOAD,
params: formData,
disableSuccessMsg: true
}
this.uploading = true
const loadingInstance = this.$loading({
lock: true,
text: '正在上传文件,请稍候...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
return this.$upload(request, null, false)
.then((resp) => {
console.log('文件上传成功:', resp)
loadingInstance.close()
let fileUrl = ''
if (resp.data && resp) {
const url = resp.data
fileUrl = url
this.editFormData.fileUrl = fileUrl
}
return fileUrl
})
.catch((err) => {
console.error('文件上传失败:', err)
loadingInstance.close()
this.$message.error('文件上传失败,请重试')
throw err
})
.finally(() => {
if (loadingInstance) {
loadingInstance.close()
}
this.uploading = false
})
},
//
handleSave() {
//
if (this.$refs.editForm) {
this.$refs.editForm.validate((valid) => {
if (!valid) {
return false
}
this.submitEdit()
})
} else {
this.submitEdit()
}
},
//
submitEdit() {
const submitData = async () => {
//
if (this.selectedFile) {
try {
const fileUrl = await this.handleFileUpload()
this.editFormData.fileUrl = fileUrl || ''
this.editFormData.fileName = this.selectedFile.name
} catch (err) {
console.error('文件上传失败:', err)
this.$message.error('文件上传失败,请重试')
return
}
}
const reportId = this.reportDetail.reportId || this.reportDetail.id
if (!reportId) {
this.$message.error('缺少报告ID')
return
}
const request = {
...api.EDIT_REPORT,
params: {
reportId: reportId,
...this.editFormData,
createBy: getUserId(),
userId: getUserId()
}
}
this.saving = true
this.$request(request)
.asyncThen((resp) => {
this.saving = false
this.isEditMode = false
//
this.loadReportDetailFromApi(reportId, this.detail.flowId)
this.$message({
type: 'success',
message: '保存成功'
})
//
this.$emit('refresh')
})
.asyncErrorCatch((err) => {
console.error('保存失败:', err)
this.saving = false
this.$message.error('保存失败,请重试')
})
}
submitData()
} }
} }
} }
@ -906,4 +1584,141 @@ export default {
left: 7px; left: 7px;
top: 3px; top: 3px;
} }
.dialog-body-wrapper {
max-height: 70vh;
overflow-y: auto;
padding-right: 10px;
}
.approval-process-title {
margin-bottom: 20px;
.description-label {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
.required-mark {
color: #f56c6c;
margin-left: 4px;
}
}
}
.approval-workflow {
display: flex;
flex-direction: column;
gap: 16px;
margin-top: 20px;
.workflow-title {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.workflow-table {
width: 100%;
.workflow-table-content {
width: 100%;
border-collapse: collapse;
border: 1px solid #ebeef5;
thead {
background: #f5f7fa;
th {
padding: 12px 16px;
text-align: left;
font-size: 14px;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #ebeef5;
&.step-name-col {
width: 120px;
}
&.handler-col {
width: auto;
}
}
}
tbody {
.workflow-row {
&:not(:last-child) {
border-bottom: 1px solid #ebeef5;
}
.step-name-cell {
padding: 12px 16px;
vertical-align: middle;
.step-name {
font-size: 14px;
color: #303133;
.required-mark {
color: #f56c6c;
margin-left: 4px;
}
}
}
.handler-cell {
padding: 12px 16px;
vertical-align: middle;
.handler-select {
width: 100%;
min-width: 200px;
::v-deep .el-input__inner {
height: 32px;
line-height: 32px;
}
::v-deep .el-tag {
margin: 2px 4px 2px 0;
}
}
}
}
}
}
}
}
.el-select-dropdown__item {
display: flex !important;
justify-content: space-between !important;
padding-right: 20px !important;
}
.username-text {
color: #303133 !important;
display: inline-block;
text-align: right !important;
}
.user-id-text {
color: #909399 !important;
display: inline-block;
margin-left: 20px;
text-align: right !important;
}
::v-deep .el-upload-dragger {
width: 100%;
height: 180px;
}
::v-deep .el-form-item {
margin-bottom: 18px;
}
</style> </style>