weizong song 6 months ago
parent 58cb6e913e
commit e9f786bb92

@ -117,3 +117,9 @@ echo ""

@ -494,8 +494,17 @@ export default {
onDialogOpen() {
this.selectionError = "";
this.selectedBudgetIds = [];
//
this.totalAmount = this.extractPaymentAmountFromForm();
// DOM
this.$nextTick(() => {
//
this.totalAmount = this.extractPaymentAmountFromForm();
// DOM
if (this.totalAmount === 0) {
setTimeout(() => {
this.totalAmount = this.extractPaymentAmountFromForm();
}, 100);
}
});
this.ensureInit();
},
onDialogClose() {
@ -658,20 +667,145 @@ export default {
},
extractPaymentAmountFromForm() {
// DOM label
// labelplaceholder
try {
// 1 label
const labels = Array.from(document.querySelectorAll(".el-form-item__label"));
const labelEl = labels.find((el) => (el.innerText || "").includes("本次付款金额"));
if (!labelEl) return 0;
const formItem = labelEl.closest(".el-form-item");
const content = formItem ? formItem.querySelector(".el-form-item__content") : null;
const input =
(content && content.querySelector("input")) ||
(content && content.querySelector("textarea")) ||
null;
const raw = input ? String(input.value || "") : "";
const num = parseFloat(raw.replace(/,/g, "").replace(/[^\d.-]/g, ""));
return isNaN(num) ? 0 : num;
let labelEl = labels.find((el) => {
const text = (el.innerText || el.textContent || "").trim();
return text.includes("本次付款金额") || text.includes("付款金额");
});
if (labelEl) {
const formItem = labelEl.closest(".el-form-item");
if (formItem) {
const content = formItem.querySelector(".el-form-item__content");
if (content) {
//
let input = content.querySelector("input") ||
content.querySelector("textarea") ||
content.querySelector(".el-input__inner") ||
content.querySelector("input[type='text']") ||
content.querySelector("input[type='number']");
if (input) {
const raw = String(input.value || input.getAttribute("value") || "");
const num = parseFloat(raw.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
// /view
// span
const displayEl = content.querySelector("span") ||
content.querySelector("div") ||
content;
if (displayEl) {
//
const raw = displayEl.innerText || displayEl.textContent || displayEl.getAttribute("value") || "";
const num = parseFloat(raw.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
}
}
}
// 2 data-field-name ""
const formItems = Array.from(document.querySelectorAll(".el-form-item[data-field-name]"));
for (const formItem of formItems) {
const fieldName = formItem.getAttribute("data-field-name") || "";
const label = formItem.querySelector(".el-form-item__label");
const labelText = label ? (label.innerText || label.textContent || "").trim() : "";
if (fieldName.includes("付款金额") || fieldName.includes("payment_amount") ||
labelText.includes("本次付款金额") || labelText.includes("付款金额")) {
const content = formItem.querySelector(".el-form-item__content");
if (content) {
//
let input = content.querySelector("input") ||
content.querySelector("textarea") ||
content.querySelector(".el-input__inner") ||
content.querySelector("input[type='text']") ||
content.querySelector("input[type='number']");
if (input) {
const raw = String(input.value || input.getAttribute("value") || "");
const num = parseFloat(raw.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
// /view
const displayEl = content.querySelector("span") ||
content.querySelector("div") ||
content;
if (displayEl) {
const raw = displayEl.innerText || displayEl.textContent || displayEl.getAttribute("value") || "";
const num = parseFloat(raw.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
}
}
}
// 3 placeholder
const inputs = Array.from(document.querySelectorAll("input, textarea, .el-input__inner"));
for (const input of inputs) {
const placeholder = input.getAttribute("placeholder") || "";
if (placeholder.includes("本次付款金额") || placeholder.includes("付款金额")) {
const raw = String(input.value || input.getAttribute("value") || "");
const num = parseFloat(raw.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
}
// 4 span/div
// form-item label ""
const allFormItems = Array.from(document.querySelectorAll(".el-form-item"));
for (const formItem of allFormItems) {
const label = formItem.querySelector(".el-form-item__label");
const labelText = label ? (label.innerText || label.textContent || "").trim() : "";
if (labelText.includes("本次付款金额") || labelText.includes("付款金额")) {
const content = formItem.querySelector(".el-form-item__content");
if (content) {
//
const displayEls = content.querySelectorAll("span, div, p");
for (const el of displayEls) {
const text = (el.innerText || el.textContent || "").trim();
//
if (text && /[\d.,]+/.test(text)) {
const num = parseFloat(text.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
}
// content
const contentText = (content.innerText || content.textContent || "").trim();
if (contentText && /[\d.,]+/.test(contentText)) {
const num = parseFloat(contentText.replace(/,/g, "").replace(/[^\d.-]/g, ""));
if (!isNaN(num) && num > 0) {
return num;
}
}
}
}
}
return 0;
} catch (e) {
console.warn("[BudgetSourcePickerField] 提取本次付款金额失败:", e);
return 0;
}
},

@ -38,55 +38,52 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="合同编号" prop="contract_no">
<el-input v-model="form.contract_no" />
<el-input v-model="form.contract_no_without_prefix">
<template slot="prepend">CZHT</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同名称">
<el-form-item label="合同名称" prop="title">
<el-input v-model="form.title" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="合同主要内容">
<el-form-item label="合同主要内容" prop="main_content">
<el-input v-model="form.main_content" type="textarea" :rows="3" />
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="甲方">
<el-form-item label="甲方" prop="party_a">
<el-input v-model="form.party_a" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="乙方">
<el-form-item label="乙方" prop="party_b">
<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-form-item label="合同金额(元)" prop="amount_total">
<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' }"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目预算金额(元)">
<el-form-item label="项目预算金额(元)" prop="budget_amount">
<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-form-item label="金额类型" prop="amount_type">
<el-select v-model="form.amount_type" placeholder="请选择金额类型" style="width: 100%">
<el-option label="闭口合同(金额确定)" value="fixed" />
<el-option label="框架协议/开口合同(金额不确定)" value="open" />
@ -94,23 +91,26 @@
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.amount_type === 'open'">
<el-form-item label="金额说明">
<el-form-item label="金额说明" prop="amount_description">
<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-form-item label="合同类型" prop="contract_type_id">
<el-select v-model="form.contract_type_id" placeholder="请选择合同类型" filterable clearable style="width: 100%">
<el-option
v-for="type in contractTypeOptions"
:key="type.id"
:label="type.name"
:value="type.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="签订日期">
<el-form-item label="签订日期" prop="sign_date">
<el-date-picker
v-model="form.sign_date"
type="date"
@ -120,7 +120,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="申请日期">
<el-form-item label="申请日期" prop="apply_date">
<el-date-picker
v-model="form.apply_date"
type="date"
@ -130,18 +130,54 @@
</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-row :gutter="20">
<el-col :span="12">
<el-form-item label="付款方式">
<el-form-item label="合同履行期" prop="perform_period">
<el-input v-model="form.perform_period" placeholder="例如2025-01-01 至 2025-12-31" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="付款方式" prop="pay_method">
<el-input v-model="form.pay_method" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="预算年度" prop="fund_source_year_id">
<el-select v-model="form.fund_source_year_id" placeholder="请选择预算年度" filterable clearable style="width: 100%" @change="handleFundSourceYearChange">
<el-option
v-for="year in budgetYearOptions"
:key="year.value"
:label="year.label"
:value="year.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经费来源">
<el-input v-model="form.fund_source" />
<el-form-item label="项目经费来源" prop="fund_source_budget_data_id">
<el-select v-model="form.fund_source_budget_data_id" placeholder="请选择项目经费来源" filterable clearable style="width: 100%">
<el-option-group
v-for="group in fundSourceOptions"
:key="group.label"
:label="group.label"
>
<el-option
v-for="option in group.options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-option-group>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否为政府采购" prop="is_government_purchase">
<el-switch v-model="form.is_government_purchase" />
</el-form-item>
</el-col>
</el-row>
@ -152,13 +188,14 @@
</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-form-item label="采购方式" prop="purchase_method_id">
<el-select v-model="form.purchase_method_id" placeholder="请选择采购方式" filterable clearable style="width: 100%">
<el-option
v-for="method in purchaseMethodOptions"
:key="method.id"
:label="method.name"
:value="method.id"
/>
</el-select>
</el-form-item>
</el-col>
@ -170,39 +207,41 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否验收">
<el-form-item label="是否验收" prop="is_accepted">
<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-form-item label="采购类别" prop="purchase_category_id">
<el-select v-model="form.purchase_category_id" placeholder="请选择采购类别" filterable clearable style="width: 100%">
<el-option
v-for="category in purchaseCategoryOptions"
:key="category.id"
:label="category.name"
:value="category.id"
/>
</el-select>
</el-form-item>
<el-form-item label="科室">
<el-form-item label="科室" prop="owner_department_ids">
<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-form-item label="合同签订/变更经办人" prop="handler_admin_ids">
<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-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请科室经办人">
<el-form-item label="申请科室经办人" prop="apply_handler_id">
<el-select v-model="form.apply_handler_id_array" placeholder="请选择" filterable multiple 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-col>
<el-col :span="12">
<el-form-item label="采购科室经办人">
<el-form-item label="采购科室经办人" prop="purchase_handler_id">
<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>
@ -231,10 +270,11 @@
</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 payplan-table" style="width: 100%">
<el-form-item prop="pay_plans" style="margin-bottom: 0;">
<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 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" />
@ -267,7 +307,8 @@
<el-button type="danger" link size="small" @click="removePayPlan($index)"></el-button>
</template>
</el-table-column>
</el-table>
</el-table>
</el-form-item>
</el-form>
<template #footer>
@ -287,6 +328,7 @@ import {
getContractSettings,
getContractByFlowId,
createContract,
getBudgetYearOptions,
} from "@/api/flow";
import { userListNoAuth, departmentListNoAuth } from "@/api/common";
@ -307,8 +349,14 @@ export default {
userList: [],
departmentList: [],
attachmentFileList: [],
budgetYearOptions: [],
fundSourceOptions: [],
purchaseCategoryOptions: [],
contractTypeOptions: [],
purchaseMethodOptions: [],
form: {
contract_no: "",
contract_no_without_prefix: "",
title: "",
main_content: "",
party_a: "",
@ -323,11 +371,15 @@ export default {
perform_period: "",
pay_method: "",
fund_source: "",
fund_source_year_id: null,
fund_source_budget_data_id: null,
is_government_purchase: false,
tender_agent: "",
purchase_method: "",
perform_status: "",
is_accepted: false,
purchase_category: "",
purchase_category_id: null,
handler_admin_ids: "",
handler_admin_ids_array: [],
apply_handler_id: "",
@ -339,11 +391,8 @@ export default {
remark: "",
attachment_id: null,
pay_plans: [],
savedAmountTotal: null,
},
rules: {
contract_no: [{ required: true, message: "请填写合同编号", trigger: "blur" }],
},
rules: {},
};
},
computed: {
@ -374,34 +423,301 @@ export default {
},
},
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;
}
'form.fund_source_year_id'(newVal, oldVal) {
//
if (newVal) {
this.fetchFundSourceOptions(newVal);
} else {
this.fundSourceOptions = [];
this.form.fund_source_budget_data_id = null;
}
//
if (newVal !== oldVal) {
this.form.fund_source_budget_data_id = null;
}
},
//
'form.owner_department_ids_array'() {
if (this.$refs.formRef) {
this.$refs.formRef.validateField('owner_department_ids', () => {});
}
},
'form.handler_admin_ids_array'() {
if (this.$refs.formRef) {
this.$refs.formRef.validateField('handler_admin_ids', () => {});
}
}
},
'form.apply_handler_id_array'() {
if (this.$refs.formRef) {
this.$refs.formRef.validateField('apply_handler_id', () => {});
}
},
'form.pay_plans'() {
if (this.$refs.formRef) {
this.$refs.formRef.validateField('pay_plans', () => {});
}
},
//
'form.amount_type'() {
if (this.$refs.formRef) {
this.$refs.formRef.validateField('amount_total', () => {});
this.$refs.formRef.validateField('amount_description', () => {});
}
},
},
methods: {
openDialog() {
this.dialogVisible = true;
},
handleAmountInputKeydown(event) {
if (this.form.amount_type === 'open') {
event.preventDefault();
event.stopPropagation();
return false;
// ID
isEditMode() {
return this.hasValue && this.normalizedValue && parseInt(this.normalizedValue);
},
//
initRules() {
//
const baseRules = {
contract_no: [{ required: true, message: "请填写合同编号", trigger: "blur" }],
title: [{ required: true, message: "请填写合同名称", trigger: "blur" }],
main_content: [{ required: true, message: "请填写合同主要内容", trigger: "blur" }],
party_a: [{ required: true, message: "请填写甲方", trigger: "blur" }],
party_b: [{ required: true, message: "请填写乙方", trigger: "blur" }],
amount_total: [
{
validator: (rule, value, callback) => {
//
if (this.form.amount_type === 'open') {
callback();
} else if (value === null || value === undefined || value === '') {
callback(new Error('请填写合同金额'));
} else {
callback();
}
},
trigger: ['blur', 'change']
}
],
amount_type: [{ required: true, message: "请选择金额类型", trigger: "change" }],
amount_description: [
{
validator: (rule, value, callback) => {
if (this.form.amount_type === 'open' && !value) {
callback(new Error('开口合同时,金额说明不能为空'));
} else {
callback();
}
},
trigger: 'blur'
}
],
budget_amount: [{ required: true, message: "请填写项目预算金额", trigger: "blur" }],
contract_type_id: [{ required: true, message: "请选择合同类型", trigger: "change" }],
sign_date: [{ required: true, message: "请选择签订日期", trigger: "change" }],
apply_date: [{ required: true, message: "请选择申请日期", trigger: "change" }],
perform_period: [{ required: true, message: "请填写合同履行期", trigger: "blur" }],
pay_method: [{ required: true, message: "请填写付款方式", trigger: "blur" }],
};
//
if (!this.isEditMode()) {
this.rules = baseRules;
return;
}
//
this.rules = {
...baseRules,
fund_source_year_id: [{ required: true, message: "请选择预算年度", trigger: "change" }],
fund_source_budget_data_id: [{ required: true, message: "请选择项目经费来源", trigger: "change" }],
is_government_purchase: [{ required: true, message: "请选择是否为政府采购", trigger: "change" }],
purchase_category_id: [{ required: true, message: "请选择采购类别", trigger: "change" }],
purchase_method_id: [{ required: true, message: "请选择采购方式", trigger: "change" }],
is_accepted: [{ required: true, message: "请选择是否验收", trigger: "change" }],
handler_admin_ids: [
{
validator: (rule, value, callback) => {
const ids = this.form.handler_admin_ids_array;
if (!ids || !Array.isArray(ids) || ids.length === 0) {
callback(new Error('请选择合同签订/变更经办人'));
} else {
callback();
}
},
trigger: ['change', 'blur']
}
],
apply_handler_id: [
{
validator: (rule, value, callback) => {
const ids = this.form.apply_handler_id_array;
if (!ids || !Array.isArray(ids) || ids.length === 0) {
callback(new Error('请选择申请科室经办人'));
} else {
callback();
}
},
trigger: ['change', 'blur']
}
],
owner_department_ids: [
{
validator: (rule, value, callback) => {
const ids = this.form.owner_department_ids_array;
if (!ids || !Array.isArray(ids) || ids.length === 0) {
callback(new Error('请选择科室'));
} else {
callback();
}
},
trigger: ['change', 'blur']
}
],
purchase_handler_id: [{ required: true, message: "请选择采购科室经办人", trigger: "change" }],
pay_plans: [
{
validator: (rule, value, callback) => {
if (!value || !Array.isArray(value) || value.length === 0) {
callback(new Error('至少需要一条付款计划'));
return;
}
const errors = [];
value.forEach((plan, index) => {
const planErrors = [];
if (!plan.phase_no || plan.phase_no <= 0) {
planErrors.push('期次');
}
if (!plan.due_date) {
planErrors.push('到期日期');
}
if (!plan.amount_plan || plan.amount_plan <= 0) {
planErrors.push('金额');
}
if (planErrors.length > 0) {
errors.push(`${index + 1}条付款计划的${planErrors.join('、')}不能为空`);
}
});
if (errors.length > 0) {
callback(new Error(errors[0]));
} else {
callback();
}
},
trigger: ['change', 'blur']
}
]
};
},
handleFundSourceYearChange() {
this.form.fund_source_budget_data_id = null;
if (this.form.fund_source_year_id) {
this.fetchFundSourceOptions(this.form.fund_source_year_id);
}
},
async fetchBudgetYearOptions() {
try {
const response = await getBudgetYearOptions(false);
const arr = Array.isArray(response) ? response : (response?.data || []);
this.budgetYearOptions = arr.map((y) => ({
value: y.value,
label: y.label || (y.year ? `${y.year}` : `${y.value}`),
status: y.status, //
}));
// ACTIVE
//
if (this.budgetYearOptions.length > 0 && !this.form.fund_source_year_id && !this.isEditMode()) {
const activeYear = this.budgetYearOptions.find(y => y.status === 'ACTIVE');
if (activeYear) {
this.form.fund_source_year_id = activeYear.value;
await this.fetchFundSourceOptions(activeYear.value);
} else {
this.form.fund_source_year_id = this.budgetYearOptions[0].value;
await this.fetchFundSourceOptions(this.budgetYearOptions[0].value);
}
}
} catch (e) {
console.error('获取预算年度列表失败', e);
Message.error('获取预算年度列表失败:' + (e.message || '未知错误'));
this.budgetYearOptions = [];
}
},
handleAmountInputFocus(event) {
if (this.form.amount_type === 'open') {
event.target.blur();
async fetchFundSourceOptions(yearId) {
if (!yearId) {
this.fundSourceOptions = [];
return;
}
try {
// res.data response
const response = await request.get('/api/budget/contracts/fund-source-options', { params: { year_id: yearId } });
//
if (Array.isArray(response)) {
this.fundSourceOptions = response;
} else if (response && response.code === 0 && Array.isArray(response.data)) {
// fallback
this.fundSourceOptions = response.data;
} else {
this.fundSourceOptions = [];
}
} catch (e) {
console.error('获取项目经费来源列表失败', e);
Message.error('获取项目经费来源列表失败:' + (e.message || '未知错误'));
this.fundSourceOptions = [];
}
},
async fetchPurchaseCategoryOptions() {
try {
// res.data response
const response = await request.get('/api/budget/purchase-categories');
//
if (Array.isArray(response)) {
this.purchaseCategoryOptions = response;
} else if (response && response.code === 0 && Array.isArray(response.data)) {
// fallback
this.purchaseCategoryOptions = response.data;
} else {
this.purchaseCategoryOptions = [];
}
} catch (e) {
console.error('获取采购类别列表失败', e);
Message.error('获取采购类别列表失败:' + (e.message || '未知错误'));
this.purchaseCategoryOptions = [];
}
},
async fetchContractTypeOptions() {
try {
// res.data response
// 使 /api/budget/contract-types /all
const response = await request.get('/api/budget/contract-types');
if (Array.isArray(response)) {
this.contractTypeOptions = response;
} else if (response && response.code === 0 && Array.isArray(response.data)) {
// fallback
this.contractTypeOptions = response.data;
} else {
this.contractTypeOptions = [];
}
} catch (e) {
console.error('获取合同类型列表失败', e);
Message.error('获取合同类型列表失败:' + (e.message || '未知错误'));
this.contractTypeOptions = [];
}
},
async fetchPurchaseMethodOptions() {
try {
// res.data response
// 使 /api/budget/purchase-methods /all
const response = await request.get('/api/budget/purchase-methods');
if (Array.isArray(response)) {
this.purchaseMethodOptions = response;
} else if (response && response.code === 0 && Array.isArray(response.data)) {
// fallback
this.purchaseMethodOptions = response.data;
} else {
this.purchaseMethodOptions = [];
}
} catch (e) {
console.error('获取采购方式列表失败', e);
Message.error('获取采购方式列表失败:' + (e.message || '未知错误'));
this.purchaseMethodOptions = [];
}
},
beforeUpload(file) {
@ -483,12 +799,20 @@ export default {
this.resetForm();
await this.loadUserList();
await this.loadDepartmentList();
await this.fetchBudgetYearOptions();
await this.fetchPurchaseCategoryOptions();
await this.fetchContractTypeOptions();
await this.fetchPurchaseMethodOptions();
//
this.initRules();
// IDflowId
const contractExists = await this.loadExistingContract();
const contractExists = await this.loadExistingContract();
// flowId
if (!contractExists && this.flowId) {
this.loadFlowDataAndPrefill();
this.loadFlowDataAndPrefill();
}
// /
this.initRules();
},
onDialogClose() {
//
@ -496,6 +820,7 @@ export default {
resetForm() {
this.form = {
contract_no: "",
contract_no_without_prefix: "",
title: "",
main_content: "",
party_a: "",
@ -504,17 +829,23 @@ export default {
amount_type: "fixed",
amount_description: "",
budget_amount: 0,
contract_type: "",
contract_type_id: null,
contract_type: "", //
sign_date: "",
apply_date: "",
perform_period: "",
pay_method: "",
fund_source: "",
fund_source_year_id: null,
fund_source_budget_data_id: null,
is_government_purchase: false,
tender_agent: "",
purchase_method: "",
purchase_method_id: null,
purchase_method: "", //
perform_status: "",
is_accepted: false,
purchase_category: "",
purchase_category_id: null,
handler_admin_ids: "",
handler_admin_ids_array: [],
apply_handler_id: "",
@ -526,9 +857,9 @@ export default {
remark: "",
attachment_id: null,
pay_plans: [],
savedAmountTotal: null,
};
this.attachmentFileList = [];
this.fundSourceOptions = [];
if (this.$refs.formRef) {
this.$refs.formRef.clearValidate();
}
@ -562,6 +893,14 @@ export default {
}
if (contractData) {
// CZHT
if (contractData.contract_no) {
if (contractData.contract_no.startsWith('CZHT')) {
this.form.contract_no_without_prefix = contractData.contract_no.substring(4);
} else {
this.form.contract_no_without_prefix = contractData.contract_no;
}
}
this.form.contract_no = contractData.contract_no || "";
this.form.title = contractData.title || "";
this.form.main_content = contractData.main_content || "";
@ -571,23 +910,36 @@ export default {
this.form.amount_type = contractData.amount_type || "fixed";
this.form.amount_description = contractData.amount_description || "";
this.form.budget_amount = contractData.budget_amount || 0;
this.form.contract_type = contractData.contract_type || "";
// 使ID使
this.form.contract_type_id = contractData.contract_type_id || null;
this.form.contract_type = contractData.contract_type || ""; //
this.form.sign_date = contractData.sign_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.fund_source_year_id = contractData.fund_source_year_id || null;
this.form.fund_source_budget_data_id = contractData.fund_source_budget_data_id || null;
this.form.is_government_purchase = contractData.is_government_purchase || false;
this.form.tender_agent = contractData.tender_agent || "";
this.form.purchase_method = contractData.purchase_method || "";
// 使ID使
this.form.purchase_method_id = contractData.purchase_method_id || null;
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.purchase_category_id = contractData.purchase_category_id || null;
this.form.apply_handler_id = contractData.apply_handler_id || "";
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;
//
if (this.form.fund_source_year_id) {
await this.fetchFundSourceOptions(this.form.fund_source_year_id);
}
// value
if (contractData.owner_department_ids) {
const ids = typeof contractData.owner_department_ids === 'string'
@ -764,11 +1116,25 @@ export default {
this.saving = true;
try {
// CZHT
let contractNo = this.form.contract_no_without_prefix || '';
if (contractNo && !contractNo.startsWith('CZHT')) {
contractNo = 'CZHT' + contractNo;
}
//
const ownerDepartmentIds = Array.isArray(this.form.owner_department_ids_array)
? this.form.owner_department_ids_array.join(',')
: (this.form.owner_department_ids || '');
// owner_department_id
let ownerDepartmentId = null;
if (Array.isArray(this.form.owner_department_ids_array) && this.form.owner_department_ids_array.length > 0) {
ownerDepartmentId = parseInt(this.form.owner_department_ids_array[0]);
} else if (this.form.owner_department_id) {
ownerDepartmentId = this.form.owner_department_id;
}
//
const handlerAdminIds = Array.isArray(this.form.handler_admin_ids_array)
? this.form.handler_admin_ids_array.join(',')
@ -778,9 +1144,6 @@ export default {
const applyHandlerIds = Array.isArray(this.form.apply_handler_id_array)
? this.form.apply_handler_id_array.filter(id => id).join(',')
: (this.form.apply_handler_id || '');
// amount_total null
const amountTotal = this.form.amount_type === 'open' ? null : (this.form.amount_total || 0);
// IDID
const contractId = this.hasValue && this.normalizedValue ? parseInt(this.normalizedValue) : null;
@ -789,30 +1152,36 @@ export default {
contract_id: contractId, //
flow_id: this.flowId ? parseInt(this.flowId) : null,
contract: {
contract_no: this.form.contract_no,
contract_no: contractNo,
title: this.form.title,
main_content: this.form.main_content || "",
party_a: this.form.party_a || "",
party_b: this.form.party_b || "",
amount_total: amountTotal,
amount_total: this.form.amount_total || 0,
amount_type: this.form.amount_type,
amount_description: this.form.amount_description || "",
budget_amount: this.form.budget_amount || 0,
contract_type: this.form.contract_type || "",
contract_type_id: this.form.contract_type_id,
contract_type: this.form.contract_type || "", //
sign_date: this.form.sign_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 || "",
fund_source_year_id: this.form.fund_source_year_id,
fund_source_budget_data_id: this.form.fund_source_budget_data_id,
is_government_purchase: this.form.is_government_purchase || false,
purchase_category_id: this.form.purchase_category_id,
tender_agent: this.form.tender_agent || "",
purchase_method: this.form.purchase_method || "",
purchase_method_id: this.form.purchase_method_id,
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: applyHandlerIds,
purchase_handler_id: this.form.purchase_handler_id,
owner_department_id: this.form.owner_department_id,
owner_department_id: ownerDepartmentId,
owner_department_ids: ownerDepartmentIds,
remark: this.form.remark || "",
attachment_id: this.form.attachment_id,

@ -1444,15 +1444,43 @@ export default function formBuilder(
) {
isJointly = !!log.is_jointly_sign;
if (log.status > 0 && log.user) {
// 对于 budget-source 类型字段,如果值是 JSON 格式,不显示原始 JSON
// 对于 budget-source 类型字段,优先显示后端返回的 _display 值
let displayValue = value.value;
if (info.type === 'budget-source' && displayValue) {
const strValue = String(displayValue);
// 如果是 JSON 字符串,不显示
if (strValue.trim().startsWith('{') || strValue.trim().startsWith('[')) {
displayValue = ''; // 不显示原始 JSON
} else if (typeof displayValue === 'object' || Array.isArray(displayValue)) {
displayValue = ''; // 不显示对象或数组
if (info.type === 'budget-source') {
// 优先使用主表字段的 _display与主表字段展示一致
const displayFieldName = info.name + '_display';
const mainDisplayValue = target[displayFieldName] || '';
if (mainDisplayValue) {
displayValue = mainDisplayValue;
} else if (displayValue) {
// 如果没有 _display尝试识别并屏蔽 JSON包括双重编码
const strValue = String(displayValue);
// 尝试解析 JSON可能是一层或两层编码
let isJson = false;
try {
const parsed1 = JSON.parse(strValue);
if (typeof parsed1 === 'object' || Array.isArray(parsed1)) {
isJson = true;
} else if (typeof parsed1 === 'string') {
// 双重编码:第一层解析出来是字符串,再试一次
try {
const parsed2 = JSON.parse(parsed1);
if (typeof parsed2 === 'object' || Array.isArray(parsed2)) {
isJson = true;
}
} catch (e2) {
// 不是双重编码的 JSON
}
}
} catch (e) {
// 不是 JSON 字符串
}
// 如果是 JSON包括双重编码不显示
if (isJson || strValue.trim().startsWith('{') || strValue.trim().startsWith('[')) {
displayValue = ''; // 不显示原始 JSON
} else if (typeof displayValue === 'object' || Array.isArray(displayValue)) {
displayValue = ''; // 不显示对象或数组
}
}
}
jointlySignContent.push(
@ -2668,15 +2696,43 @@ export default function formBuilder(
) {
isJointly = !!log.is_jointly_sign;
if (log.status > 0 && log.user) {
// 对于 budget-source 类型字段,如果值是 JSON 格式,不显示原始 JSON
// 对于 budget-source 类型字段,优先显示后端返回的 _display 值
let displayValue = value.value;
if (info.type === 'budget-source' && displayValue) {
const strValue = String(displayValue);
// 如果是 JSON 字符串,不显示
if (strValue.trim().startsWith('{') || strValue.trim().startsWith('[')) {
displayValue = ''; // 不显示原始 JSON
} else if (typeof displayValue === 'object' || Array.isArray(displayValue)) {
displayValue = ''; // 不显示对象或数组
if (info.type === 'budget-source') {
// 优先使用主表字段的 _display与主表字段展示一致
const displayFieldName = info.name + '_display';
const mainDisplayValue = target[displayFieldName] || '';
if (mainDisplayValue) {
displayValue = mainDisplayValue;
} else if (displayValue) {
// 如果没有 _display尝试识别并屏蔽 JSON包括双重编码
const strValue = String(displayValue);
// 尝试解析 JSON可能是一层或两层编码
let isJson = false;
try {
const parsed1 = JSON.parse(strValue);
if (typeof parsed1 === 'object' || Array.isArray(parsed1)) {
isJson = true;
} else if (typeof parsed1 === 'string') {
// 双重编码:第一层解析出来是字符串,再试一次
try {
const parsed2 = JSON.parse(parsed1);
if (typeof parsed2 === 'object' || Array.isArray(parsed2)) {
isJson = true;
}
} catch (e2) {
// 不是双重编码的 JSON
}
}
} catch (e) {
// 不是 JSON 字符串
}
// 如果是 JSON包括双重编码不显示
if (isJson || strValue.trim().startsWith('{') || strValue.trim().startsWith('[')) {
displayValue = ''; // 不显示原始 JSON
} else if (typeof displayValue === 'object' || Array.isArray(displayValue)) {
displayValue = ''; // 不显示对象或数组
}
}
}
jointlySignContent.push(

@ -91,7 +91,7 @@ service.interceptors.response.use(
}
})
}
if (top) {
if (top && typeof top._addError === 'function') {
top._addError(res.msg, `axios-response ${response.config?.url}`, response.config?.params || response.config?.data)
}
return Promise.reject(new Error(res.msg || 'Error'))
@ -107,8 +107,8 @@ service.interceptors.response.use(
type: 'error',
duration: 5 * 1000
})
if (top) {
top._addError(error.toString(), `axios-request ${error.config.url}`, error.config.params || error.config.data)
if (top && typeof top._addError === 'function') {
top._addError(error.toString(), `axios-request ${error.config?.url}`, error.config?.params || error.config?.data)
}
return Promise.reject(error)
}

@ -2173,7 +2173,8 @@ export default {
this.form[key] = jsonObj;
}
} catch (err) {
if (this.form.hasOwnProperty(key)) {
// _display
if (this.form.hasOwnProperty(key) || key.endsWith('_display')) {
if (data[key] instanceof Array) {
if (data[key].length > 0) {
this.form[key] = data[key];

File diff suppressed because it is too large Load Diff

@ -188,3 +188,9 @@ A: 这是正常的Vue CLI使用内容哈希来生成文件名。只要HTML文

Loading…
Cancel
Save