|
|
|
|
@ -22,7 +22,7 @@
|
|
|
|
|
<el-dialog
|
|
|
|
|
title="合同签订"
|
|
|
|
|
:visible.sync="dialogVisible"
|
|
|
|
|
width="60%"
|
|
|
|
|
width="75%"
|
|
|
|
|
:close-on-click-modal="false"
|
|
|
|
|
append-to-body
|
|
|
|
|
@open="onDialogOpen"
|
|
|
|
|
@ -32,67 +32,209 @@
|
|
|
|
|
:model="form"
|
|
|
|
|
:rules="rules"
|
|
|
|
|
ref="formRef"
|
|
|
|
|
label-width="120px"
|
|
|
|
|
label-width="140px"
|
|
|
|
|
v-loading="loading"
|
|
|
|
|
>
|
|
|
|
|
<el-form-item label="合同编号" prop="contract_no">
|
|
|
|
|
<el-input v-model="form.contract_no" />
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="合同编号" prop="contract_no">
|
|
|
|
|
<el-input v-model="form.contract_no" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="合同名称">
|
|
|
|
|
<el-input v-model="form.title" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-form-item label="合同主要内容">
|
|
|
|
|
<el-input v-model="form.main_content" type="textarea" :rows="3" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="标题">
|
|
|
|
|
<el-input v-model="form.title" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="甲方">
|
|
|
|
|
<el-input v-model="form.party_a" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="乙方">
|
|
|
|
|
<el-input v-model="form.party_b" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="总额">
|
|
|
|
|
<el-input-number v-model="form.amount_total" :min="0" :step="1000" style="width: 100%" />
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="甲方">
|
|
|
|
|
<el-input v-model="form.party_a" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="乙方">
|
|
|
|
|
<el-input v-model="form.party_b" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="合同金额(元)">
|
|
|
|
|
<el-input-number
|
|
|
|
|
v-model="form.amount_total"
|
|
|
|
|
:min="0"
|
|
|
|
|
:precision="2"
|
|
|
|
|
:controls="form.amount_type !== 'open'"
|
|
|
|
|
:readonly="form.amount_type === 'open'"
|
|
|
|
|
@keydown="handleAmountInputKeydown"
|
|
|
|
|
@focus="handleAmountInputFocus"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
:class="{ 'readonly-amount': form.amount_type === 'open' }"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="项目预算金额(元)">
|
|
|
|
|
<el-input-number v-model="form.budget_amount" :min="0" :precision="2" style="width: 100%" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="金额类型">
|
|
|
|
|
<el-select v-model="form.amount_type" placeholder="请选择金额类型" style="width: 100%">
|
|
|
|
|
<el-option label="闭口合同(金额确定)" value="fixed" />
|
|
|
|
|
<el-option label="框架协议/开口合同(金额不确定)" value="open" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12" v-if="form.amount_type === 'open'">
|
|
|
|
|
<el-form-item label="金额说明">
|
|
|
|
|
<el-input v-model="form.amount_description" type="textarea" :rows="2" placeholder="请说明开口合同的金额情况" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="合同类型">
|
|
|
|
|
<el-select v-model="form.contract_type" placeholder="请选择合同类型" style="width: 100%">
|
|
|
|
|
<el-option label="收入类" value="1" />
|
|
|
|
|
<el-option label="支出类" value="2" />
|
|
|
|
|
<el-option label="技术协议" value="3" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="签订日期">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="form.sign_date"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="yyyy-MM-dd"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="申请日期">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="form.apply_date"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="yyyy-MM-dd"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-form-item label="合同履行期">
|
|
|
|
|
<el-input v-model="form.perform_period" placeholder="例如:2025-01-01 至 2025-12-31" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="金额类型">
|
|
|
|
|
<el-select v-model="form.amount_type" placeholder="请选择金额类型" style="width: 100%">
|
|
|
|
|
<el-option label="闭口合同(金额确定)" value="fixed" />
|
|
|
|
|
<el-option label="框架协议/开口合同(金额不确定)" value="open" />
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="付款方式">
|
|
|
|
|
<el-input v-model="form.pay_method" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="项目经费来源">
|
|
|
|
|
<el-input v-model="form.fund_source" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="招标代理机构">
|
|
|
|
|
<el-input v-model="form.tender_agent" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="采购方式">
|
|
|
|
|
<el-select v-model="form.purchase_method" placeholder="请选择采购方式" style="width: 100%">
|
|
|
|
|
<el-option label="公开招标" value="公开招标" />
|
|
|
|
|
<el-option label="邀请招标" value="邀请招标" />
|
|
|
|
|
<el-option label="竞争性谈判" value="竞争性谈判" />
|
|
|
|
|
<el-option label="单一来源采购" value="单一来源采购" />
|
|
|
|
|
<el-option label="询价" value="询价" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="合同履行情况">
|
|
|
|
|
<el-input v-model="form.perform_status" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="是否验收">
|
|
|
|
|
<el-switch v-model="form.is_accepted" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-form-item label="采购类别">
|
|
|
|
|
<el-select v-model="form.purchase_category" placeholder="请选择采购类别" style="width: 100%">
|
|
|
|
|
<el-option label="仪器" value="仪器" />
|
|
|
|
|
<el-option label="外包" value="外包" />
|
|
|
|
|
<el-option label="服务" value="服务" />
|
|
|
|
|
<el-option label="一般采购" value="一般采购" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="状态">
|
|
|
|
|
<el-select v-model="form.status" style="width: 100%">
|
|
|
|
|
<el-option label="草稿" value="draft" />
|
|
|
|
|
<el-option label="生效" value="approved" />
|
|
|
|
|
<el-option label="终止" value="terminated" />
|
|
|
|
|
<el-form-item label="科室">
|
|
|
|
|
<el-select v-model="form.owner_department_ids_array" multiple placeholder="请选择科室" filterable clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="dept in departmentList" :key="dept.id" :label="dept.name" :value="dept.id.toString()" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="签订日期">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="form.sign_date"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="yyyy-MM-dd"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
/>
|
|
|
|
|
<el-form-item label="合同签订/变更经办人">
|
|
|
|
|
<el-select v-model="form.handler_admin_ids_array" multiple placeholder="请选择经办人" filterable clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id.toString()" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="开始日期">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="form.start_date"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="yyyy-MM-dd"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
/>
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="申请科室经办人">
|
|
|
|
|
<el-select v-model="form.apply_handler_id" placeholder="请选择" filterable clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="采购科室经办人">
|
|
|
|
|
<el-select v-model="form.purchase_handler_id" placeholder="请选择" filterable clearable style="width: 100%">
|
|
|
|
|
<el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-form-item label="备注">
|
|
|
|
|
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="结束日期">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="form.end_date"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="yyyy-MM-dd"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
/>
|
|
|
|
|
<el-form-item label="附件">
|
|
|
|
|
<el-upload
|
|
|
|
|
:action="uploadAction"
|
|
|
|
|
:headers="uploadHeaders"
|
|
|
|
|
:before-upload="beforeUpload"
|
|
|
|
|
:on-success="handleUploadSuccess"
|
|
|
|
|
:on-error="handleUploadError"
|
|
|
|
|
:on-remove="handleUploadRemove"
|
|
|
|
|
:file-list="attachmentFileList"
|
|
|
|
|
:limit="1"
|
|
|
|
|
>
|
|
|
|
|
<el-button type="primary">上传文件</el-button>
|
|
|
|
|
<template #tip>
|
|
|
|
|
<div class="el-upload__tip">只能上传 JPG/PNG/PDF/DOC/DOCX/XLS/XLSX 格式的文件,且不超过 10MB</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-upload>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-divider content-position="left">付款计划</el-divider>
|
|
|
|
|
<div class="payplan-toolbar">
|
|
|
|
|
<el-button type="primary" size="small" @click="addPayPlan">新增计划</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<el-table :data="form.pay_plans" border size="small" class="mb-16">
|
|
|
|
|
<el-table :data="form.pay_plans" border size="small" class="mb-16 payplan-table" style="width: 100%">
|
|
|
|
|
<el-table-column prop="phase_no" label="期次" width="100">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-input-number v-model="row.phase_no" :min="1" size="small" />
|
|
|
|
|
@ -139,12 +281,14 @@
|
|
|
|
|
<script>
|
|
|
|
|
import request from "@/utils/request";
|
|
|
|
|
import { Message } from 'element-ui';
|
|
|
|
|
import { getToken } from '@/utils/auth';
|
|
|
|
|
import {
|
|
|
|
|
getFlowDetailForContract,
|
|
|
|
|
getContractSettings,
|
|
|
|
|
getContractByFlowId,
|
|
|
|
|
createContract,
|
|
|
|
|
} from "@/api/flow";
|
|
|
|
|
import { userListNoAuth, departmentListNoAuth } from "@/api/common";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: "ContractSignField",
|
|
|
|
|
@ -160,18 +304,41 @@ export default {
|
|
|
|
|
loading: false,
|
|
|
|
|
saving: false,
|
|
|
|
|
formRef: null,
|
|
|
|
|
userList: [],
|
|
|
|
|
departmentList: [],
|
|
|
|
|
attachmentFileList: [],
|
|
|
|
|
form: {
|
|
|
|
|
contract_no: "",
|
|
|
|
|
title: "",
|
|
|
|
|
main_content: "",
|
|
|
|
|
party_a: "",
|
|
|
|
|
party_b: "",
|
|
|
|
|
amount_total: 0,
|
|
|
|
|
amount_total: null,
|
|
|
|
|
amount_type: "fixed",
|
|
|
|
|
status: "draft",
|
|
|
|
|
amount_description: "",
|
|
|
|
|
budget_amount: 0,
|
|
|
|
|
contract_type: "",
|
|
|
|
|
sign_date: "",
|
|
|
|
|
start_date: "",
|
|
|
|
|
end_date: "",
|
|
|
|
|
apply_date: "",
|
|
|
|
|
perform_period: "",
|
|
|
|
|
pay_method: "",
|
|
|
|
|
fund_source: "",
|
|
|
|
|
tender_agent: "",
|
|
|
|
|
purchase_method: "",
|
|
|
|
|
perform_status: "",
|
|
|
|
|
is_accepted: false,
|
|
|
|
|
purchase_category: "",
|
|
|
|
|
handler_admin_ids: "",
|
|
|
|
|
handler_admin_ids_array: [],
|
|
|
|
|
apply_handler_id: null,
|
|
|
|
|
purchase_handler_id: null,
|
|
|
|
|
owner_department_id: null,
|
|
|
|
|
owner_department_ids: "",
|
|
|
|
|
owner_department_ids_array: [],
|
|
|
|
|
remark: "",
|
|
|
|
|
attachment_id: null,
|
|
|
|
|
pay_plans: [],
|
|
|
|
|
savedAmountTotal: null,
|
|
|
|
|
},
|
|
|
|
|
rules: {
|
|
|
|
|
contract_no: [{ required: true, message: "请填写合同编号", trigger: "blur" }],
|
|
|
|
|
@ -188,20 +355,138 @@ export default {
|
|
|
|
|
displayText() {
|
|
|
|
|
return this.hasValue ? this.display || "" : "";
|
|
|
|
|
},
|
|
|
|
|
uploadAction() {
|
|
|
|
|
// 使用环境变量中的上传API,如果没有则使用默认路径
|
|
|
|
|
const uploadAPI = process.env.VUE_APP_UPLOAD_API;
|
|
|
|
|
if (uploadAPI) {
|
|
|
|
|
return uploadAPI;
|
|
|
|
|
}
|
|
|
|
|
// 如果没有配置,使用 baseURL + /upload-file
|
|
|
|
|
const baseURL = process.env.VUE_APP_BASE_API || '';
|
|
|
|
|
// 确保路径正确:baseURL 可能是 /api 或 /api/,需要统一处理
|
|
|
|
|
const apiPath = baseURL.endsWith('/') ? baseURL.slice(0, -1) : baseURL;
|
|
|
|
|
return `${apiPath}/upload-file`;
|
|
|
|
|
},
|
|
|
|
|
uploadHeaders() {
|
|
|
|
|
const token = getToken();
|
|
|
|
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
'form.amount_type'(newVal, oldVal) {
|
|
|
|
|
if (newVal === 'open') {
|
|
|
|
|
if (oldVal === 'fixed' && this.form.amount_total) {
|
|
|
|
|
this.form.savedAmountTotal = this.form.amount_total;
|
|
|
|
|
}
|
|
|
|
|
this.form.amount_total = null;
|
|
|
|
|
} else if (newVal === 'fixed') {
|
|
|
|
|
if (this.form.savedAmountTotal !== null) {
|
|
|
|
|
this.form.amount_total = this.form.savedAmountTotal;
|
|
|
|
|
this.form.savedAmountTotal = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
openDialog() {
|
|
|
|
|
this.dialogVisible = true;
|
|
|
|
|
},
|
|
|
|
|
handleAmountInputKeydown(event) {
|
|
|
|
|
if (this.form.amount_type === 'open') {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handleAmountInputFocus(event) {
|
|
|
|
|
if (this.form.amount_type === 'open') {
|
|
|
|
|
event.target.blur();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
beforeUpload(file) {
|
|
|
|
|
const isValidType = ['image/jpeg', 'image/png', 'image/jpg', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'].includes(file.type);
|
|
|
|
|
const isLt10M = file.size / 1024 / 1024 < 10;
|
|
|
|
|
|
|
|
|
|
if (!isValidType) {
|
|
|
|
|
Message.error('只能上传 JPG/PNG/PDF/DOC/DOCX/XLS/XLSX 格式的文件!');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!isLt10M) {
|
|
|
|
|
Message.error('文件大小不能超过 10MB!');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
handleUploadSuccess(response) {
|
|
|
|
|
if (response.code === 0) {
|
|
|
|
|
const fileData = response.data;
|
|
|
|
|
this.form.attachment_id = fileData.id;
|
|
|
|
|
this.attachmentFileList = [{
|
|
|
|
|
name: fileData.original_name || fileData.name || '文件',
|
|
|
|
|
url: fileData.url || (fileData.folder && fileData.name ? `${fileData.folder}/${fileData.name}` : ''),
|
|
|
|
|
uid: fileData.id,
|
|
|
|
|
id: fileData.id
|
|
|
|
|
}];
|
|
|
|
|
Message.success('上传成功');
|
|
|
|
|
} else {
|
|
|
|
|
Message.error('上传失败:' + (response.msg || '未知错误'));
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handleUploadError(error) {
|
|
|
|
|
console.error('上传错误:', error);
|
|
|
|
|
Message.error('上传失败,请重试');
|
|
|
|
|
},
|
|
|
|
|
handleUploadRemove() {
|
|
|
|
|
this.form.attachment_id = null;
|
|
|
|
|
this.attachmentFileList = [];
|
|
|
|
|
Message.success('已移除文件');
|
|
|
|
|
},
|
|
|
|
|
async loadUserList() {
|
|
|
|
|
try {
|
|
|
|
|
// 使用 common.js 中的函数,确保路径正确
|
|
|
|
|
// 注意:响应拦截器返回的是 res.data,所以这里 response 已经是 data 部分
|
|
|
|
|
const response = await userListNoAuth({ rows: 1000 });
|
|
|
|
|
// simpleUser 返回分页数据,格式: { data: [...], current_page: 1, ... }
|
|
|
|
|
if (response && response.data && Array.isArray(response.data)) {
|
|
|
|
|
this.userList = response.data;
|
|
|
|
|
} else if (Array.isArray(response)) {
|
|
|
|
|
// 兼容直接返回数组的情况
|
|
|
|
|
this.userList = response;
|
|
|
|
|
} else {
|
|
|
|
|
this.userList = [];
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取用户列表失败', e);
|
|
|
|
|
Message.error('获取用户列表失败:' + (e.message || '未知错误'));
|
|
|
|
|
this.userList = [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async loadDepartmentList() {
|
|
|
|
|
try {
|
|
|
|
|
// 使用 common.js 中的函数,确保路径正确
|
|
|
|
|
// 注意:响应拦截器返回的是 res.data,所以这里 response 已经是 data 部分(数组)
|
|
|
|
|
const response = await departmentListNoAuth({ rows: 1000 });
|
|
|
|
|
// simpleDepartment 直接返回数组
|
|
|
|
|
if (Array.isArray(response)) {
|
|
|
|
|
this.departmentList = response;
|
|
|
|
|
} else {
|
|
|
|
|
this.departmentList = [];
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('获取部门列表失败', e);
|
|
|
|
|
Message.error('获取部门列表失败:' + (e.message || '未知错误'));
|
|
|
|
|
this.departmentList = [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async onDialogOpen() {
|
|
|
|
|
this.resetForm();
|
|
|
|
|
if (this.flowId) {
|
|
|
|
|
// 先检查是否存在相同flow_id的合同,如果存在则加载合同数据
|
|
|
|
|
const contractExists = await this.loadExistingContract();
|
|
|
|
|
if (!contractExists) {
|
|
|
|
|
// 如果不存在合同,则从流程数据预填
|
|
|
|
|
this.loadFlowDataAndPrefill();
|
|
|
|
|
}
|
|
|
|
|
await this.loadUserList();
|
|
|
|
|
await this.loadDepartmentList();
|
|
|
|
|
// 优先尝试加载已有合同(通过合同ID或flowId)
|
|
|
|
|
const contractExists = await this.loadExistingContract();
|
|
|
|
|
// 如果找不到已有合同,且提供了flowId,则从流程数据预填
|
|
|
|
|
if (!contractExists && this.flowId) {
|
|
|
|
|
this.loadFlowDataAndPrefill();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onDialogClose() {
|
|
|
|
|
@ -211,42 +496,127 @@ export default {
|
|
|
|
|
this.form = {
|
|
|
|
|
contract_no: "",
|
|
|
|
|
title: "",
|
|
|
|
|
main_content: "",
|
|
|
|
|
party_a: "",
|
|
|
|
|
party_b: "",
|
|
|
|
|
amount_total: 0,
|
|
|
|
|
amount_total: null,
|
|
|
|
|
amount_type: "fixed",
|
|
|
|
|
status: "draft",
|
|
|
|
|
amount_description: "",
|
|
|
|
|
budget_amount: 0,
|
|
|
|
|
contract_type: "",
|
|
|
|
|
sign_date: "",
|
|
|
|
|
start_date: "",
|
|
|
|
|
end_date: "",
|
|
|
|
|
apply_date: "",
|
|
|
|
|
perform_period: "",
|
|
|
|
|
pay_method: "",
|
|
|
|
|
fund_source: "",
|
|
|
|
|
tender_agent: "",
|
|
|
|
|
purchase_method: "",
|
|
|
|
|
perform_status: "",
|
|
|
|
|
is_accepted: false,
|
|
|
|
|
purchase_category: "",
|
|
|
|
|
handler_admin_ids: "",
|
|
|
|
|
handler_admin_ids_array: [],
|
|
|
|
|
apply_handler_id: null,
|
|
|
|
|
purchase_handler_id: null,
|
|
|
|
|
owner_department_id: null,
|
|
|
|
|
owner_department_ids: "",
|
|
|
|
|
owner_department_ids_array: [],
|
|
|
|
|
remark: "",
|
|
|
|
|
attachment_id: null,
|
|
|
|
|
pay_plans: [],
|
|
|
|
|
savedAmountTotal: null,
|
|
|
|
|
};
|
|
|
|
|
this.attachmentFileList = [];
|
|
|
|
|
if (this.$refs.formRef) {
|
|
|
|
|
this.$refs.formRef.clearValidate();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async loadExistingContract() {
|
|
|
|
|
if (!this.flowId) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.loading = true;
|
|
|
|
|
try {
|
|
|
|
|
const res = await getContractByFlowId(this.flowId);
|
|
|
|
|
const contractData = res?.data || res;
|
|
|
|
|
let contractData = null;
|
|
|
|
|
|
|
|
|
|
// 优先通过合同ID加载(如果字段已有值)
|
|
|
|
|
if (this.hasValue && this.normalizedValue) {
|
|
|
|
|
try {
|
|
|
|
|
const contractId = parseInt(this.normalizedValue);
|
|
|
|
|
if (contractId) {
|
|
|
|
|
// 通过合同ID获取合同详情(使用完整路径,与其他API调用保持一致)
|
|
|
|
|
const res = await request.get(`/api/budget/contracts/${contractId}`);
|
|
|
|
|
// 响应拦截器返回的是 res.data,所以这里 res 已经是合同数据
|
|
|
|
|
if (res && res.id) {
|
|
|
|
|
contractData = res;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('通过合同ID加载失败,尝试通过flowId加载:', e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果通过ID加载失败,且提供了flowId,尝试通过flowId加载
|
|
|
|
|
if (!contractData && this.flowId) {
|
|
|
|
|
const res = await getContractByFlowId(this.flowId);
|
|
|
|
|
contractData = res?.data || res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (contractData) {
|
|
|
|
|
// 填充合同数据
|
|
|
|
|
this.form.contract_no = contractData.contract_no || "";
|
|
|
|
|
this.form.title = contractData.title || "";
|
|
|
|
|
this.form.main_content = contractData.main_content || "";
|
|
|
|
|
this.form.party_a = contractData.party_a || "";
|
|
|
|
|
this.form.party_b = contractData.party_b || "";
|
|
|
|
|
this.form.amount_total = contractData.amount_total || 0;
|
|
|
|
|
this.form.amount_total = contractData.amount_total !== null && contractData.amount_total !== undefined ? contractData.amount_total : null;
|
|
|
|
|
this.form.amount_type = contractData.amount_type || "fixed";
|
|
|
|
|
this.form.status = contractData.status || "draft";
|
|
|
|
|
this.form.amount_description = contractData.amount_description || "";
|
|
|
|
|
this.form.budget_amount = contractData.budget_amount || 0;
|
|
|
|
|
this.form.contract_type = contractData.contract_type || "";
|
|
|
|
|
this.form.sign_date = contractData.sign_date || "";
|
|
|
|
|
this.form.start_date = contractData.start_date || "";
|
|
|
|
|
this.form.end_date = contractData.end_date || "";
|
|
|
|
|
this.form.apply_date = contractData.apply_date || "";
|
|
|
|
|
this.form.perform_period = contractData.perform_period || "";
|
|
|
|
|
this.form.pay_method = contractData.pay_method || "";
|
|
|
|
|
this.form.fund_source = contractData.fund_source || "";
|
|
|
|
|
this.form.tender_agent = contractData.tender_agent || "";
|
|
|
|
|
this.form.purchase_method = contractData.purchase_method || "";
|
|
|
|
|
this.form.perform_status = contractData.perform_status || "";
|
|
|
|
|
this.form.is_accepted = contractData.is_accepted || false;
|
|
|
|
|
this.form.purchase_category = contractData.purchase_category || "";
|
|
|
|
|
this.form.apply_handler_id = contractData.apply_handler_id || null;
|
|
|
|
|
this.form.purchase_handler_id = contractData.purchase_handler_id || null;
|
|
|
|
|
this.form.owner_department_id = contractData.owner_department_id || null;
|
|
|
|
|
this.form.remark = contractData.remark || "";
|
|
|
|
|
this.form.attachment_id = contractData.attachment_id || null;
|
|
|
|
|
|
|
|
|
|
// 处理科室多选:转换为字符串数组(因为下拉组件的value是字符串)
|
|
|
|
|
if (contractData.owner_department_ids) {
|
|
|
|
|
const ids = typeof contractData.owner_department_ids === 'string'
|
|
|
|
|
? contractData.owner_department_ids.split(',').filter(id => id)
|
|
|
|
|
: (Array.isArray(contractData.owner_department_ids) ? contractData.owner_department_ids : []);
|
|
|
|
|
this.form.owner_department_ids_array = ids.map(id => String(id));
|
|
|
|
|
} else {
|
|
|
|
|
this.form.owner_department_ids_array = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理经办人多选:转换为字符串数组(因为下拉组件的value是字符串)
|
|
|
|
|
if (contractData.handler_admin_ids) {
|
|
|
|
|
const ids = typeof contractData.handler_admin_ids === 'string'
|
|
|
|
|
? contractData.handler_admin_ids.split(',').filter(id => id)
|
|
|
|
|
: (Array.isArray(contractData.handler_admin_ids) ? contractData.handler_admin_ids : []);
|
|
|
|
|
this.form.handler_admin_ids_array = ids.map(id => String(id));
|
|
|
|
|
} else {
|
|
|
|
|
this.form.handler_admin_ids_array = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理附件
|
|
|
|
|
if (contractData.attachment_id && contractData.attachment) {
|
|
|
|
|
this.attachmentFileList = [{
|
|
|
|
|
name: contractData.attachment.original_name || contractData.attachment.name || '文件',
|
|
|
|
|
url: contractData.attachment.url || (contractData.attachment.folder && contractData.attachment.name ? `${contractData.attachment.folder}/${contractData.attachment.name}` : ''),
|
|
|
|
|
uid: contractData.attachment.id,
|
|
|
|
|
id: contractData.attachment.id
|
|
|
|
|
}];
|
|
|
|
|
} else {
|
|
|
|
|
this.attachmentFileList = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 填充付款计划
|
|
|
|
|
if (contractData.pay_plans && Array.isArray(contractData.pay_plans)) {
|
|
|
|
|
@ -278,7 +648,6 @@ export default {
|
|
|
|
|
|
|
|
|
|
this.loading = true;
|
|
|
|
|
try {
|
|
|
|
|
// 并行请求flow详情和合同设置
|
|
|
|
|
const [flowDetailRes, settingsRes] = await Promise.all([
|
|
|
|
|
getFlowDetailForContract(this.flowId),
|
|
|
|
|
getContractSettings(this.flowId),
|
|
|
|
|
@ -295,7 +664,6 @@ export default {
|
|
|
|
|
const contractMapping = (settings?.contract_field_mapping || []);
|
|
|
|
|
const payplanMapping = (settings?.payplan_field_mapping || []);
|
|
|
|
|
|
|
|
|
|
// 根据映射填充合同字段
|
|
|
|
|
if (contractMapping && contractMapping.length > 0) {
|
|
|
|
|
contractMapping.forEach((map) => {
|
|
|
|
|
const budgetField = map.budget_field;
|
|
|
|
|
@ -308,9 +676,7 @@ export default {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 填充付款计划
|
|
|
|
|
if (settings.oa_custom_model_id_for_payplan && flowDetail.customModel) {
|
|
|
|
|
// 尝试从子表提取
|
|
|
|
|
const payplanField = flowDetail.customModel.fields.find(
|
|
|
|
|
(f) => f.type === "relation" && f.sub_custom_model_id === settings.oa_custom_model_id_for_payplan
|
|
|
|
|
);
|
|
|
|
|
@ -328,7 +694,6 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (flowData.pay_plans && Array.isArray(flowData.pay_plans)) {
|
|
|
|
|
// 从主表的pay_plans字段提取
|
|
|
|
|
this.form.pay_plans = this.mapPayPlans(flowData.pay_plans, payplanMapping);
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
@ -387,19 +752,53 @@ export default {
|
|
|
|
|
|
|
|
|
|
this.saving = true;
|
|
|
|
|
try {
|
|
|
|
|
// 处理科室多选:将数组转换为逗号分隔的字符串
|
|
|
|
|
const ownerDepartmentIds = Array.isArray(this.form.owner_department_ids_array)
|
|
|
|
|
? this.form.owner_department_ids_array.join(',')
|
|
|
|
|
: (this.form.owner_department_ids || '');
|
|
|
|
|
|
|
|
|
|
// 处理经办人多选:将数组转换为逗号分隔的字符串
|
|
|
|
|
const handlerAdminIds = Array.isArray(this.form.handler_admin_ids_array)
|
|
|
|
|
? this.form.handler_admin_ids_array.join(',')
|
|
|
|
|
: (this.form.handler_admin_ids || '');
|
|
|
|
|
|
|
|
|
|
// 处理金额:开口合同时,amount_total 应该为 null
|
|
|
|
|
const amountTotal = this.form.amount_type === 'open' ? null : (this.form.amount_total || 0);
|
|
|
|
|
|
|
|
|
|
// 如果已有合同ID,传递合同ID用于更新
|
|
|
|
|
const contractId = this.hasValue && this.normalizedValue ? parseInt(this.normalizedValue) : null;
|
|
|
|
|
|
|
|
|
|
const payload = {
|
|
|
|
|
contract_id: contractId, // 如果提供,则更新该合同
|
|
|
|
|
flow_id: this.flowId ? parseInt(this.flowId) : null,
|
|
|
|
|
contract: {
|
|
|
|
|
contract_no: this.form.contract_no,
|
|
|
|
|
title: this.form.title,
|
|
|
|
|
party_a: this.form.party_a,
|
|
|
|
|
party_b: this.form.party_b,
|
|
|
|
|
amount_total: this.form.amount_total,
|
|
|
|
|
main_content: this.form.main_content || "",
|
|
|
|
|
party_a: this.form.party_a || "",
|
|
|
|
|
party_b: this.form.party_b || "",
|
|
|
|
|
amount_total: amountTotal,
|
|
|
|
|
amount_type: this.form.amount_type,
|
|
|
|
|
status: this.form.status,
|
|
|
|
|
amount_description: this.form.amount_description || "",
|
|
|
|
|
budget_amount: this.form.budget_amount || 0,
|
|
|
|
|
contract_type: this.form.contract_type || "",
|
|
|
|
|
sign_date: this.form.sign_date || null,
|
|
|
|
|
start_date: this.form.start_date || null,
|
|
|
|
|
end_date: this.form.end_date || null,
|
|
|
|
|
apply_date: this.form.apply_date || null,
|
|
|
|
|
perform_period: this.form.perform_period || "",
|
|
|
|
|
pay_method: this.form.pay_method || "",
|
|
|
|
|
fund_source: this.form.fund_source || "",
|
|
|
|
|
tender_agent: this.form.tender_agent || "",
|
|
|
|
|
purchase_method: this.form.purchase_method || "",
|
|
|
|
|
perform_status: this.form.perform_status || "",
|
|
|
|
|
is_accepted: this.form.is_accepted || false,
|
|
|
|
|
purchase_category: this.form.purchase_category || "",
|
|
|
|
|
handler_admin_ids: handlerAdminIds,
|
|
|
|
|
apply_handler_id: this.form.apply_handler_id,
|
|
|
|
|
purchase_handler_id: this.form.purchase_handler_id,
|
|
|
|
|
owner_department_id: this.form.owner_department_id,
|
|
|
|
|
owner_department_ids: ownerDepartmentIds,
|
|
|
|
|
remark: this.form.remark || "",
|
|
|
|
|
attachment_id: this.form.attachment_id,
|
|
|
|
|
},
|
|
|
|
|
pay_plans: this.form.pay_plans.map((p) => ({
|
|
|
|
|
phase_no: p.phase_no,
|
|
|
|
|
@ -416,9 +815,9 @@ export default {
|
|
|
|
|
this.$emit("input", String(data.id));
|
|
|
|
|
this.$emit("update:display", displayText);
|
|
|
|
|
this.dialogVisible = false;
|
|
|
|
|
Message.success("合同创建成功");
|
|
|
|
|
Message.success(contractId ? "合同更新成功" : "合同创建成功");
|
|
|
|
|
} else {
|
|
|
|
|
Message.error("创建合同失败");
|
|
|
|
|
Message.error(contractId ? "更新合同失败" : "创建合同失败");
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("创建合同失败:", e);
|
|
|
|
|
@ -466,5 +865,20 @@ export default {
|
|
|
|
|
.mb-16 {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
.payplan-table {
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.readonly-amount :deep(.el-input__inner) {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
color: #909399;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.readonly-amount :deep(.el-input-number__decrease),
|
|
|
|
|
.readonly-amount :deep(.el-input-number__increase) {
|
|
|
|
|
display: none;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|