You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4546 lines
148 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div ref="contractList" style="padding: 0 20px;">
<lx-header icon="md-apps" style="margin-bottom: 10px; border: 0px; margin-top: 15px" text="合同列表">
<div slot="content" />
<slot>
<!-- 修改为新增合同按钮 -->
<div class="category-buttons">
<Button
type="primary"
size="large"
class="category-button"
@click="handleAddContractByCategory('contract')"
>
新增合同类
</Button>
<Button
type="primary"
size="large"
class="category-button"
@click="handleAddContractByCategory('other')"
>
新增其他支出类
</Button>
</div>
<div class="selects">
<!-- 1. 关键字 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">关键字</span>
<span>
<Input v-model="select.keyword" clearable placeholder="关键字搜索" style="width: 200px" />
</span>
</div>
<!-- 2. 供应商/付款对象 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">供应商/付款对象</span>
<span>
<Input v-model="select.supply" clearable placeholder="请输入供应商/付款对象" style="width: 200px" />
</span>
</div>
<!-- 3. 预算计划 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">预算计划</span>
<span>
<Input
v-model="select.plan_name"
clearable
placeholder="请选择预算计划"
style="width: 200px"
@on-focus="showPlanForSearch()"
@on-clear="clearSelectForSearch"
/>
</span>
</div>
<!-- 4. 签订年份 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">签订年份</span>
<span>
<DatePicker
:value="select.year"
placeholder="选择年份"
placement="bottom"
style="width: 90px;"
type="year"
@on-change="(e)=>select.year = e"
/>
</span>
</div>
<!-- 5. 预算金额 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">预算金额</span>
<el-input-number v-model="select.start_plan_price" size="small" :controls="false" :min="0" placeholder="最小金额" style="width: 100px;" />
<span style="padding: 0 5px;">-</span>
<el-input-number v-model="select.end_plan_price" size="small" :controls="false" :min="0" placeholder="最大金额" style="width: 100px;" />
</div>
<!-- 更多按钮 -->
<div>
<Button type="text" @click="showMoreSearch = !showMoreSearch">
{{ showMoreSearch ? '收起' : '更多' }}
</Button>
</div>
<!-- 其余搜索项v-if="showMoreSearch" -->
<template v-if="showMoreSearch">
<!-- 项目类型 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">项目类型</span>
<Select v-model="select.type" clearable placeholder="请选择项目类型" style="width:140px;">
<Option v-for="item in type" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</div>
<!-- 业务科室 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">业务科室</span>
<el-select
v-model="select.department_id"
clearable
placeholder="业务科室选择"
size="small"
style="width: 120px;"
>
<el-option v-for="item in departments" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</div>
<!-- 采购形式 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">采购形式</span>
<Select v-model="select.purchase_type_id" clearable placeholder="请选择采购形式" style="width:140px;">
<Option v-for="item in purchaseType" :key="item.id" :value="item.id">{{ item.value }}</Option>
</Select>
</div>
<!-- 采购方式 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">采购方式</span>
<Select v-model="select.purchase_way_id" clearable placeholder="请选择采购方式" style="width:200px;">
<Option v-for="item in purchaseWay" :key="item.id" :value="item.id">{{ item.value }}</Option>
</Select>
</div>
<!-- 合同状态 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">合同状态</span>
<Select v-model="select.status" clearable placeholder="请选择" style="width:100px;">
<Option
v-for="item in [{label:'待签订',value:1},{label:'已签订',value:2}]"
:key="item.value"
:value="item.value"
>{{ item.label }}
</Option>
</Select>
</div>
<!-- 招标流程状态 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">招标流程状态</span>
<Select v-model="select.invite_status" clearable placeholder="请选择" style="width:100px;">
<Option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}
</Option>
</Select>
</div>
<!-- 采购流程状态 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">采购流程状态</span>
<Select v-model="select.purchase_status" clearable placeholder="请选择" style="width:100px;">
<Option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}
</Option>
</Select>
</div>
<!-- 合同会签状态 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">合同会签状态</span>
<Select v-model="select.join_status" clearable placeholder="请选择" style="width:100px;">
<Option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}
</Option>
</Select>
</div>
<!-- 请示流程状态 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">请示流程状态</span>
<Select v-model="select.req_status" clearable placeholder="请选择" style="width:100px;">
<Option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}
</Option>
</Select>
</div>
<!-- 创建日期 -->
<div>
<span style="padding: 0 6px;word-break: keep-all;">创建日期</span>
<span>
<DatePicker
v-model="select.showDatePicker"
clearable
placeholder="请选择日期"
placement="bottom-start"
style="width: 200px"
type="daterange"
@on-change="datePick"
/>
</span>
</div>
</template>
<!-- 查询、重置、导出按钮始终显示 -->
<Button style="margin-left: 10px" type="primary" @click="getContracts">查询</Button>
<Button
ghost
style="margin-left: 10px"
type="primary"
@click=" select = {showDatePicker:'',ageIndex:1,startDate:'',endDate:'',type:'',department:'',purchaseModality:'',purchaseMethods:'',priceMin:null,priceMax:null,status:''}"
>
重置
</Button>
<Button type="primary" style="margin-left: 10px" @click="downloadExel()">导出</Button>
</div>
</slot>
</lx-header>
<xy-table
ref="xyTable"
:cell-style="cellStyle"
:list="list"
:show-summary="true"
:summary-method="summary"
:table-item="table"
@cellClick="showPaymentPlan"
@delete="(row)=>deleteContract(row.id)"
>
<template v-slot:btns>
<el-table-column fixed="right" header-align="center" label="操作" width="200">
<template slot-scope="scope">
<div class="slot-btns">
<!-- is_end=0 -->
<!-- 如果需要走合同会签contract_category.flow_join ,合同会签已办结的 join_status=3 -->
<!-- 如果需要走采购 contract_category.flow_purchase 采购流程已办结的 purchase_status=3 -->
<!-- 如果需要走请示流程 contract_category.flow_req 请示流程已办结的 req_status=3 -->
<!-- 如果需要走招标流程 contract_category.flow_invite 招标流程已办结的 invite_status=3 -->
<!-- 有支付表格的 contract_category.contract_template_id || contract_category.contract_template_id2 不为空 -->
<!-- 新表格的付款登记 -->
<template v-if="scope.row.contract_category &&
scope.row.is_end === 0 &&
scope.row.contract_type !== 180 &&
(!scope.row.contract_category.flow_join || scope.row.join_status === 3) &&
(!scope.row.contract_category.flow_purchase || scope.row.purchase_status === 3) &&
(!scope.row.contract_category.flow_req || scope.row.req_status === 3) &&
(!scope.row.contract_category.flow_invite || scope.row.invite_status === 3) &&
(scope.row.contract_category.contract_template_id || scope.row.contract_category.contract_template_id2)">
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="checkFormsBeforePayment(scope.row)"
>
付款登记
</Button>
</template>
<!-- 之前的付款登记 -->
<!-- 如果是 履约文件-->
<!-- 那么必须财务审核通过-->
<template v-else>
<template v-if="scope.row.status === 2&&scope.row.is_end===0 && scope.row.contract_type !== 180">
<template v-if="scope.row.is_assurance==1">
<template v-if="scope.row.assurance_status==1">
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="checkFormsBeforePayment(scope.row)"
>
付款登记
</Button>
</template>
</template>
<template v-else>
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="checkFormsBeforePayment(scope.row)"
>
付款登记
</Button>
</template>
</template>
<template v-if="scope.row.is_simple === 1&&scope.row.is_end === 0 && scope.row.contract_type !== 180">
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="checkFormsBeforePayment(scope.row)"
>
付款登记
</Button>
</template>
</template>
<template v-if="scope.row.status === 1 && scope.row.join_status === 3">
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="$refs['contractSign'].isShow = true,$refs['contractSign'].contractId = scope.row.id"
>
签订合同
</Button>
</template>
<!-- (scope.row.purchase_way ? scope.row.purchase_way.remark === 'true' : false) && -->
<!-- 旧合同的招标审查 -->
<!-- contract_category.flow_invite -->
<!-- 新合同的招标审查 =1 且如果需要走采购,采购=3 -->
<template v-if="scope.row.contract_category">
<!-- 如果需要 走采购 且采购状态=3的 需要走招标flow_invite=1就可以走招标审查 -->
<!-- 如果不需要走采购 只需要走招标的flow_invite=1就可以走招标审查 -->
<!-- invite_status=3 代表招标审查已结束,不需要显示按钮 -->
<template v-if="scope.row.contract_category.flow_purchase && scope.row.purchase_status === 3 && scope.row.contract_category.flow_invite==1 && scope.row.invite_status == 1">
<Button class="slot-btns-item" size="small" type="primary" @click="bidding(scope.row)">招标审查</Button>
</template>
<template v-else-if="!scope.row.contract_category.flow_purchase && scope.row.contract_category.flow_invite==1 && scope.row.invite_status == 1">
<Button class="slot-btns-item" size="small" type="primary" @click="bidding(scope.row)">招标审查</Button>
</template>
</template>
<template v-else>
<template
v-if="scope.row.invite_status === 1 && scope.row.purchase_status === 3 && (scope.row.purchase_way ? scope.row.purchase_way.remark === 'true' : false) && !scope.row.is_substitute"
>
<Button class="slot-btns-item" size="small" type="primary" @click="bidding(scope.row)">招标审查</Button>
</template>
</template>
<!-- <Button class="slot-btns-item" type="primary" size="small">附件管理</Button>-->
<template v-if="scope.row.req_status === 1 && (scope.row.is_plan === 0 && !scope.row.is_substitute || scope.row.has_charge) && (!scope.row.contract_category || scope.row.req_status === 1)">
<Button class="slot-btns-item" size="small" type="primary" @click="askProcess(scope.row)">请示流程
</Button>
</template>
<!--不需要走采购流程那么直接就是会签,如果采购方式不需要招标的也是直接会签-->
<!-- (scope.row.purchase_way ? scope.row.purchase_way.remark === 'false' : false) -->
<template
v-if="(scope.row.has_charge && scope.row.req_status === 3 && scope.row.join_status === 1) || (scope.row.join_status === 1 && ((scope.row.invite_status === 3)||(scope.row.purchase_status === 3 && (!scope.row.contract_category || !scope.row.contract_category.flow_invite || scope.row.invite_status === 3))) || ( scope.row.is_substitute && scope.row.join_status === 1) ) && (!scope.row.contract_category || scope.row.join_status === 1)"
>
<Button class="slot-btns-item" size="small" type="primary" @click="signProcess(scope.row)">合同会签
</Button>
</template>
<template
v-if="!scope.row.has_charge && scope.row.is_simple !== 1 && scope.row.purchase_status === 1 && ((scope.row.req_status === 3 && scope.row.is_plan === 0)||scope.row.is_plan === 1)&& !scope.row.is_substitute && (!scope.row.contract_category || scope.row.purchase_status === 1)"
>
<Button class="slot-btns-item" size="small" type="primary" @click="buyProcess(scope.row)">采购流程
</Button>
</template>
<!-- v-if="!(scope.row.req_status != 1 || scope.row.join_status != 1 || scope.row.invite_status != 1 || scope.row.purchase_status != 1 || scope.row.status === 2)" -->
<Poptip trigger="hover" placement="bottom" transfer>
<Button ghost size="small" type="primary">更多</Button>
<div slot="content">
<template
v-if="$store.state.user.myRole.find(i => /财审科|系统管理员/g.test(i.name))"
>
<Poptip
:transfer="true"
confirm
placement="bottom"
title="可能存在已办结的流程,确认要删除吗?"
@on-ok="()=>deleteContract(scope.row.id)"
>
<i-button class="slot-btns-item" ghost size="small" type="error">删除
</i-button>
</Poptip>
</template>
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="$refs['detailContract'].getDetail(scope.row.id),$refs['detailContract'].isShowDetail = true"
>
查看
</Button>
<template v-if="scope.row.status != 2||hasEdit">
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="handleEdit(scope.row)"
>编辑
</Button>
</template>
<template v-if="hasEdit&&scope.row.status === 2">
<Button
class="slot-btns-item"
size="small"
type="primary"
@click="$refs['contractSign'].isShow = true,$refs['contractSign'].contractId = scope.row.id"
>
签订修改
</Button>
</template>
</div>
</Poptip>
</div>
</template>
</el-table-column>
</template>
</xy-table>
<div style="display: flex;justify-content: flex-end;">
<Page :total="total" show-elevator show-sizer @on-change="pageChange" @on-page-size-change="pageSizeChange" />
</div>
<!-- 新增合同模态框 -->
<Modal
v-model="isShowAdd"
:title="isEditMode ? '编辑合同' : '新增合同'"
width="800"
:mask-closable="false"
:closable="false"
class="contract-add-modal"
>
<!-- Steps Header -->
<div class="steps-header">
<div class="step" :class="{ active: currentStep === 1 }">
<span class="step-number">1</span>
<span class="step-text">选择类型</span>
</div>
<div class="step" :class="{ active: currentStep === 2 }">
<span class="step-number">2</span>
<span class="step-text">基本信息</span>
</div>
<div v-if="currentStep > 1 && form.before_contract_template" class="step" :class="{ active: currentStep === 3 }">
<span class="step-number">3</span>
<span class="step-text">附件信息</span>
</div>
</div>
<!-- Step Content -->
<div class="step-content">
<!-- Step 1: Type Selection -->
<div v-show="currentStep === 1" class="step-content">
<div v-if="categoryOptions && categoryOptions.length > 0" class="form-group">
<label class="form-label">分类</label>
<Select v-model="form.category" placeholder="请选择分类" class="form-input" disabled @on-change="handleCategoryChange">
<Option v-for="item in categoryOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div v-if="affairTypeOptions && affairTypeOptions.length > 0" class="form-group">
<label class="form-label">事项类型</label>
<Select v-model="form.affairType" placeholder="请选择事项类型" class="form-input" @on-change="handleAffairTypeChange">
<Option v-for="item in affairTypeOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div v-if="contractTypeOptions && contractTypeOptions.length > 0" class="form-group">
<label class="form-label">合同类型</label>
<Select v-model="form.contractType" placeholder="请选择合同类型" class="form-input" @on-change="handleContractTypeChange">
<Option v-for="item in contractTypeOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div v-if="purchaseFormOptions && purchaseFormOptions.length > 0" class="form-group">
<label class="form-label">采购形式</label>
<Select v-model="form.purchaseForm" placeholder="请选择采购形式" class="form-input" @on-change="handlePurchaseFormChange">
<Option v-for="item in purchaseFormOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div v-if="purchaseMethodOptions && purchaseMethodOptions.length > 0" class="form-group">
<label class="form-label">采购方式</label>
<Select v-model="form.purchaseMethod" placeholder="请选择采购方式" class="form-input" @on-change="handlePurchaseMethodChange">
<Option v-for="item in purchaseMethodOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
</div>
<!-- Step 2: Basic Information -->
<div v-show="currentStep === 2" class="step-content">
<!-- 选择的合同模版类型信息 -->
<div class="template-info">
<div class="info-title">合同类型</div>
<div class="info-content">
<div v-if="form.category && getCategoryName(form.category)" class="info-item">
<span class="label">合同分类:</span>
<span class="value">{{ getCategoryName(form.category) }}</span>
</div>
<div v-if="form.affairType && getAffairTypeName(form.affairType)" class="info-item">
<span class="label">事务类型:</span>
<span class="value">{{ getAffairTypeName(form.affairType) }}</span>
</div>
<div v-if="form.contractType && getContractTypeName(form.contractType)" class="info-item">
<span class="label">合同类型:</span>
<span class="value">{{ getContractTypeName(form.contractType) }}</span>
</div>
<div v-if="form.purchaseForm && getPurchaseFormName(form.purchaseForm)" class="info-item">
<span class="label">采购形式:</span>
<span class="value">{{ getPurchaseFormName(form.purchaseForm) }}</span>
</div>
<div v-if="form.purchaseMethod && getPurchaseMethodName(form.purchaseMethod)" class="info-item">
<span class="label">采购方式:</span>
<span class="value">{{ getPurchaseMethodName(form.purchaseMethod) }}</span>
</div>
</div>
</div>
<!-- 流程控制 -->
<div class="form-section">
<div class="section-title">合同配置</div>
<div class="process-control-grid">
<div class="control-item">
<div class="control-label">是否为简易流程</div>
<div class="control-content">
<el-radio-group v-model="form.is_simple" :disabled="!editPermissions.is_simple">
<el-radio :label="0">否</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
<div class="helper-text">(水电煤、报刊订阅、网络通讯、车辆使用等费用付款)</div>
</div>
</div>
<div class="control-item">
<div class="control-label">是否为河道处收费类项目</div>
<div class="control-content">
<el-radio-group v-model="form.has_charge" :disabled="!editPermissions.has_charge">
<el-radio :label="0">否</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</div>
</div>
<div class="control-item">
<div class="control-label">是否为预算内确定项目</div>
<div class="control-content">
<el-radio-group v-model="form.isBudget" :disabled="!editPermissions.is_plan">
<el-radio :label="0">否</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</div>
</div>
<div class="control-item">
<div class="control-label">是否为代建项目</div>
<div class="control-content">
<el-radio-group v-model="form.is_substitute" :disabled="!editPermissions.is_substitute">
<el-radio :label="0">否</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</div>
</div>
</div>
</div>
<!-- 基本信息表单 -->
<div class="form-section">
<div class="section-title">基本信息</div>
<el-form ref="basicInfoForm" :model="form" label-width="140px">
<el-form-item v-show="showFields.projectName" label="项目名称" prop="name" :rules="[{ required: true, message: '请输入项目名称', trigger: 'submit' }]">
<el-input v-model="form.name" placeholder="请输入项目名称" />
</el-form-item>
<el-form-item v-show="showFields.projectType" label="项目类型" prop="type" :rules="[{ required: true, message: '请选择项目类型', trigger: 'submit' }]">
<Select v-model="form.type" placeholder="请选择项目类型" style="width: 100%">
<Option v-for="item in type" :key="item.value" :value="item.value">{{ item.label }}</Option>
</Select>
</el-form-item>
<!-- 新增承包商/供应商输入框 -->
<!-- <el-form-item
v-show="true"
:label="formType === 'contract' ? '承包商/供应商' : '付款对象'"
prop="supply"
:rules="formType === 'contract' ? [] : [{ required: true, message: '请输入付款对象', trigger: 'submit' }]"
>
<el-input
v-model="form.supply"
:placeholder="formType === 'contract' ? '请输入承包商/供应商' : '请输入付款对象'"
/>
</el-form-item> -->
<el-form-item
v-show="formType != 'contract'"
:label="'付款对象'"
prop="supply"
:rules="[{ required: true, message: '请输入付款对象', trigger: 'submit' }]"
>
<el-input
v-model="form.supply"
:placeholder="'请输入付款对象'"
/>
</el-form-item>
<!-- 新增执行科室选择 -->
<el-form-item label="执行科室" prop="contract_carry_department" :rules="[{ required: true, message: '请选择执行科室', trigger: 'submit' }]">
<Select v-model="form.contract_carry_department" placeholder="请选择执行科室" style="width: 100%" multiple>
<Option v-for="item in departments" :key="item.id" :value="item.id">{{ item.name }}</Option>
</Select>
<div v-if="form.contract_carry_department && form.contract_carry_department.length > 0" class="department-tags">
<el-tag v-for="id in form.contract_carry_department" :key="id" closable @close="removeDepartment(id)">
{{ getDepartmentName(id) }}
</el-tag>
</div>
</el-form-item>
<el-form-item v-show="showFields.budgetPrice" label="合同预算金额(元)" prop="price" :rules="[{ required: true, message: '请输入预算金额', trigger: 'submit' }]">
<el-input-number v-model="form.price" :min="0" :precision="2" :step="1000" style="width: 100%" />
</el-form-item>
<el-form-item v-show="showFields.fundChannel" label="资金渠道" prop="moneyWay" :rules="[{ required: true, message: '请选择资金渠道', trigger: 'submit' }]">
<div class="money-way-tree-container">
<el-tree
ref="moneyWayTree"
:data="planTypes"
:props="defaultProps"
show-checkbox
node-key="id"
:default-checked-keys="form.moneyWay || []"
:check-strictly="false"
style="width: 100%; max-height: 200px; overflow: auto; border: 1px solid #dcdfe6; border-radius: 4px; padding: 10px;"
@check="handleMoneyWayCheck"
/>
</div>
<div v-if="selectedMoneyWayLabels.length > 0" class="selected-money-way" style="margin-top: 10px;">
<div style="margin-bottom: 5px; font-size: 12px; color: #606266;">已选择:</div>
<el-tag
v-for="(item, index) in selectedMoneyWayLabels"
:key="index"
closable
size="small"
style="margin-right: 5px; margin-bottom: 5px;"
@close="removeMoneyWay(item.id)"
>
{{ item.label }}
</el-tag>
</div>
</el-form-item>
<el-form-item v-show="showFields.budgetPlan" label="关联预算计划" prop="plan" :rules="[{ required: true, message: '请选择关联预算计划', trigger: 'change' }]">
<div class="plan-selector">
<el-input
v-model="form.plan_display"
placeholder="请选择预算计划"
readonly
class="plan-input"
@focus="showPlanForSearch('modal')"
/>
<div v-if="form.plan && form.plan.length > 0" class="plan-tags">
<el-tag
v-for="item in form.plan"
:key="item.id"
closable
@close="removePlan(item)"
>
{{ item.label }}
</el-tag>
</div>
</div>
</el-form-item>
</el-form>
</div>
</div>
<!-- Step 3: Attachment Information -->
<div v-show="currentStep === 3 && form.before_contract_template" class="step-content">
<!-- 事前支付表格 -->
<div v-if="form.before_contract_template && !form.showAfterPayment" class="form-section">
<div class="section-title" style="display: flex; align-items: center;">
事前审批表格
<el-button type="text" icon="el-icon-zoom-in" @click="openPaymentFormPreview('before')">放大</el-button>
</div>
<div v-if="!showPaymentFormPreview || previewType !== 'before'">
<div ref="beforePaymentForm" v-html="form.before_forms || (form.before_contract_template && form.before_contract_template.template) || ''" />
</div>
</div>
<!-- 事后支付表格 -->
<div v-else-if="form.contract_template" class="form-section">
<div class="section-title" style="display: flex; align-items: center;">
事后支付表格
<el-button type="text" icon="el-icon-zoom-in" @click="openPaymentFormPreview('after')">放大</el-button>
</div>
<div v-if="!showPaymentFormPreview || previewType !== 'after'">
<div ref="afterPaymentForm" v-html="form.forms || (form.contract_template && form.contract_template.template) || ''" />
</div>
</div>
<!-- 无支付表格提示 -->
<div v-else class="form-section">
<div class="section-title">无支付表格</div>
<div class="no-payment-form">当前选择的合同类型没有配置支付表格</div>
</div>
</div>
</div>
<div slot="footer">
<div class="modal-footer">
<!-- 从付款登记进入时的按钮控制 -->
<template v-if="isFromPayment">
<!-- 只显示事前支付表格时 -->
<template v-if="form.before_contract_template && !form.showAfterPayment && (!form.contract_template || form.forms)">
<Button type="primary" class="action-button" @click="submit">提交</Button>
</template>
<!-- 只显示事后支付表格时 -->
<template v-else-if="form.contract_template && form.showAfterPayment && (!form.before_contract_template || form.before_forms)">
<Button type="primary" class="action-button" @click="submit">提交</Button>
</template>
<!-- 两个表格都没填写时 -->
<template v-else-if="form.before_contract_template && !form.showAfterPayment && form.contract_template && !form.forms">
<Button type="primary" class="action-button" @click="nextPaymentStep">下一步</Button>
</template>
<!-- 其他情况 -->
<template v-else>
<Button type="primary" class="action-button" @click="submit">提交</Button>
</template>
</template>
<!-- 正常编辑模式时的按钮控制 -->
<template v-else>
<Button v-if="currentStep > 1" class="action-button" @click="prevStep">上一步</Button>
<!-- 第二步时,根据是否有事前支付表格决定显示下一步还是提交按钮 -->
<template v-if="currentStep === 2">
<template v-if="form.before_contract_template">
<Button type="primary" class="action-button" @click="nextStep">下一步</Button>
</template>
<template v-else>
<Button type="primary" class="action-button" @click="submit">提交</Button>
</template>
</template>
<!-- 第三步时的按钮控制 -->
<template v-else-if="currentStep === 3">
<Button type="primary" class="action-button" @click="submit">提交</Button>
</template>
<!-- 第一步时显示下一步按钮 -->
<Button v-else-if="currentStep === 1" type="primary" class="action-button" @click="nextStep">下一步</Button>
</template>
<Button class="action-button" @click="cancel">取消</Button>
</div>
</div>
</Modal>
<!-- 搜索使用 预算计划 -->
<xy-dialog :is-show.sync="isShowPlanForSearch" title="预算计划" width="60" @on-ok="planSelectForSearch">
<template v-slot:normalContent>
<div class="search-controls">
<Select
v-model="planSearch.plan_department_id"
placeholder="选择部门"
style="width: 220px; margin-right: 10px"
clearable
@on-change="searchBudgets"
>
<Option
v-for="dept in departments"
:key="dept.id"
:value="dept.id"
>
{{ dept.name }}
</Option>
</Select>
<Select
v-model="planSearch.year"
placeholder="选择年份"
style="width: 120px;margin-right: 10px"
clearable
@on-change="searchBudgets"
>
<Option
v-for="year in availableYears"
:key="year"
:value="year"
>
{{ year }}
</Option>
</Select>
<Input
v-model="planSearch.name"
search
enter-button="搜 索"
placeholder="搜索预算计划名称"
@on-search="searchBudgets"
/>
</div>
<div style="margin: 10px 0;display: flex;justify-content: space-between;align-items: center;">
<div>已选择:<span style="margin-right:10px; color: #409EFF; font-weight: 500;">{{ select.plan_name || '未选择' }}</span></div>
<el-link type="success" @click="clearSelectForSearch">清空选择</el-link>
</div>
<xy-table
ref="singlePlanTable"
:list="plans"
:show-index="false"
:table-item="planTableSearch"
:height="310"
style="margin-top: 10px;"
@rowClick="selectPlanForSearch"
>
<template v-slot:btns>
<div>
</div>
</template>
</xy-table>
<div style="display: flex;justify-content: flex-end;">
<Page :total="planTotal" show-elevator @on-change="planPageChange" />
</div>
<el-tag type="warning">点击行进行选择</el-tag>
</template>
<template v-slot:footerContent>
<Button type="primary" @click="planSelectForSearch">确定</Button>
</template>
</xy-dialog>
<!-- 新增表 预算计划 -->
<xy-dialog :is-show.sync="isShowPlan" width="60" title="预算计划" @on-ok="planSelect">
<template v-slot:normalContent>
<div style="display: flex;">
<el-select
v-model="planSearch.plan_department_id"
placeholder="科室选择"
clearable
size="small"
multiple
collapse-tags
style="width: 220px;margin-right: 10px"
>
<el-option
v-for="item in departments"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-date-picker size="small" type="year" placeholder="请选择年份" v-model="planSearch.year"
format="yyyy" value-format="yyyy" style="width: 120px;margin-right: 10px"></el-date-picker>
<Input
v-model="planSearch.name"
enter-button="搜 索"
placeholder="搜索预算计划.."
search
@on-search="searchBudgets"
/>
</div>
<xy-table
ref="planTable"
:height="300"
:list="plans"
:show-index="false"
:table-item="planTable"
style="margin-top: 10px;"
@select="selectPlan"
>
<template v-slot:btns>
<el-table-column header-align="center" label="使用金额" fixed="right" width="140">
<template slot-scope="scope">
<Input :value="scope.row.useMoney" @input="planInput($event,scope.row)" />
</template>
</el-table-column>
</template>
</xy-table>
<div style="display: flex;justify-content: flex-end;">
<Page :total="planTotal" show-elevator @on-change="planPageChange" />
</div>
</template>
<template v-slot:footerContent>
<Button type="primary" @click="isShowPlan = false">确定</Button>
</template>
</xy-dialog>
<!-- 编辑-->
<editor
ref="editor"
:departments="departments"
:is-show-editor.sync="isShowEditor"
:money-way="moneyWay"
:purchase-type="purchaseType"
:purchase-way="purchaseWay"
@success="getContracts"
/>
<!-- 查看-->
<detail ref="detailContract" />
<!--付款登记-->
<paymentRegistration ref="paymentRegistration" />
<!-- 合同签订-->
<contractSign ref="contractSign" @signSuccess="getContracts" />
<!-- 查看付款计划-->
<contractPaymentRegistration ref="contractPaymentRegistration" />
<govPlane ref="govPlane" @selected="row => form.gov_plane_id = row.id" />
<!-- 放大弹窗 -->
<Modal
v-model="showPaymentFormPreview"
width="80%"
:title="previewType === 'before' ? '事前支付表格' : '事后支付表格'"
:z-index="4000"
class="payment-preview-modal"
footer-hide
@on-cancel="handlePaymentFormPreviewClose"
>
<div v-if="previewType === 'before'">
<div ref="previewBeforePaymentForm" v-html="form.before_forms || (form.before_contract_template && form.before_contract_template.template) || ''" />
</div>
<div v-else>
<div ref="previewAfterPaymentForm" v-html="form.forms || (form.contract_template && form.contract_template.template) || ''" />
</div>
</Modal>
<!-- 添加打印组件 -->
<printRegistration ref="printRegistration" />
<printFundApproval ref="printFundApproval" />
<printPaymentForm ref="printPaymentForm" />
<!-- 编辑供应商弹窗 -->
<Modal
v-model="isShowSupplyDialog"
title="编辑供应商/服务商"
@on-ok="submitSupply"
@on-cancel="cancelSupplyDialog"
>
<div style="padding: 20px 0;">
<div style="margin-bottom: 15px;">
<span style="display: inline-block; width: 100px; text-align: right; margin-right: 10px;">供应商/服务商:</span>
<Input
v-model="editingSupplyValue"
placeholder="请输入供应商/服务商"
style="width: 300px;"
@on-enter="submitSupply"
/>
</div>
</div>
</Modal>
<!-- OA流程流水号弹窗 -->
<Modal
v-model="isShowFlowDialog"
title="填写OA流程流水号"
@on-ok="submitFlowId"
@on-cancel="cancelFlowDialog"
>
<div style="padding: 20px 0;">
<div style="margin-bottom: 15px;">
<span style="display: inline-block; width: 100px; text-align: right; margin-right: 10px;">流水号:</span>
<Input
v-model="flowId"
placeholder="请输入OA流程流水号"
style="width: 300px;"
@on-enter="submitFlowId"
/>
</div>
<div style="margin-bottom: 15px;">
<span style="display: inline-block; width: 100px; text-align: right; margin-right: 10px;">状态:</span>
<Select
v-model="flowStatus"
style="width: 300px;"
placeholder="请选择状态"
>
<Option :value="0">流转中</Option>
<Option :value="1"></Option>
</Select>
</div>
</div>
</Modal>
</div>
</template>
<script>
import {
getContract,
addContrant,
delContract,
checkContractName,
editorContract,
detailContract,
updateContract
} from '@/api/contract/contract'
import {
getparameter
} from '@/api/system/dictionary'
import {
index as getPlanType
} from '@/api/budget/plantype.js'
import {
getContractCategoryTemplateBaseConfig,
getContractCategoryTemplateConfigParams,
getContractFormList
} from '@/api/businessConfig/businessConfig'
import {
listdeptNoAuth
} from '@/api/system/department'
import {
getBudget
} from '@/api/budget/budget'
import {
getOatoken
} from '@/api/oatoken'
import {
parseTime,
buildTree
} from '@/utils'
import {
Message
} from 'element-ui'
import {
getInfo
} from '@/api/user.js'
import {
getToken
} from '@/utils/auth'
import editor from './components/editorContract'
import detail from './components/detailContract'
import paymentRegistration from './components/paymentRegistration'
import contractSign from '@/views/contract/components/contractSign'
import contractPaymentRegistration from '@/views/contract/components/contractPaymentRegistration'
import govPlane from './components/govPlane.vue'
import {
download
} from '@/utils/downloadRequest'
import printRegistration from './components/printRegistration'
import printFundApproval from './components/printFundApproval'
import printPaymentForm from './components/printPaymentForm'
function syncFormDomToHtml(dom, contractTemplateFields) {
if (!dom) return ''
const inputs = dom.querySelectorAll('input, select, textarea')
inputs.forEach(input => {
const fieldName = input.getAttribute('data-field')
if (fieldName && contractTemplateFields) {
const field = contractTemplateFields.find(f => f.field === fieldName)
if (field) {
if (input.type === 'checkbox' || input.type === 'radio') {
const checkedInput = dom.querySelector(`[data-field="${fieldName}"]:checked`)
field.value = checkedInput ? checkedInput.value : ''
if (checkedInput) {
checkedInput.setAttribute('checked', 'checked')
}
} else {
if (input.tagName.toLowerCase() === 'textarea') {
// textarea 是双标签,内容在标签之间,不使用 value 属性
field.value = input.value || input.textContent || ''
console.log('fieldName:'+fieldName+"--- "+field.value)
// 对于 textarea直接设置内容到标签之间
input.textContent = field.value
} else {
field.value = input.value
console.log('fieldName:'+fieldName+"--- "+input.value)
input.setAttribute('value', input.value)
}
}
}
}
})
return dom.innerHTML
}
export default {
components: {
editor,
detail,
paymentRegistration,
contractSign,
contractPaymentRegistration,
govPlane,
printRegistration,
printFundApproval,
printPaymentForm
},
data() {
var planPass = (rule, value, callback) => {
if (this.form.isBudget) {
if (this.form.plan.length === 0) {
callback(new Error('必选'))
} else {
callback()
}
} else {
callback()
}
}
var supplyPass = (rule, value, callback) => {
if (this.form.is_simple) {
if (value === '') {
callback(new Error('必填'))
} else {
callback()
}
} else {
callback()
}
}
var moneyPass = (rule, value, callback) => {
if (this.form.is_simple) {
if (value === '') {
callback(new Error('必填'))
} else {
if (/^\d+(\.\d+)?$/.test(value)) {
callback()
} else {
callback(new Error('必须为数字'))
}
}
} else {
callback()
}
}
var typePass = (rule, value, callback) => {
if (!this.form.is_simple) {
if (value === '') {
callback(new Error('必填'))
} else {
callback()
}
} else {
callback()
}
}
var methodsPass = (rule, value, callback) => {
if (!this.form.is_simple) {
if (value === '') {
callback(new Error('必填'))
} else {
callback()
}
} else {
callback()
}
}
var modalityPass = (rule, value, callback) => {
if (!this.form.is_simple) {
if (value === '') {
callback(new Error('必填'))
} else {
callback()
}
} else {
callback()
}
}
var pricePass = (rule, value, callback) => {
if (!this.form.is_simple) {
if (value === '') {
callback(new Error('必填'))
} else {
if (/^\d+(\.\d+)?$/.test(value)) {
callback()
} else {
callback(new Error('必须为数字'))
}
}
} else {
callback()
}
}
return {
selectedCategory: 'contract', // 添加选中的分类
userList: ['liuxiangyu', 'zhushulan', 'admin', 'jiangjiao'],
window: {
width: 0,
height: 0,
top: 0,
left: 0
},
hasEdit: false,
// 添加控制显示/隐藏的变量
showFields: {
purchaseMethod: true,
projectName: true,
projectType: true,
budgetPrice: true,
fundChannel: true,
budgetPlan: true,
request: true,
purchaseApproval: true,
tenderReview: true,
contractSign: true
},
planSource: 'search',
isShowPlanForSearch: false,
options: [{
value: '',
label: '全部'
}, {
value: 1,
label: '待申请'
}, {
value: 2,
label: '流转中'
}, {
value: 3,
label: '已办结'
}],
// 搜索
select: {
keyword: '',
showDatePicker: '',
pageIndex: 1,
pageSize: 10,
startDate: '',
endDate: '',
type: '',
department_id: '',
purchaseModality: '',
purchaseMethods: '',
priceMin: null,
priceMax: null,
status: '',
year: '',
plan_id: '',
plan_name: '请选择预算计划',
start_plan_price: undefined,
end_plan_price: undefined
},
showMoreSearch: false,
type: [{
label: '服务',
value: 1
}, {
label: '货物',
value: 2
}, {
label: '工程',
value: 3
}],
purchaseType: [], // 购买形式
purchaseWay: [], // 购买方式
moneyWay: [], // 资金渠道
departments: [], // 部门科室
list: [], // 数据
total: 0,
// 表格
table: [
{
label: '合同分类',
width: 120,
prop: 'contract_category.category',
// fixed: 'left',
align: 'center',
formatter: (row) => this.categoryIdNameMap?.[row.contract_category?.category] || ''
},
{
label: '事务类型',
width: 120,
prop: 'contract_category.work_type',
// fixed: 'left',
align: 'center',
formatter: (row) => this.categoryIdNameMap?.[row.contract_category?.work_type] || ''
},
{
label: '合同类型',
width: 120,
prop: 'contract_category.contract_type',
// fixed: 'left',
align: 'center',
formatter: (row) => this.categoryIdNameMap?.[row.contract_category?.contract_type] || '-'
},{
label: '项目名称',
width: 240,
prop: 'name',
fixed: 'left',
align: 'left'
},
{
label: '采购形式',
width: 120,
prop: 'purchase_type.value',
formatter: (cell, data, value) => {
return value || '无'
}
},
{
label: '项目类型',
width: 120,
prop: 'type',
formatter: (cell, data, value) => {
switch (value) {
case 1:
return '服务'
break
case 2:
return '货物'
break
case 3:
return '工程'
break
default:
return '无'
}
}
},
{
label: '采购流程',
multiHd: [ {
label: '请示流程',
width: 140,
prop: 'req_status',
customFn: (row) => {
let statusText = '无'
if (!(row.is_substitute && !row.has_charge) && !(row.is_plan === 1 && !row.has_charge)) {
switch (row.req_status) {
case 0:
statusText = '无'
break
case 1:
statusText = '待申请'
break
case 2:
statusText = '流转中'
break
case 3:
statusText = '已办结'
break
default:
statusText = '异常'
break
}
}
const needFlow = !(row.is_substitute && !row.has_charge) && !(row.is_plan === 1 && !row.has_charge)
const canEdit = needFlow && row.req_status > 0 && row.req_status <= 3 &&
this.$store.state.user.myRole &&
this.$store.state.user.myRole.find(i => /财审科|系统管理员/g.test(i.name))
return (
<div style="display: flex; align-items: center; justify-content: space-between;">
<span>{statusText}</span>
{canEdit && (
<i
class="el-icon-edit"
style="cursor: pointer; color: #409EFF; margin-left: 5px;"
on-click={(e) => {
e.stopPropagation()
this.openFlowDialog(row, 'qingshi')
}}
/>
)}
</div>
)
}
},
{
label: '采购业务审批流程',
width: 158,
prop: 'purchase_status',
customFn: (row) => {
let statusText = '无'
if (!row.is_substitute && !row.is_simple) {
switch (row.purchase_status) {
case 0:
statusText = '无'
break
case 1:
statusText = '待申请'
break
case 2:
statusText = '流转中'
break
case 3:
statusText = '已办结'
break
default:
statusText = '异常'
break
}
}
const needFlow = !row.is_substitute && !row.is_simple
const canEdit = needFlow && row.purchase_status > 0 && row.purchase_status <= 3 &&
this.$store.state.user.myRole &&
this.$store.state.user.myRole.find(i => /财审科|系统管理员/g.test(i.name))
return (
<div style="display: flex; align-items: center; justify-content: space-between;">
<span>{statusText}</span>
{canEdit && (
<i
class="el-icon-edit"
style="cursor: pointer; color: #409EFF; margin-left: 5px;"
on-click={(e) => {
e.stopPropagation()
this.openFlowDialog(row, 'caigou')
}}
/>
)}
</div>
)
}
},
{
label: '招标审核流程',
width: 145,
prop: 'invite_status',
customFn: (row) => {
let statusText = '无'
if (!row.is_substitute && !row.is_simple && row.purchase_way?.remark !== 'false') {
switch (row.invite_status) {
case 0:
statusText = '无'
break
case 1:
statusText = '待申请'
break
case 2:
statusText = '流转中'
break
case 3:
statusText = '已办结'
break
default:
statusText = '异常'
break
}
}
const needFlow = !row.is_substitute && !row.is_simple && row.purchase_way?.remark !== 'false'
const canEdit = needFlow && row.invite_status > 0 && row.invite_status <= 3 &&
this.$store.state.user.myRole &&
this.$store.state.user.myRole.find(i => /财审科|系统管理员/g.test(i.name))
return (
<div style="display: flex; align-items: center; justify-content: space-between;">
<span>{statusText}</span>
{canEdit && (
<i
class="el-icon-edit"
style="cursor: pointer; color: #409EFF; margin-left: 5px;"
on-click={(e) => {
e.stopPropagation()
this.openFlowDialog(row, 'zhaobiaowenjianshencha')
}}
/>
)}
</div>
)
}
},
{
label: '合同会签流程',
width: 145,
prop: 'join_status',
customFn: (row) => {
let statusText = '无'
if (!row.is_simple) {
switch (row.join_status) {
case 0:
statusText = '无'
break
case 1:
statusText = '待申请'
break
case 2:
statusText = '流转中'
break
case 3:
statusText = '已办结'
break
default:
statusText = '异常'
break
}
}
const needFlow = !row.is_simple
const canEdit = needFlow && row.join_status > 0 && row.join_status <= 3 &&
this.$store.state.user.myRole &&
this.$store.state.user.myRole.find(i => /财审科|系统管理员/g.test(i.name))
return (
<div style="display: flex; align-items: center; justify-content: space-between;">
<span>{statusText}</span>
{canEdit && (
<i
class="el-icon-edit"
style="cursor: pointer; color: #409EFF; margin-left: 5px;"
on-click={(e) => {
e.stopPropagation()
this.openFlowDialog(row, 'hetonghuiqian')
}}
/>
)}
</div>
)
}
}
]
},
{
label: '采购方式',
width: 120,
prop: 'purchase_way.value',
formatter: (cell, data, value) => {
return value || '无'
}
},
{
label: '资金渠道',
width: 320,
align: 'left',
customFn: (row) => {
{
return row.money_way_detail.map(item => {
return (<div> {
item.value
} </div>)
})
}
}
},
{
label: '预算计划',
width: 320,
align: 'left',
customFn: (row) => {
return row.plans.map(item => {
return (
<div>
[{item.year}] - {item.name}
</div>
)
})
}
},
{
label: '已申请金额(元)',
prop: 'apply_money_total',
width: 140,
align: 'right',
formatter: (cell, data, value) => {
return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
}
},
{
label: '已付金额(元)',
prop: 'fund_log_total',
width: 140,
align: 'right',
formatter: (cell, data, value) => {
return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
}
},
{
label: '支付占比',
width: 140,
customFn: (row) => {
if (Number(row.money) === 0) {
return '0%'
}
const per = ((((row.fund_log_total) / row.money) || 0) * 100)?.toFixed(2) || 0
return `${per}%`
}
},
{
label: '合同预算价(元)',
width: 140,
prop: 'plan_price',
align: 'right',
formatter: (v1, v2, value) => {
return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
}
},
{
label: '合同签订价(元)',
width: 140,
prop: 'money',
align: 'right',
formatter: (cell, data, value) => {
return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
}
},
{
label: '合同状态',
width: 120,
prop: 'status',
formatter: (cell, data, value) => {
switch (value) {
case 1:
return '待签订'
break
case 2:
return '已签订'
break
default:
return '未知'
}
}
},
{
label: '结算状态',
width: 120,
prop: 'is_end',
formatter: (cell, data, value) => {
return value === 0 ? '未结清' : '已结清'
}
},
{
label: '付款计划',
prop: 'sign_plan_count',
width: 120,
formatter: (cell, data, value) => {
if (value == 0) return '暂无'
return value + '期'
}
},
{
label: '供应商/服务商',
width: 220,
prop: 'supply',
align: 'left',
customFn: (row) => {
return (
<div style="display: flex; align-items: center; justify-content: space-between;">
<span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{row.supply || '-'}
</span>
<i
class="el-icon-edit"
style="cursor: pointer; color: #409EFF; margin-left: 5px; flex-shrink: 0;"
on-click={(e) => {
e.stopPropagation()
this.openSupplyDialog(row)
}}
/>
</div>
)
}
},
{
label: '业务科室',
width: 140,
prop: 'department.name'
},
{
label: '经办人',
width: 140,
prop: 'admin.name'
},
{
label: '签订日期',
width: 160,
prop: 'date',
formatter: (cell, data, value) => {
if (value) {
return parseTime(new Date(value), '{y}-{m}-{d}')
}
}
},
{
label: '创建日期',
width: 160,
prop: 'created_at',
formatter: (cell, data, value) => {
return parseTime(new Date(value), '{y}-{m}-{d}')
}
},
{
label: '执行科室',
minWidth: 200,
prop: 'contract_carry_department',
customFn: row => {
if (!row.contract_carry_department || !row.contract_carry_department.length) {
return '未设置'
}
return row.contract_carry_department.map(i => i.carry_department?.name || '未设置').join('')
}
}
],
// 总计
tableTotal: {
fundLogTotal: 0,
moneyTotal: 0,
planPriceTotal: 0
},
planTypes: [],
// 树形选择相关数据
defaultProps: {
children: 'children',
label: 'name'
},
// 已选择的资金列支渠道标签
selectedMoneyWayLabels: [],
planTableSearch: [{
label: '预算类型',
prop: 'type_detail.name',
width:220,
// formatter: (cell, data, value) => {
// const res = this.moneyWay.filter(item => {
// return item.id === value
// })
// return res[0]?.value || '未知'
// }
},
{
label: '年份',
prop: 'year',
width: 100,
align: 'center'
},
{
label: '名称',
prop: 'name',
align: 'left'
},
{
label: '计划金额',
prop: 'money',
align: 'right',
width: 120,
customFn: (row) => {
const m1 = row.money
const m2 = row.update_money
return (!m2 || m2 == 0) ? m1 : m2
}
}
],
planTable: [{
width: 36,
sortable: false,
type: 'selection',
fixed: 'left'
},
{
label: '预算类型',
prop: 'type_detail.name',
// formatter: (cell, data, value) => {
// const res = this.moneyWay.filter(item => {
// return item.id === value
// })
// return res[0]?.value || '未知'
// },
width: 220,
fixed: 'left'
},
{
label: '科室',
prop: 'plan_department.name',
width: 100,
align: 'center'
},
{
label: '年份',
prop: 'year',
width: 80,
align: 'center'
},
{
label: '隶属',
prop: 'pid_info.name',
width: 180,
align: 'left'
},
{
label: '名称',
prop: 'name',
width: 320,
align: 'left'
},
{
label: '计划金额',
prop: 'money',
align: 'right',
width: 120,
customFn: (row) => {
const m1 = row.money
const m2 = row.update_money
return (!m2 || m2 == 0) ? m1 : m2
}
},
{
label: '已用金额',
prop: 'use_money_total',
width: 120,
align: 'right'
}
],
// 新增
isShowAdd: false,
currentStep: 1,
form: {
category: '',
affairType: '',
contractType: '',
purchaseForm: '',
purchaseSubForm: '',
purchaseMethod: '',
forms: '',
before_forms: '',
originBeforeForms: '',
originAfterForms: '',
other_data: [],
before_other_data: [],
skipBeforeTemplate: false,
skipAfterTemplate: false,
name: '',
type: '', // 项目类型
moneyWay: [],
plan: [],
plan_display: '', // 预算计划显示文本
price: 0,
supply: '',
// 流程控制字段
is_simple: 0, // 是否为简易流程
has_charge: 0, // 是否为河道处收费类项目
isBudget: 0, // 是否为预算内确定项目
is_substitute: 0, // 是否为代建项目
// 新增流程控制字段
request: 0, // 请示流程
purchaseApproval: 0, // 采购业务审批流程
tenderReview: 0, // 招标文件审查流程
contractSign: 0, // 合同会签流程
expenses: [
{
content: '',
amount: 0,
ratio: '',
payee: ''
}
],
showAfterPayment: true, // 是否显示事后支付表格
contract_carry_department: [] // 新增执行科室字段
},
categoryOptions: [],
purchaseMethodsMap: {},
affairTypeOptions: [],
contractTypeOptions: [],
purchaseFormOptions: [],
purchaseMethodOptions: [],
isShowPlan: false, // 新增中预算计划
plans: [], // 预算数据
planSearch: {
name: '',
plan_department_id: '', // 搜索时使用单选
year: (new Date().getFullYear() )+ ''
},
// 可用年份列表
availableYears: [],
planTotal: 0,
plansPageIndex: 1,
// OA流程流水号弹窗相关
isShowFlowDialog: false,
currentFlow: '', // 当前流程类型: qingshi, caigou, zhaobiaowenjianshencha, hetonghuiqian
currentRow: null, // 当前行数据
flowId: '', // 流水号
flowStatus: 0, // 流程状态: 0流转中, 1已办结
oatoken: '', // OA token
isShowSupplyDialog: false, // 编辑供应商弹窗
editingSupplyRow: null, // 正在编辑的行数据
editingSupplyValue: '', // 编辑中的供应商值
isShowEditor: false,
contractTypes: [
{ label: '设备采购', value: 'equipment' },
{ label: '工程合同', value: 'project' },
{ label: '服务合同', value: 'service' }
],
plan: [], // 添加这个来存储选中的预算计划
isEditMode: false, // 添加编辑模式标识
currentContractId: null, // 添加当前编辑的合同ID
isFromPayment: false, // 是否从付款登记进入
// 添加编辑权限控制
editPermissions: {
is_simple: true, // 简易流程编辑权限
has_charge: true, // 河道处收费类项目编辑权限
is_plan: true, // 预算内确定项目编辑权限
is_substitute: true // 代建项目编辑权限
},
showPaymentFormPreview: false,
previewType: '', // 'before' or 'after'
formType: 'contract' // 默认类型
}
},
mounted() {
this.window.width = screen.availWidth * 0.95
this.window.height = screen.availHeight * 0.95
this.window.top = (window.screen.height - 30 - this.window.height) / 2
this.window.left = (window.screen.width - 10 - this.window.width) / 2
const that = this
getInfo().then(response => {
console.log(response)
this.user = response
if (that.userList.indexOf(response.username) != -1) {
that.hasEdit = true
}
}).catch(error => {
})
// oa 过来的合同会签
if(this.$route.query.oaType==='contractSign'){
let contractId = this.$route.query.contractId
this.$refs['contractSign'].isShow = true
this.$refs['contractSign'].contractId = contractId
}
this.getPurchaseType()
this.getContracts()
this.getDepartment(-1)
this.getPurchaseWay()
this.getMoneyWay()
this.getPlanTypes()
this.initAvailableYears()
// 页面激活后刷新合同列表
window.onfocus = () => {
this.getContracts(false, true)
}
this.getCategoryOptions();
this.getContracts();
},
destroyed() {
window.onfocus = null
},
methods: {
toExport() {
this.select.is_export = 1
this.getContracts(true)
},
clearSelectForSearch() {
this.select.plan_id = ''
this.select.plan_name = ''
},
// 初始化可用年份列表
initAvailableYears() {
const currentYear = new Date().getFullYear()
this.availableYears = []
for (let i = currentYear - 5; i <= currentYear + 2; i++) {
this.availableYears.push(i)
}
},
async getPlanTypes() {
const res = await getPlanType({
page_size: 999,
page: 1,
sort_name: 'sort',
sort_type: 'asc',
filter: [{
key: 'year',
op: 'eq',
value: this.$moment().format('YYYY'),
}]
})
this.planTypes = buildTree(res.data)
},
// 预算计划金额输入
planInput(e, row) {
if (!/^[0-9]+.?[0-9]*$/.test(e) && e) {
Message({
type: 'warning',
message: '金额格式错误'
})
row.useMoney = 0
return
}
if (e <= (Number(row.money) - Number(row.use_money_total))) {
row.useMoney = e
this.plan.forEach(item => {
if (item.value.plan_id == row.id) {
item.value.use_money = e
}
})
return
}
Message({
type: 'warning',
message: '使用金额大于剩余预算'
})
row.useMoney = 0
},
// 合计
summary(param) {
this.$nextTick(() => {
this.$refs['xyTable'].$children[0].doLayout()
})
const {
columns,
data
} = param
const sums = []
columns.map((column, index) => {
if (index === 0) {
sums[index] = '总计'
return
}
if (column.property === 'fund_log_total') {
sums[index] = this.tableTotal.fundLogTotal
return
}
if (column.property === 'plan_price') {
sums[index] = this.tableTotal.planPriceTotal
return
}
if (column.property === 'money') {
sums[index] = this.tableTotal.moneyTotal
return
}
// const values = data.map(item => Number(item[column.property]));
// if (!values.every(value => isNaN(value)) && (column.property === 'money' || column.property === 'plan_price'|| column.property === 'fund_log_total')) {
//
// sums[index] = sums[index].toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
// } else {
// sums[index] = '';
// }
})
return sums
},
cellStyle({
row,
column,
rowIndex,
columnIndex
}) {
if (column.property === 'req_status') {
if ((row.is_plan || row.is_substitute || row.is_simple) && !row.has_charge) {
return {
'color': 'rgb(140,140,140)'
}
}
switch (row.req_status) {
case 0:
return {
'color': 'rgb(140,140,140)'
}
break
case 1:
return {
'color': 'rgb(96,109,241)'
}
break
case 2:
return {
'color': 'rgb(219,122,122)'
}
break
case 3:
return {
'color': 'rgb(147,201,134)'
}
break
default:
return {
'color': 'rgb(220,185,126)'
}
}
}
if (column.property === 'purchase_status') {
if (row.is_substitute || row.is_simple) {
return {
'color': 'rgb(140,140,140)'
}
}
switch (row.purchase_status) {
case 0:
return {
'color': 'rgb(140,140,140)'
}
break
case 1:
return {
'color': 'rgb(96,109,241)'
}
break
case 2:
return {
'color': 'rgb(219,122,122)'
}
break
case 3:
return {
'color': 'rgb(147,201,134)'
}
break
default:
return {
'color': 'rgb(220,185,126)'
}
}
}
if (column.property === 'invite_status') {
if (row.purchase_way?.remark === 'false' || row.is_substitute || row.is_simple) {
return {
'color': 'rgb(140,140,140)'
}
}
switch (row.invite_status) {
case 0:
return {
'color': 'rgb(140,140,140)'
}
break
case 1:
return {
'color': 'rgb(96,109,241)'
}
break
case 2:
return {
'color': 'rgb(219,122,122)'
}
break
case 3:
return {
'color': 'rgb(147,201,134)'
}
break
default:
return {
'color': 'rgb(220,185,126)'
}
}
}
if (column.property === 'join_status') {
if (row.is_simple) {
return {
'color': 'rgb(140,140,140)'
}
}
switch (row.join_status) {
case 0:
return {
'color': 'rgb(140,140,140)'
}
break
case 1:
return {
'color': 'rgb(96,109,241)'
}
break
case 2:
return {
'color': 'rgb(219,122,122)'
}
break
case 3:
return {
'color': 'rgb(147,201,134)'
}
break
default:
return {
'color': 'rgb(220,185,126)'
}
}
}
},
// y验证合同的名称是否存在重复
checkName(name) {
// 暂时不调用 API,直接返回
console.log('检查名称:', name)
},
confirmPlanForSearch() {
this.isShowPlanForSearch = false
this.getContracts()
},
// 点击付款计划查看
showPaymentPlan(row, column, cell) {
if (column.property === 'sign_plan_count') {
this.$refs['contractPaymentRegistration'].getSignPlan(row.id)
row.status === 2 ? this.$refs['contractPaymentRegistration'].isSign = true : this.$refs[
'contractPaymentRegistration'].isSign = false
this.$refs['contractPaymentRegistration'].isShow = true
}
if (column.property === 'fund_log_total') {
this.$router.push(`/contract/paymentRegistrationList?contractId=${row.id}`)
}
},
// 招标文件审查
async bidding(row) {
const baseInfo = {
'项目名称': row?.name,
'项目预算(元)': row?.plan_price
}
const res = await getOatoken()
const url =
`${process.env.VUE_APP_OUT_URL}/admin/flow/create/27?oatoken=${res.oatoken}&out_contract_id=${row.id}&contract_json=${JSON.stringify(baseInfo)}`
const bidding = window.open(url, 'bidding',
`top=${this.window.top},left=${this.window.left},width=${this.window.width},height=${this.window.height},location=0`
)
},
// 采购流程
async buyProcess(row) {
const baseInfo = {
'项目名称': row?.name,
'采购形式': row?.purchase_type?.value,
'采购方式': row?.purchase_way?.value,
'项目类型': this.type.find(item => {
return item.value === row.type
})?.label,
'资金渠道': row?.money_way?.value,
'项目预算(元)': row?.plan_price
}
const res = await getOatoken()
const url =
`${process.env.VUE_APP_OUT_URL}/admin/flow/create/2?oatoken=${res.oatoken}&out_contract_id=${row.id}&contract_json=${JSON.stringify(baseInfo)}`
const buyProcess = window.open(url, 'buyProcess',
`top=${this.window.top},left=${this.window.left},width=${this.window.width},height=${this.window.height},location=0`
)
},
// 会签流程
async signProcess(row) {
const baseInfo = {
'合同名称': row?.name,
'执行部门': row?.carry_department,
'合同金额(元)': row?.money
// "承包商\\供应商":row.supply
}
const res = await getOatoken()
const url =
`${process.env.VUE_APP_OUT_URL}/admin/flow/create/3?oatoken=${res.oatoken}&out_contract_id=${row.id}&contract_json=${JSON.stringify(baseInfo)}`
const signProcess = window.open(url, 'signProcess',
`top=${this.window.top},left=${this.window.left},width=${this.window.width},height=${this.window.height},location=0`
)
},
// 请示流程
async askProcess(row) {
const res = await getOatoken()
const url =
`${process.env.VUE_APP_OUT_URL}/admin/flow/create/17?oatoken=${res.oatoken}&out_contract_id=${row.id}`
const askProcess = window.open(url, 'askProcess',
`top=${this.window.top},left=${this.window.left},width=${this.window.width},height=${this.window.height},location=0`
)
},
// 防抖
debounce(fn, delay = 500) {
let timer = null
return function _debounce() {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
}, delay)
}
},
// 翻页
pageChange(e) {
this.select.pageIndex = e
this.getContracts()
},
planPageChange(e) {
this.plansPageIndex = e
this.getBudgets()
},
// 日期选择
datePick(e) {
this.select.start_created_at = e[0]
this.select.end_created_at = e[1]
},
// 获取预算计划
async getBudgets() {
const res = await getBudget({
name: this.planSearch.name,
page_size: 10,
page: this.plansPageIndex,
plan_department_id: this.planSearch.plan_department_id,
top_pid: 1,
year: this.planSearch.year
})
this.plans = res.list.data
this.planTotal = res.list.total
},
// 获取资金渠道
async getMoneyWay() {
try {
const response = await getparameter({
number: 'money_way'
})
if (response && response.detail) {
// 统一数据格式,确保每个项都有唯一的 id
this.moneyWay = response.detail.map((item, index) => ({
id: item.id || `money_way_${index}`,
name: item.name || item.value || `资金来源${index + 1}`,
value: item.value || item.id || `money_way_${index}`
}))
// 初始化表单的 moneyWay
this.form.moneyWay = []
} else {
this.moneyWay = []
this.form.moneyWay = []
}
} catch (error) {
console.error('获取资金来源失败:', error)
this.$message.error('获取资金来源失败')
this.moneyWay = []
this.form.moneyWay = []
}
},
// 获取购买方式
async getPurchaseWay() {
this.purchaseWay = (await getparameter({
number: 'purchase_way'
})).detail
}, // 计划搜索
searchBudgets() {
this.plansPageIndex = 1
this.getBudgets()
},
// 获取科室
async getDepartment(status) {
const params = { show_tree: 1,sort_name:'sortnumber',sort_type:'asc' }
if (status !== undefined) {
params.status = status
}
this.departments = await listdeptNoAuth(params)
},
// 获取购买形式列表
async getPurchaseType() {
this.purchaseType = (await getparameter({
number: 'purchase_type'
})).detail
},
pageSizeChange(e) {
this.select.pageSize = e
this.select.pageIndex = 1
this.getContracts()
},
downloadExel() {
download(
'/api/admin/contract/index',
'get', {
is_auth: 1,
is_export: 1,
...this.select
},
'合同列表.xlsx')
},
// 获取合同列表
async getContracts(is_export, noloading = false) {
const res = await getContract({
page_size: this.select.pageSize,
page: this.select.pageIndex,
is_auth: 1,
...this.select
}, noloading)
const tokens = getToken()
// if (is_export) {
// this.select.is_export == 1
// var url = "/api/admin/contract/index?is_auth=1&token=" + tokens
// Object.keys(this.select).map((key, item) => {
// url += "&" + key + "=" + this.select[key];
// });
// url = location.host + url;
// console.log(url)
// window.open("http://" + url, '_blank')
// this.select.is_export = 0
// return;
// }
this.list = res.list.data
this.total = res.list.total
this.tableTotal.fundLogTotal = res.fund_log_total
this.tableTotal.moneyTotal = Number(res.money_total).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
this.tableTotal.planPriceTotal = Number(res.plan_price_total).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g,
'$1,')
},
// 新建合同
// 显示
async showPlan() {
this.isShowPlan = true
await this.getBudgets()
},
showPlanForSearch(type) {
if (type === 'modal') {
// 表单中选择预算计划,显示表单用的弹出框
this.isShowPlan = true
this.getBudgets()
} else {
// 搜索中选择预算计划,显示搜索用的弹出框
this.isShowPlanForSearch = true
this.getBudgets()
}
},
selectPlanForSearch(sel, row) {
console.log(sel,row)
if (sel) {
this.select.plan_name = '[' + sel.year + '] - ' + sel.name
this.select.plan_id = sel.id
} else {
this.select.plan_id = ''
this.select.plan_name = ''
}
},
planSelectForSearch() {
if (!this.select.plan_id) {
Message({
type: 'warning',
message: '请选择预算计划'
})
return
}
this.isShowPlanForSearch = false
},
// 选择计划
selectPlan(sel, row) {
console.log(sel)
if (sel) {
const select = sel.map(item => {
return {
label: item.name,
value: {
plan_id: item.id,
use_money: item.useMoney || 0,
new_money: item.money
}
}
})
// 直接替换 form.plan避免重复添加
this.form.plan = select
// 同时更新临时变量用于显示
this.plan = this.form.plan
} else {
this.plan = this.form.plan
}
},
// 确认计划选择
planSelect() {
if (this.form.plan.length === 0) {
Message({
type: 'warning',
message: '选择计划不能为空'
})
return
}
// for (const item of this.form.plan) {
// if (!item.value.use_money) {
// Message({
// type: 'warning',
// message: '金额不能为空'
// })
// return
// }
// }
// 设置显示文本,使用逗号分隔
this.form.plan_display = this.form.plan.map(item => item.label).join(', ')
// 关闭对话框
this.isShowPlan = false
},
delPlan(val) {
this.form.plan = this.form.plan.filter(item => item.value.plan_id !== val.value.plan_id)
},
// 默认选择计划
toggleSelection(plans) {
if (plans) {
this.plans.filter(plan => {
if (plans.includes(plan.id)) {
const selectedPlan = this.plan.find(item => item.value.plan_id === plan.id)
if (selectedPlan) {
plan.useMoney = selectedPlan.value.use_money
}
return true
}
}).map(row => {
this.$refs.planTable.toggleRowSelection(row)
})
} else {
this.$refs.planTable.clearSelection()
}
},
// 提交新建
async submit() {
try {
// 验证基本信息表单
const visibleFields = []
// 检查项目名称是否显示
if (this.showFields.projectName) {
visibleFields.push('name')
}
// 检查项目类型是否显示
if (this.showFields.projectType) {
visibleFields.push('type')
}
// 检查预算价格是否显示
if (this.showFields.budgetPrice) {
visibleFields.push('price')
}
// 检查资金渠道是否显示
if (this.showFields.fundChannel) {
visibleFields.push('moneyWay')
}
// 检查预算计划是否显示
if (this.showFields.budgetPlan) {
visibleFields.push('plan')
}
// 检查显示的表单项是否都已填写
const missingFields = visibleFields.filter(field => {
if (field === 'moneyWay') {
return !this.form.moneyWay || this.form.moneyWay.length === 0
} else if (field === 'plan') {
return !this.form.plan || this.form.plan.length === 0
} else {
return !this.form[field]
}
})
// 单独检查供应商/付款对象字段
if (this.formType !== 'contract' && !this.form.supply) {
this.$Message.warning('请输入付款对象')
return
}
// 检查执行科室字段
if (!this.form.contract_carry_department || this.form.contract_carry_department.length === 0) {
this.$Message.warning('请选择执行科室')
return
}
if (missingFields.length > 0) {
// 生成具体的错误提示信息
const fieldNames = {
name: '项目名称',
type: '项目类型',
price: '合同预算金额',
moneyWay: '资金渠道',
plan: '关联预算计划',
supply: '付款对象'
}
const missingFieldNames = missingFields.map(field => fieldNames[field] || field).join('、')
this.$Message.warning(`请填写:${missingFieldNames}`)
return
}
// 保存事前支付表格的数据
if (!this.form.showAfterPayment && this.form.before_contract_template) {
// 获取所有输入控件
const inputs = this.$refs.beforePaymentForm.querySelectorAll('input, select, textarea')
// 遍历所有输入控件,更新值到 HTML
inputs.forEach(input => {
const fieldName = input.getAttribute('data-field')
if (fieldName) {
const field = this.form.before_contract_template.contract_template_fields.find(f => f.field === fieldName)
if (field) {
if (input.type === 'checkbox' || input.type === 'radio') {
// 对于复选框和单选框,需要找到选中的值
const checkedInput = this.$refs.beforePaymentForm.querySelector(`[data-field="${fieldName}"]:checked`)
field.value = checkedInput ? checkedInput.value : ''
// 更新 HTML 中的 checked 状态
if (checkedInput) {
checkedInput.setAttribute('checked', 'checked')
}
} else {
field.value = input.value
// 更新 HTML 中的 value
input.setAttribute('value', input.value)
}
}
}
})
// 获取更新后的 HTML
const beforeFormHtml = this.$refs.beforePaymentForm.innerHTML
// 更新模板和保存数据
this.form.before_forms = beforeFormHtml
}
// 保存事后支付表格的数据
if (this.form.showAfterPayment && this.form.contract_template) {
// 获取所有输入控件
const inputs = this.$refs.afterPaymentForm.querySelectorAll('input, select, textarea')
// 遍历所有输入控件,更新值到 HTML
inputs.forEach(input => {
const fieldName = input.getAttribute('data-field')
if (fieldName) {
const field = this.form.contract_template.contract_template_fields.find(f => f.field === fieldName)
if (field) {
if (input.type === 'checkbox' || input.type === 'radio') {
// 对于复选框和单选框,需要找到选中的值
const checkedInput = this.$refs.afterPaymentForm.querySelector(`[data-field="${fieldName}"]:checked`)
field.value = checkedInput ? checkedInput.value : ''
// 更新 HTML 中的 checked 状态
if (checkedInput) {
checkedInput.setAttribute('checked', 'checked')
}
} else {
field.value = input.value
// 更新 HTML 中的 value
input.setAttribute('value', input.value)
}
}
}
})
// 获取更新后的 HTML
const afterFormHtml = this.$refs.afterPaymentForm.innerHTML
// 更新模板和保存数据
this.form.forms = afterFormHtml
}
// 构建提交数据
const submitData = {
...this.form,
is_plan: this.form.isBudget ? 1 : 0,
money_way_id: this.form.moneyWay.join(','),
contract_plan_links: this.form.plan.map(item => ({
plan_id: item.value.plan_id,
use_money: item.value.use_money
})),
contract_carry_department: this.form.contract_carry_department.map(id => ({
carry_department_id: id
}))
}
submitData.contract_category = this.form.category
submitData.work_type = this.form.affairType
submitData.contract_type = this.form.contractType
submitData.purchase_type_id = this.form.purchaseForm
submitData.purchase_way_id = this.form.purchaseMethod
submitData.plan_price = this.form.price
submitData.is_contract = this.formType === 'contract' ? 1 : 0
// 处理事前支付模板
if (this.form.skipBeforeTemplate && !this.isEditMode) {
// 如果用户选择跳过,提交空的模板和字段列表
submitData.before_forms = this.form.before_forms
submitData.before_other_data = this.form.before_contract_template.contract_template_fields
} else if (this.form.before_contract_template) {
// 如果用户填写了模板,提交当前填写的模板和字段列表
submitData.before_forms = this.form.before_forms
submitData.before_other_data = this.form.before_contract_template.contract_template_fields
}
// 处理事后支付模板
if (this.form.skipAfterTemplate && !this.isEditMode) {
// 如果用户选择跳过,提交空的模板和字段列表
submitData.forms = this.form.forms
submitData.other_data = this.form.contract_template.contract_template_fields
} else if (this.form.contract_template) {
// 如果用户填写了模板,提交当前填写的模板和字段列表
submitData.forms = this.form.forms
submitData.other_data = this.form.contract_template.contract_template_fields
}
// 根据是新增还是编辑调用不同的API
if (this.isEditMode) {
submitData.id = this.currentContractId
await editorContract(submitData)
} else {
await addContrant(submitData)
}
// 提交成功后关闭弹窗
this.isShowAdd = false
// 如果是编辑模式,获取编辑的那条合同数据;如果是新增模式,获取最新的合同列表
let contractData = null
if (this.isEditMode && this.currentContractId) {
// 编辑模式:获取编辑的那条合同数据
const res = await getContract({ id: this.currentContractId })
if (res && res.list && res.list.data && res.list.data.length > 0) {
contractData = res.list.data[0]
}
} else {
// 新增模式:获取最新的合同列表
const res = await getContract({
page_size: 1,
page: 1,
is_auth: 1
})
if (res.list.data && res.list.data.length > 0) {
contractData = res.list.data[0]
}
}
// 检查合同数据的状态
if (contractData) {
if (contractData.req_status === 0 &&
contractData.purchase_status === 0 &&
contractData.invite_status === 0 &&
contractData.join_status === 0) {
// 所有状态都为0直接唤起付款登记窗口
this.$refs['paymentRegistration'].getContract(contractData)
this.$refs['paymentRegistration'].isShowPaymentRegistration = true
}
}
// 刷新列表
this.getContracts()
Message({
type: 'success',
message: this.isEditMode ? '编辑成功' : '新增成功'
})
} catch (error) {
console.error('提交失败:', error)
Message({
type: 'error',
message: '提交失败'
})
}
},
// 删除合同
deleteContract(id) {
delContract({
id
}).then(res => {
Message({
type: 'success',
message: '操作成功'
})
this.getContracts()
})
},
// 打开OA流程流水号弹窗
openFlowDialog(row, flowType) {
const flow2contractField = new Map([
['caigou', 'purchase_status'],
['hetonghuiqian', 'join_status'],
['qingshi', 'req_status'],
['zhaobiaowenjianshencha', 'invite_status']
])
this.currentRow = row
this.currentFlow = flowType
this.flowId = ''
// 初始化状态将当前状态值转换为OA状态值
// 状态选择中只有 0流转中和 1已办结
// 1待申请和 2流转中-> 0流转中3已办结-> 1已办结
const currentStatus = row[flow2contractField.get(flowType)]
if (currentStatus === 3) {
this.flowStatus = 1 // 已办结
} else {
this.flowStatus = 0 // 待申请或流转中都设置为流转中
}
this.isShowFlowDialog = true
},
// 取消弹窗
cancelFlowDialog() {
this.isShowFlowDialog = false
this.currentRow = null
this.currentFlow = ''
this.flowId = ''
this.flowStatus = 0
},
// 打开编辑供应商弹窗
openSupplyDialog(row) {
this.editingSupplyRow = row
this.editingSupplyValue = row.supply || ''
this.isShowSupplyDialog = true
},
// 取消编辑供应商弹窗
cancelSupplyDialog() {
this.isShowSupplyDialog = false
this.editingSupplyRow = null
this.editingSupplyValue = ''
},
// 提交供应商信息
async submitSupply() {
if (!this.editingSupplyRow || !this.editingSupplyRow.id) {
return
}
try {
await editorContract({
id: this.editingSupplyRow.id,
supply: this.editingSupplyValue || ''
})
Message({
type: 'success',
message: '保存成功'
})
// 更新行数据
this.editingSupplyRow.supply = this.editingSupplyValue
// 关闭弹窗
this.cancelSupplyDialog()
// 刷新列表
this.getContracts()
} catch (error) {
console.error('保存供应商失败:', error)
Message({
type: 'error',
message: '保存失败,请重试'
})
}
},
// 提交OA流程流水号
async submitFlowId() {
if (!this.flowId || !this.flowId.trim()) {
Message({
type: 'warning',
message: '请输入OA流程流水号'
})
return
}
try {
const flow_type = new Map([
['caigou', 2],
['hetonghuiqian', 3],
['qingshi', 17],
['zhaobiaowenjianshencha', 27]
])
const flow2contractField = new Map([
['caigou', 'purchase_status'],
['hetonghuiqian', 'join_status'],
['qingshi', 'req_status'],
['zhaobiaowenjianshencha', 'invite_status']
])
if (!this.oatoken) {
const res = await getOatoken()
this.oatoken = res.oatoken
}
await updateContract({
out_contract_id: this.currentRow.id,
flow_type: flow_type.get(this.currentFlow),
status: this.flowStatus,
flow_id: this.flowId.trim()
})
Message({
type: 'success',
message: '提交成功'
})
this.cancelFlowDialog()
this.getContracts()
} catch (error) {
console.error('提交失败:', error)
Message({
type: 'error',
message: '提交失败,请重试'
})
}
},
handleCategoryChange() {
this.form.affairType = ''
this.form.contractType = ''
this.form.purchaseForm = ''
this.form.purchaseMethod = ''
this.updateTypeOptions()
},
handleAffairTypeChange() {
this.form.contractType = ''
this.form.purchaseForm = ''
this.form.purchaseMethod = ''
this.updateTypeOptions()
},
handleContractTypeChange() {
this.form.purchaseForm = ''
this.form.purchaseMethod = ''
this.updateTypeOptions()
},
handlePurchaseFormChange() {
this.form.purchaseMethod = ''
this.updateTypeOptions()
},
handlePurchaseMethodChange() {
this.updateTypeOptions()
},
updateTypeOptions() {
// 重置所有选项
this.affairTypeOptions = []
this.contractTypeOptions = []
this.purchaseFormOptions = []
this.purchaseMethodOptions = []
this.purchaseSubFormOptions = []
// 获取当前选中的分类
const selectedCategory = this.categoryOptions.find(item => item.id === this.form.category)
if (!selectedCategory) {
// 如果有分类选项但没有选中值,自动选择第一个
if (this.categoryOptions && this.categoryOptions.length > 0) {
this.form.category = this.categoryOptions[0].id
this.handleCategoryChange()
}
return
}
// 更新事项类型选项
this.affairTypeOptions = selectedCategory.children || []
// 如果有事项类型选项但没有选中值,自动选择第一个
if (this.affairTypeOptions.length > 0 && !this.form.affairType) {
this.form.affairType = this.affairTypeOptions[0].id
this.handleAffairTypeChange()
}
// 获取当前选中的事项类型
const selectedAffairType = this.affairTypeOptions.find(item => item.id === this.form.affairType)
if (!selectedAffairType) return
// 更新合同类型选项
this.contractTypeOptions = selectedAffairType.children || []
// 如果有合同类型选项但没有选中值,自动选择第一个
if (this.contractTypeOptions.length > 0 && !this.form.contractType) {
this.form.contractType = this.contractTypeOptions[0].id
this.handleContractTypeChange()
}
// 获取当前选中的合同类型
const selectedContractType = this.contractTypeOptions.find(item => item.id === this.form.contractType)
if (!selectedContractType) return
// 更新采购形式选项
this.purchaseFormOptions = selectedContractType.children || []
// 如果有采购形式选项但没有选中值,自动选择第一个
if (this.purchaseFormOptions.length > 0 && !this.form.purchaseForm) {
this.form.purchaseForm = this.purchaseFormOptions[0].id
this.handlePurchaseFormChange()
}
// 获取当前选中的采购形式
const selectedPurchaseForm = this.purchaseFormOptions.find(item => item.id === this.form.purchaseForm)
if (!selectedPurchaseForm) return
// 更新采购子形式选项
this.purchaseSubFormOptions = selectedPurchaseForm.children || []
// 获取当前选中的采购子形式
const selectedPurchaseSubForm = this.purchaseSubFormOptions.find(item => item.id === this.form.purchaseSubForm)
// 如果有采购子形式使用其children作为采购方式
if (selectedPurchaseSubForm && selectedPurchaseSubForm.children) {
this.purchaseMethodOptions = selectedPurchaseSubForm.children
}
// 如果没有采购子形式使用采购形式的children作为采购方式
else if (selectedPurchaseForm.children) {
this.purchaseMethodOptions = selectedPurchaseForm.children
}
// 如果有采购方式选项但没有选中值,自动选择第一个
if (this.purchaseMethodOptions.length > 0 && !this.form.purchaseMethod) {
this.form.purchaseMethod = this.purchaseMethodOptions[0].id
this.handlePurchaseMethodChange()
}
},
nextStep() {
if (this.currentStep === 1) {
// 验证每个有选项的下拉框是否已选择
if (this.categoryOptions.length > 0 && !this.form.category) {
this.$Message.warning('请选择合同分类')
return
}
if (this.affairTypeOptions.length > 0 && !this.form.affairType) {
this.$Message.warning('请选择事项类型')
return
}
if (this.contractTypeOptions.length > 0 && !this.form.contractType) {
this.$Message.warning('请选择合同类型')
return
}
if (this.purchaseFormOptions.length > 0 && !this.form.purchaseForm) {
this.$Message.warning('请选择采购形式')
return
}
if (this.purchaseMethodOptions.length > 0 && !this.form.purchaseMethod) {
this.$Message.warning('请选择采购方式')
return
}
// 获取模版配置
this.getTemplateConfig()
} else if (this.currentStep === 2) {
// 在进入第三步之前,确保所有必要的数据都被正确设置
if (!this.form.type) {
this.form.type = ''
}
if (!this.form.contract_carry_department) {
this.form.contract_carry_department = []
}
if (!this.form.moneyWay) {
this.form.moneyWay = []
}
// 检查是否有支付表格
if (!this.form.before_contract_template) {
// 如果没有事前支付表格,直接提交
this.submit()
} else {
// 如果有事前支付表格,进入第三步
this.currentStep++
}
}
},
prevStep() {
if (this.currentStep === 3) {
// 如果在事后支付表格,先保存内容
if (this.form.showAfterPayment &&
this.form.contract_template &&
this.form.contract_template.contract_template_fields &&
this.$refs.afterPaymentForm) {
// 获取所有输入控件
const inputs = this.$refs.afterPaymentForm.querySelectorAll('input, select, textarea')
// 遍历所有输入控件,更新值到 HTML
inputs.forEach(input => {
const fieldName = input.getAttribute('data-field')
if (fieldName) {
const field = this.form.contract_template.contract_template_fields.find(f => f.field === fieldName)
if (field) {
if (input.type === 'checkbox' || input.type === 'radio') {
// 对于复选框和单选框,需要找到选中的值
const checkedInput = this.$refs.afterPaymentForm.querySelector(`[data-field="${fieldName}"]:checked`)
field.value = checkedInput ? checkedInput.value : ''
// 更新 HTML 中的 checked 状态
if (checkedInput) {
checkedInput.setAttribute('checked', 'checked')
}
} else {
field.value = input.value
// 更新 HTML 中的 value
input.setAttribute('value', input.value)
}
}
}
})
// 获取更新后的 HTML
const afterFormHtml = this.$refs.afterPaymentForm.innerHTML
// 更新模保存数据
this.form.forms = afterFormHtml
}
if (this.form.showAfterPayment && this.form.before_contract_template) {
// 如果在事后支付表格且有事前支付表格,则返回事前支付表格
this.form.showAfterPayment = false
} else {
// 其他情况正常返回上一步
this.currentStep--
}
} else {
this.currentStep--
}
},
// 获取模版配置参数
async getTemplateConfig() {
try {
const params = {
category: this.form.category || 0,
work_type: this.form.affairType || 0,
contract_type: this.form.contractType || 0,
purchase_form: this.form.purchaseForm || 0
}
console.log('请求模版配置参数:', params)
const res = await getContractCategoryTemplateConfigParams(params)
console.log('模版配置返回数据:', res)
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || '获取模版配置失败')
return
}
// 在编辑模式下保存当前的forms和before_forms值
let currentForms = null
let currentBeforeForms = null
if (this.isEditMode) {
currentForms = this.form.forms
currentBeforeForms = this.form.before_forms
}
// 根据返回的配置参数设置表单
this.setFormConfig(res)
// 初始化合同配置的默认值
// 设置是否为简易流程
if (res.is_simple_default !== undefined) {
this.form.is_simple = res.is_simple_default
}
// 设置是否为河道处收费类项目
if (res.has_charge_default !== undefined) {
this.form.has_charge = res.has_charge_default
}
// 设置是否为预算内确定项目
if (res.is_plan_default !== undefined) {
this.form.isBudget = res.is_plan_default
}
// 设置是否为代建项目
if (res.is_substitute_default !== undefined) {
this.form.is_substitute = res.is_substitute_default
}
// 设置编辑权限
this.editPermissions.is_simple = res.edit_is_simple === 1
this.editPermissions.has_charge = res.edit_has_charge === 1
this.editPermissions.is_plan = res.edit_is_plan === 1
this.editPermissions.is_substitute = res.edit_is_substitute === 1
// 在编辑模式下恢复forms和before_forms的值
if (this.isEditMode) {
this.form.forms = currentForms
this.form.before_forms = currentBeforeForms
}
if (this.form.forms && this.form.forms.trim() !== '') {
this.form.skipAfterTemplate = true
}
if (this.form.before_forms && this.form.before_forms.trim() !== '') {
this.form.skipBeforeTemplate = true
}
// 进入下一步
this.currentStep++
} catch (error) {
console.log('获取模版配置失败1:', error)
this.$message.error('获取模版配置失败1')
}
},
// 设置表单配置
setFormConfig(config) {
console.log('模版配置参数:', config)
// 只更新显示/隐藏状态,不影响数据来源
Object.assign(this.showFields, {
projectName: config.project_name === 1, // 项目名称
projectType: config.project_type === 1, // 项目类型
budgetPrice: config.budget_price === 1, // 预算价格
fundChannel: config.fund_channel === 1, // 资金渠道
budgetPlan: config.budget_plan === 1 // 预算计划
})
// 保存支付表格模板
if (config.before_contract_template) {
// 处理事前支付表格模板
let beforeTemplate = config.before_contract_template.template
if (beforeTemplate && config.before_contract_template.contract_template_fields) {
console.log('处理事前支付表格模板:', config.before_contract_template.contract_template_fields)
// 遍历字段列表,为每个字段找到对应的控件
config.before_contract_template.contract_template_fields.forEach(field => {
// 在模板中查找 data-field 属性值与字段名相同的控件
const regex = new RegExp(`<[^>]*data-field="${field.field}"[^>]*>`, 'g')
const matches = beforeTemplate.match(regex)
if (matches) {
matches.forEach(match => {
// 获取控件类型
let controlType = 'text'
if (match.includes('type="number"')) controlType = 'number'
else if (match.includes('type="date"')) controlType = 'date'
else if (match.includes('type="checkbox"')) controlType = 'checkbox'
else if (match.includes('type="radio"')) controlType = 'radio'
else if (match.includes('<select')) controlType = 'select'
else if (match.includes('<textarea')) controlType = 'textarea'
// 根据控件类型设置 id 和 value
let newControl = match
// 添加 id
if (!newControl.includes('id=')) {
newControl = newControl.replace('data-field', `id="before_${field.field}" data-field`)
}
// 设置 value
if (controlType === 'checkbox' || controlType === 'radio') {
// 对于复选框和单选框,需要处理 options
const options = field.options ? field.options.split(',') : []
options.forEach(opt => {
const value = typeof opt === 'object' ? opt.value : opt
const optionRegex = new RegExp(`<input[^>]*value="${value}"[^>]*>`, 'g')
const optionMatches = newControl.match(optionRegex)
if (optionMatches) {
optionMatches.forEach(optionMatch => {
const checked = field.value === value ? 'checked' : ''
newControl = newControl.replace(optionMatch, optionMatch.replace('>', ` ${checked}>`))
})
}
})
} else {
// 对于其他控件,直接设置 value
if (!newControl.includes('value=')) {
newControl = newControl.replace('>', ` value="${field.value || ''}">`)
} else {
newControl = newControl.replace(/value="[^"]*"/, `value="${field.value || ''}"`)
}
}
// 替换原控件
beforeTemplate = beforeTemplate.replace(match, newControl)
})
}
})
}
this.form.before_contract_template = config.before_contract_template
this.form.before_contract_template.template = beforeTemplate
}
if (config.contract_template) {
// 处理事后支付表格模板
let afterTemplate = config.contract_template.template
if (afterTemplate && config.contract_template.contract_template_fields) {
console.log('处理事后支付表格模板:', config.contract_template.contract_template_fields)
// 遍历字段列表,为每个字段找到对应的控件
config.contract_template.contract_template_fields.forEach(field => {
// 在模板中查找 data-field 属性值与字段名相同的控件
const regex = new RegExp(`<[^>]*data-field="${field.field}"[^>]*>`, 'g')
const matches = afterTemplate.match(regex)
if (matches) {
matches.forEach(match => {
// 获取控件类型
let controlType = 'text'
if (match.includes('type="number"')) controlType = 'number'
else if (match.includes('type="date"')) controlType = 'date'
else if (match.includes('type="checkbox"')) controlType = 'checkbox'
else if (match.includes('type="radio"')) controlType = 'radio'
else if (match.includes('<select')) controlType = 'select'
else if (match.includes('<textarea')) controlType = 'textarea'
// 根据控件类型设置 id 和 value
let newControl = match
// 添加 id
if (!newControl.includes('id=')) {
newControl = newControl.replace('data-field', `id="after_${field.field}" data-field`)
}
// 设置 value
if (controlType === 'checkbox' || controlType === 'radio') {
// 对于复选框和单选框,需要处理 options
const options = field.options ? field.options.split(',') : []
options.forEach(opt => {
const value = typeof opt === 'object' ? opt.value : opt
const optionRegex = new RegExp(`<input[^>]*value="${value}"[^>]*>`, 'g')
const optionMatches = newControl.match(optionRegex)
if (optionMatches) {
optionMatches.forEach(optionMatch => {
const checked = field.value === value ? 'checked' : ''
newControl = newControl.replace(optionMatch, optionMatch.replace('>', ` ${checked}>`))
})
}
})
} else {
// 对于其他控件,直接设置 value
if (!newControl.includes('value=')) {
newControl = newControl.replace('>', ` value="${field.value || ''}">`)
} else {
newControl = newControl.replace(/value="[^"]*"/, `value="${field.value || ''}"`)
}
}
// 替换原控件
afterTemplate = afterTemplate.replace(match, newControl)
})
}
})
}
this.form.contract_template = config.contract_template
this.form.contract_template.template = afterTemplate
}
},
async handleAddContract() {
// 先设置 loading 状态
this.loading = true
try {
// 显示弹窗
this.isShowAdd = true
this.isEditMode = false
this.isFromPayment = false
// 等待获取分类配置
await this.getCategoryOptions()
// 先重置表单
this.resetForm()
} catch (error) {
console.error('加载数据失败:', error)
this.$message.error('加载数据失败')
} finally {
// 无论成功失败都关闭 loading
this.loading = false
}
},
handleAddContractOk() {
// 这个方法可以移除,因为功能已经被 nextStep 和 submit 方法替代
},
handleAddContractCancel() {
// 这个方法可以移除,因为功能已经被 cancel 方法替代
},
cancel() {
this.isShowAdd = false
this.resetForm()
},
resetForm() {
this.currentStep = 1
this.isEditMode = false
this.currentContractId = null
this.form = {
category: '',
affairType: '',
contractType: '',
purchaseForm: '',
purchaseSubForm: '',
purchaseMethod: '',
name: '',
type: '',
moneyWay: [],
plan: [],
plan_display: '',
price: 0,
supply: '',
is_simple: 0,
has_charge: 0,
isBudget: 0,
is_substitute: 0,
request: 0,
purchaseApproval: 0,
tenderReview: 0,
contractSign: 0,
expenses: [
{
content: '',
amount: 0,
ratio: '',
payee: ''
}
],
showAfterPayment: true,
contract_carry_department: []
}
this.affairTypeOptions = []
this.contractTypeOptions = []
this.purchaseFormOptions = []
this.purchaseSubFormOptions = []
this.purchaseMethodOptions = []
// 清空资金渠道选择
this.selectedMoneyWayLabels = []
this.$nextTick(() => {
if (this.$refs.moneyWayTree) {
this.$refs.moneyWayTree.setCheckedKeys([])
}
})
// 清空预算计划选择
this.plan = []
},
// 获取分类配置
async getCategoryOptions() {
try {
// 获取分类配置
const res = await getContractCategoryTemplateBaseConfig()
if (res.errcode !== undefined) {
this.$message.error(res.errmsg || '获取分类配置失败')
return
}
// 保存采购方式映射
this.purchaseMethodsMap = res.purchase_methods || {}
// 设置分类选项 - 从第二级开始(合同类、报销类、其他支出类)
this.categoryOptions = res.map?.[0]?.children || []
// 递归处理每一层的 children确保所有层级都被正确解析
const processChildren = (items) => {
if (!items) return
items.forEach(item => {
if (item.children) {
// 确保每个子项都有正确的 id 和 name/value
item.children = item.children.map(child => ({
id: child.id,
name: child.name || child.value, // 添加对 value 字段的支持
value: child.value || child.name, // 保留原始的 value 字段
children: child.children || []
}))
processChildren(item.children)
}
})
}
// 处理所有层级的 children
processChildren(this.categoryOptions)
// 初始化其他选项
this.updateTypeOptions()
// 递归解析所有id-name映射
this.categoryIdNameMap = this.parseIdNameMap(res.map)
} catch (error) {
console.error('获取分类配置失败:', error)
this.$message.error('获取分类配置失败')
}
},
// 初始化采购方式映射
initPurchaseMethodsMap() {
this.purchaseMethodsMap = {}
// 递归遍历分类选项,构建采购方式映射
const traverse = (items) => {
items.forEach(item => {
if (item.children) {
traverse(item.children)
} else {
// 如果是叶子节点,添加到映射中
this.purchaseMethodsMap[item.id] = item.name
}
})
}
traverse(this.categoryOptions)
},
// 获取分类名称的方法
getCategoryName(id) {
const category = this.categoryOptions.find(item => item.id === id)
return category ? category.value : ''
},
getAffairTypeName(id) {
const category = this.categoryOptions.find(item => item.id === this.form.category)
if (category && category.children) {
const affairType = category.children.find(item => item.id === id)
return affairType ? affairType.name : ''
}
return ''
},
getContractTypeName(id) {
const category = this.categoryOptions.find(item => item.id === this.form.category)
if (category && category.children) {
const affairType = category.children.find(item => item.id === this.form.affairType)
if (affairType && affairType.children) {
const contractType = affairType.children.find(item => item.id === id)
return contractType ? contractType.name : ''
}
}
return ''
},
getPurchaseFormName(id) {
const category = this.categoryOptions.find(item => item.id === this.form.category)
if (category && category.children) {
const affairType = category.children.find(item => item.id === this.form.affairType)
if (affairType && affairType.children) {
const contractType = affairType.children.find(item => item.id === this.form.contractType)
if (contractType && contractType.children) {
const purchaseForm = contractType.children.find(item => item.id === id)
return purchaseForm ? purchaseForm.name : ''
}
}
}
return ''
},
getPurchaseMethodName(id) {
// 从嵌套结构中查找采购方式名称
const category = this.categoryOptions.find(item => item.id === this.form.category)
if (category && category.children) {
const affairType = category.children.find(item => item.id === this.form.affairType)
if (affairType && affairType.children) {
const contractType = affairType.children.find(item => item.id === this.form.contractType)
if (contractType && contractType.children) {
const purchaseForm = contractType.children.find(item => item.id === this.form.purchaseForm)
if (purchaseForm && purchaseForm.children) {
// 如果有选择子表单,从子表单中查找
if (this.form.purchaseSubForm) {
const purchaseSubForm = purchaseForm.children.find(item => item.id === this.form.purchaseSubForm)
if (purchaseSubForm && purchaseSubForm.children) {
const purchaseMethod = purchaseSubForm.children.find(item => item.id === id)
if (purchaseMethod) {
return purchaseMethod.name
}
}
}
// 直接从采购形式中查找
const purchaseMethod = purchaseForm.children.find(item => item.id === id)
if (purchaseMethod) {
return purchaseMethod.name
}
}
}
}
}
// 备用:从映射中查找
return this.purchaseMethodsMap[id] || ''
},
// 更新预算计划显示文本
updatePlanDisplay() {
if (this.form.plan && this.form.plan.length > 0) {
this.form.plan_display = `已选择 ${this.form.plan.length} 个预算计划`
} else {
this.form.plan_display = ''
}
},
// 新增方法:移除预算计划
removePlan(item) {
// 从 form.plan 中移除
this.form.plan = this.form.plan.filter(p => p.value.plan_id !== item.value.plan_id)
// 同时更新临时变量
this.plan = this.form.plan
// 更新显示文本
this.form.plan_display = this.form.plan.map(p => p.label).join(', ')
},
// 切换到事后支付表格
nextPaymentStep() {
// 保存事前支付表格的数据
if (this.form.before_contract_template && !this.form.showAfterPayment) {
// 获取所有输入控件
const inputs = this.$refs.beforePaymentForm.querySelectorAll('input, select, textarea')
// 遍历所有输入控件,更新值到 HTML
inputs.forEach(input => {
const fieldName = input.getAttribute('data-field')
if (fieldName) {
const field = this.form.before_contract_template.contract_template_fields.find(f => f.field === fieldName)
if (field) {
if (input.type === 'checkbox' || input.type === 'radio') {
// 对于复选框和单选框,需要找到选中的值
const checkedInput = this.$refs.beforePaymentForm.querySelector(`[data-field="${fieldName}"]:checked`)
field.value = checkedInput ? checkedInput.value : ''
// 更新 HTML 中的 checked 状态
if (checkedInput) {
checkedInput.setAttribute('checked', 'checked')
}
} else {
field.value = input.value
// 更新 HTML 中的 value
input.setAttribute('value', input.value)
}
}
}
})
// 获取更新后的 HTML
const beforeFormHtml = this.$refs.beforePaymentForm.innerHTML
// 更新模板和保存数据
this.form.before_forms = beforeFormHtml
}
this.form.showAfterPayment = true
},
// 处理资金列支渠道树形选择
handleMoneyWayCheck(data, checkedInfo) {
const { checkedKeys, checkedNodes } = checkedInfo
// 过滤出叶子节点(没有子级的节点)
const leafNodes = checkedNodes.filter(node => !node.children || node.children.length === 0)
// 更新选中的值
this.form.moneyWay = leafNodes.map(node => node.id)
// 更新显示的标签
this.selectedMoneyWayLabels = leafNodes.map(node => ({
id: node.id,
label: node.name
}))
},
// 移除已选择的资金列支渠道
removeMoneyWay(id) {
// 从选中数组中移除
const index = this.form.moneyWay.indexOf(id)
if (index > -1) {
this.form.moneyWay.splice(index, 1)
}
// 从标签数组中移除
const labelIndex = this.selectedMoneyWayLabels.findIndex(item => item.id === id)
if (labelIndex > -1) {
this.selectedMoneyWayLabels.splice(labelIndex, 1)
}
// 更新树形组件的选中状态
this.$nextTick(() => {
if (this.$refs.moneyWayTree) {
this.$refs.moneyWayTree.setCheckedKeys(this.form.moneyWay)
}
})
},
// 更新已选择的资金列支渠道标签
updateSelectedMoneyWayLabels() {
if (!this.form.moneyWay || this.form.moneyWay.length === 0) {
this.selectedMoneyWayLabels = []
return
}
// 根据选中的ID数组从planTypes中查找对应的节点信息
const selectedNodes = []
const findNodes = (nodes, ids) => {
nodes.forEach(node => {
if (ids.includes(node.id)) {
selectedNodes.push({
id: node.id,
label: node.name
})
}
if (node.children && node.children.length > 0) {
findNodes(node.children, ids)
}
})
}
findNodes(this.planTypes, this.form.moneyWay)
this.selectedMoneyWayLabels = selectedNodes
// 更新树形组件的选中状态
this.$nextTick(() => {
if (this.$refs.moneyWayTree) {
this.$refs.moneyWayTree.setCheckedKeys(this.form.moneyWay)
}
})
},
// 获取科室名称的方法
getDepartmentName(id) {
const dept = this.departments.find(item => item.id === id)
return dept ? dept.name : ''
},
// 移除科室
removeDepartment(id) {
this.form.contract_carry_department = this.form.contract_carry_department.filter(item => item !== id)
},
// 跳过事前支付表格
skipPrePayment() {
this.form.showAfterPayment = true
if (this.isEditMode) {
this.form.before_forms = this.originBeforeForms
} else {
this.form.before_forms = ''
this.form.before_other_data = []
this.form.skipBeforeTemplate = true
}
},
// 跳过事后支付表格
skipPostPayment() {
if (this.isEditMode) {
this.form.forms = this.originAfterForms
} else {
this.form.forms = ''
this.form.other_data = []
this.form.skipAfterTemplate = true
}
this.submit()
},
// 更新事前表格字段值
updateBeforeFieldValue(field, value) {
if (this.form.before_contract_template && this.form.before_contract_template.contract_template_fields) {
const fieldObj = this.form.before_contract_template.contract_template_fields.find(f => f.field === field)
if (fieldObj) {
fieldObj.value = value
}
}
},
// 更新事后表格字段值
updateAfterFieldValue(field, value) {
if (this.form.contract_template && this.form.contract_template.contract_template_fields) {
const fieldObj = this.form.contract_template.contract_template_fields.find(f => f.field === field)
if (fieldObj) {
fieldObj.value = value
}
}
},
// 编辑按钮点击事件
async handleEdit(row, isFromPayment = false) {
this.isEditMode = true
this.currentContractId = row.id
this.isShowAdd = true
this.currentStep = 1 // 始终保持初始值为1
this.isFromPayment = isFromPayment // 设置是否从付款登记进入
try {
// 1. 先获取分类选项
await this.getCategoryOptions()
// 2. 获取合同详情
const res = await getContract({ id: row.id })
if (res && res.list && res.list.data && res.list.data.length > 0) {
const detail = res.list.data[0]
// 3. 通过正常的流程来初始化下拉框
// 设置分类
if (detail.contract_category) {
this.form.category = detail.contract_category.category
this.handleCategoryChange(detail.contract_category.category)
}
// 设置事项类型
if (detail.work_type) {
this.form.affairType = detail.work_type
this.handleAffairTypeChange(detail.work_type)
}
// 设置合同类型
if (detail.contract_type) {
this.form.contractType = detail.contract_type
this.handleContractTypeChange(detail.contract_type)
}
// 设置采购形式
if (detail.purchase_type_id) {
this.form.purchaseForm = detail.purchase_type_id
this.handlePurchaseFormChange(detail.purchase_type_id)
}
// 设置采购方式
if (detail.purchase_way_id) {
this.form.purchaseMethod = detail.purchase_way_id
}
// 保存其他表单数据
this.form = {
...this.form,
forms: detail.forms,
before_forms: detail.before_forms,
originBeforeForms: detail.before_forms,
originAfterForms: detail.forms,
other_data: detail.other_data,
before_other_data: detail.before_other_data,
type: detail.type,
isBudget: detail.is_plan,
is_simple: detail.is_simple,
has_charge: detail.has_charge,
is_substitute: detail.is_substitute,
supply: detail.supply,
price: detail.plan_price,
name: detail.name,
moneyWay: detail.money_way_id ? detail.money_way_id.split(',').map(Number) : [],
plan: detail.plans.map(item => {
return {
label: item.name,
value: {
plan_id: item.id,
use_money: detail.plan_link ? detail.plan_link.filter(item1 => {
return item1.plan_id === item.id
})[0]?.use_money : 0,
new_money: item.money
}
}
}),
contract_carry_department: detail.contract_carry_department.map(i => i.carry_department_id),
before_contract_template: detail.before_contract_template,
contract_template: detail.contract_template
}
// 设置预算计划显示文本
if (this.form.plan && this.form.plan.length > 0) {
this.form.plan_display = this.form.plan.map(item => item.label).join(', ')
}
// 更新资金渠道标签显示
this.$nextTick(() => {
this.updateSelectedMoneyWayLabels()
})
// 如果是从付款登记进入,手动触发流程
if (isFromPayment) {
this.disablePrevStep = true
// 手动触发第一步到第二步的流程
await this.nextStep()
// 手动触发第二步到第三步的流程
await this.nextStep()
}
}
} catch (error) {
console.error('初始化编辑数据失败:', error)
Message({
type: 'error',
message: '初始化编辑数据失败'
})
}
},
// 新增获取合同详情方法
async getContractDetail(id) {
try {
const res = await getContract({ id })
if (res && res.list && res.list.data && res.list.data.length > 0) {
const detail = res.list.data[0]
console.log('detail', detail)
// 填充表单数据
this.form = {
...this.form,
// 第一步的五个分类选择
category: detail.category,
affairType: detail.work_type,
contractType: detail.contract_type,
purchaseForm: detail.purchase_type_id, // 采购形式
purchaseMethod: detail.purchase_way_id, // 采购方式
// 其他基本信息
type: detail.type,
isBudget: detail.is_plan,
is_simple: detail.is_simple,
has_charge: detail.has_charge,
is_substitute: detail.is_substitute,
supply: detail.supply,
price: detail.plan_price,
name: detail.name,
moneyWay: detail.money_way_id ? detail.money_way_id.split(',').map(Number) : [],
plan: detail.plans.map(item => {
return {
label: item.name,
value: {
plan_id: item.id,
use_money: detail.plan_link ? detail.plan_link.filter(item1 => {
return item1.plan_id === item.id
})[0]?.use_money : 0,
new_money: item.money
}
}
}),
contract_carry_department: detail.contract_carry_department.map(i => i.carry_department_id),
before_contract_template: detail.before_contract_template,
contract_template: detail.contract_template,
before_forms: detail.before_forms,
before_other_data: detail.before_other_data,
forms: detail.forms,
other_data: detail.other_data
}
// 设置预算计划显示文本
if (this.form.plan && this.form.plan.length > 0) {
this.form.plan_display = this.form.plan.map(item => item.label).join(', ')
}
// 更新资金渠道标签显示
this.$nextTick(() => {
this.updateSelectedMoneyWayLabels()
})
// 确保停留在第一步
this.currentStep = 1
}
} catch (error) {
console.error('获取合同详情失败:', error)
Message({
type: 'error',
message: '获取合同详情失败'
})
}
},
// 验证表单
validateForm() {
// 验证第一步的必填字段
if (this.currentStep === 1) {
if (!this.form.category) {
Message({
type: 'error',
message: '请选择合同分类'
})
return false
}
if (!this.form.affairType) {
Message({
type: 'error',
message: '请选择事项类型'
})
return false
}
if (!this.form.contractType) {
Message({
type: 'error',
message: '请选择合同类型'
})
return false
}
if (!this.form.purchaseForm) {
Message({
type: 'error',
message: '请选择采购形式'
})
return false
}
if (!this.form.purchaseMethod) {
Message({
type: 'error',
message: '请选择采购方式'
})
return false
}
}
// 验证第二步的必填字段
if (this.currentStep === 2) {
if (!this.form.type) {
Message({
type: 'error',
message: '请选择合同性质'
})
return false
}
if (!this.form.name) {
Message({
type: 'error',
message: '请输入合同名称'
})
return false
}
if (!this.form.price) {
Message({
type: 'error',
message: '请输入合同金额'
})
return false
}
if (!this.form.moneyWay || this.form.moneyWay.length === 0) {
Message({
type: 'error',
message: '请选择资金来源'
})
return false
}
if (!this.form.plan || this.form.plan.length === 0) {
Message({
type: 'error',
message: '请选择预算计划'
})
return false
}
if (!this.form.contract_carry_department || this.form.contract_carry_department.length === 0) {
Message({
type: 'error',
message: '请选择合同承办部门'
})
return false
}
}
return true
},
async checkFormsBeforePayment(row) {
try {
// 获取最新的合同详情
const res = await detailContract({ id: row.id })
if (res) {
this.$refs['paymentRegistration'].getContract(row)
this.$refs['paymentRegistration'].isShowPaymentRegistration = true
} else {
this.$Message.error('获取合同详情失败')
}
} catch (error) {
console.error('Error checking forms:', error)
this.$Message.error('检查表单时发生错误')
}
},
openPaymentFormPreview(type) {
this.previewType = type
if (type === 'before') {
const dom = this.$refs.beforePaymentForm
if (dom && this.form.before_contract_template) {
this.form.before_forms = syncFormDomToHtml(dom, this.form.before_contract_template.contract_template_fields)
}
} else {
const dom = this.$refs.afterPaymentForm
if (dom && this.form.contract_template) {
this.form.forms = syncFormDomToHtml(dom, this.form.contract_template.contract_template_fields)
}
}
this.showPaymentFormPreview = true
},
handlePaymentFormPreviewClose() {
if (this.previewType === 'before') {
const dom = this.$refs.previewBeforePaymentForm
if (dom && this.form.before_contract_template) {
this.form.before_forms = syncFormDomToHtml(dom, this.form.before_contract_template.contract_template_fields)
}
} else {
const dom = this.$refs.previewAfterPaymentForm
if (dom && this.form.contract_template) {
this.form.forms = syncFormDomToHtml(dom, this.form.contract_template.contract_template_fields)
}
}
this.showPaymentFormPreview = false
},
// 添加分类选择处理方法
handleCategorySelect(category) {
this.selectedCategory = category
// 根据分类筛选数据
this.select.category = category
this.getContracts()
},
// 添加按分类新增合同的方法
async handleAddContractByCategory(type) {
this.formType = type
this.loading = true
try {
// 0. 先获取最新执行部门信息
await this.getDepartment()
// 1. 获取当前用户所在部门id
const userDeptId = this.$store.state.user.info.department_id
// 2. 显示弹窗、初始化表单
this.isShowAdd = true
this.isEditMode = false
this.isFromPayment = false
await this.getCategoryOptions()
this.resetForm()
// 3. 默认选中用户部门
if (userDeptId) {
this.form.contract_carry_department = [userDeptId]
}
// ...后续自动选择分类等逻辑...
if (this.categoryOptions && this.categoryOptions.length > 0) {
const categoryOption = this.categoryOptions.find(item => {
const name = item.name || item.value || ''
return name.includes(type === 'contract' ? '合同' : type === 'reimbursement' ? '报销' : '其他支出')
})
if (categoryOption) {
this.form.category = categoryOption.id
this.handleCategoryChange()
}
}
} catch (error) {
console.error('加载数据失败:', error)
this.$message.error('加载数据失败')
} finally {
this.loading = false
}
},
// 递归解析所有id-name映射
parseIdNameMap(list, map = {}) {
list.forEach(item => {
map[item.id] = item.name || item.value;
if (item.children && item.children.length) {
this.parseIdNameMap(item.children, map);
}
});
return map;
}
}
}
</script>
<style lang="scss" scoped>
.selects {
display: flex;
flex-wrap: wrap;
& > div {
margin-bottom: 6px;
}
}
.selectTop {
margin-top: 10px;
}
.contract-add-plan {
min-height: 30px;
border: 1px solid #dcdee2;
border-radius: 4px;
display: flex;
flex-wrap: wrap;
align-items: center;
align-content: center;
padding: 0 8px;
&-no-plan {
height: 30px;
line-height: 30px;
color: #CDD0D5;
}
}
.slot-btns {
display: flex;
flex-wrap: wrap;
align-content: center;
justify-content: flex-start;
&-item {
margin: 0 6px 4px 0;
}
}
.xy-table-item-label {
width: 200px;
}
.xy-table-item-price {
position: relative;
&::after {
position: absolute;
right: 0;
top: 0;
content: '(元)'
}
}
.contract-add-modal {
:deep(.ivu-modal-content) {
border-radius: 8px;
overflow: hidden;
}
:deep(.ivu-modal-header) {
background: #f8f8f9;
padding: 16px 24px;
border-bottom: 1px solid #e8eaec;
}
:deep(.ivu-modal-body) {
padding: 24px;
}
.steps-header {
display: flex;
justify-content: center;
margin-bottom: 32px;
padding: 0 20px;
.step {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
flex: 0 0 auto;
width: 200px;
&:not(:last-child)::after {
content: '';
position: absolute;
top: 16px;
left: 50%;
width: 100%;
height: 2px;
background: #e8eaec;
z-index: 1;
}
.step-number {
width: 32px;
height: 32px;
border-radius: 50%;
background: #e8eaec;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
position: relative;
z-index: 2;
}
.step-text {
color: #808695;
font-size: 14px;
}
&.active {
.step-number {
background: #2d8cf0;
}
.step-text {
color: #2d8cf0;
font-weight: 500;
}
}
}
}
.step-content {
padding: 0 20px;
}
.form-group {
margin-bottom: 20px;
.form-label {
display: block;
margin-bottom: 8px;
color: #515a6e;
font-weight: 500;
}
.form-input {
width: 100%;
}
.form-button {
width: 100%;
}
}
.expense-table {
width: 100%;
border-collapse: collapse;
margin-top: 8px;
th, td {
padding: 8px;
border: 1px solid #e8eaec;
text-align: center;
}
th {
background: #f8f8f9;
color: #515a6e;
font-weight: 500;
}
td {
.ivu-input {
border: none;
&:focus {
box-shadow: none;
}
}
}
}
.step-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 32px;
padding: 0 20px;
.action-button {
min-width: 100px;
}
}
}
.template-info {
background-color: #f5f7fa;
border-radius: 4px;
padding: 16px;
margin-bottom: 24px;
.info-title {
font-size: 16px;
font-weight: 600;
color: #1f2937;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #e5e7eb;
}
.info-content {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
.info-item {
display: flex;
align-items: center;
.label {
color: #4b5563;
font-weight: 500;
margin-right: 8px;
min-width: 84px;
}
.value {
color: #1f2937;
}
}
}
}
.form-section {
background-color: #ffffff;
border-radius: 4px;
padding: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.section-title {
font-size: 16px;
font-weight: 600;
color: #1f2937;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #e5e7eb;
}
:deep(.el-form-item) {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
:deep(.el-form-item__label) {
font-weight: 500;
color: #4b5563;
}
:deep(.el-input),
:deep(.el-input-number),
:deep(.el-select) {
width: 100%;
}
}
.helper-text {
margin-top: 8px;
color: #666;
font-size: 13px;
line-height: 1.4;
}
.form-section {
& + .form-section {
margin-top: 24px;
}
:deep(.el-radio-group) {
.el-radio {
margin-right: 30px;
&:last-child {
margin-right: 0;
}
}
}
}
.process-control-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
.control-item {
background: #f9fafb;
border-radius: 4px;
padding: 16px;
.control-label {
color: #4b5563;
font-weight: 500;
margin-bottom: 12px;
}
.control-content {
.helper-text {
margin-top: 8px;
color: #666;
font-size: 13px;
line-height: 1.4;
}
}
:deep(.el-radio-group) {
display: flex;
gap: 24px;
.el-radio {
margin-right: 0;
}
}
}
}
.plan-selector {
width: 100%;
.plan-input {
width: 100%;
}
.plan-tags {
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 8px;
.el-tag {
margin-right: 0;
}
}
}
.search-box {
margin-bottom: 16px;
}
.plan-list {
height: 400px;
overflow-y: auto;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
padding: 10px 0;
.action-button {
min-width: 100px;
}
}
.no-payment-form {
padding: 20px;
text-align: center;
color: #909399;
font-size: 14px;
background-color: #f5f7fa;
border-radius: 4px;
}
.money-way-selector {
width: 100%;
.money-way-tags {
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 8px;
.el-tag {
margin-right: 0;
}
}
}
.department-selector {
width: 100%;
.department-tags {
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 8px;
.el-tag {
margin-right: 0;
}
}
}
.skip-option {
margin-top: 16px;
text-align: center;
.el-button {
color: #909399;
&:hover {
color: #409EFF;
}
}
}
// 添加分类按钮样式
.category-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
padding: 10px 0;
.category-button {
min-width: 180px;
height: 48px;
font-size: 16px;
font-weight: 500;
border-radius: 8px;
transition: all 0.3s ease;
background: #2d8cf0;
border-color: #2d8cf0;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
background: #2b85e4;
border-color: #2b85e4;
}
&:active {
transform: translateY(0);
box-shadow: none;
}
}
}
</style>
<style scoped lang="scss">
// ... existing styles ...
// 加宽 require 提示框
::v-deep .el-form-item__error {
width: 200px;
white-space: normal;
word-break: break-all;
text-align: right;
right: 0;
left: auto;
}
.payment-preview-modal {
z-index: 4000 !important;
}
.search-controls {
display: flex;
align-items: center;
margin-bottom: 15px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 6px;
}
// 资金列支渠道树形选择组件样式
.money-way-tree-container {
::v-deep .el-tree {
.el-tree-node__content {
height: 32px;
line-height: 32px;
&:hover {
background-color: #f5f7fa;
}
}
.el-tree-node__label {
font-size: 14px;
}
.el-checkbox {
margin-right: 8px;
}
}
}
.selected-money-way {
.el-tag {
background-color: #e1f3d8;
border-color: #67c23a;
color: #67c23a;
&:hover {
background-color: #f0f9ff;
border-color: #409eff;
color: #409eff;
}
}
}
</style>