lion 2 months ago
parent d0c864913f
commit 85a020244c

@ -33,6 +33,10 @@ export default {
},
okText:{
type:String
},
loading:{
type:Boolean,
default:false
}
},
data() {
@ -45,16 +49,16 @@ export default {
if(this.type === 'form'){
return (
<div>
<Button ghost type="primary" on-click={this.reset}>重置</Button>
<Button type="primary" on-click={this.submit}>{this.okText || '确定'}</Button>
<Button ghost type="primary" disabled={this.loading} on-click={this.reset}>重置</Button>
<Button type="primary" loading={this.loading} disabled={this.loading} on-click={this.submit}>{this.okText || '确定'}</Button>
</div>
)
}
if(this.type === 'normal'){
return (
<div>
<Button ghost type="primary" on-click={()=>{this.$emit('update:isShow',false)}}>取消</Button>
<Button type="primary" on-click={()=>{this.$emit('on-ok')}}>{this.okText || '确定'}</Button>
<Button ghost type="primary" disabled={this.loading} on-click={()=>{this.$emit('update:isShow',false)}}>取消</Button>
<Button type="primary" loading={this.loading} disabled={this.loading} on-click={()=>{this.$emit('on-ok')}}>{this.okText || '确定'}</Button>
</div>
)
}
@ -90,6 +94,9 @@ export default {
if(this.type === 'normal'){
return
}
if(this.loading){
return
}
this.$refs['elForm'].validate().then(res=>{
if(res)this.$emit('submit')
}).catch(err=>{
@ -149,6 +156,16 @@ export default {
}
}
return (
<div
class={this.loading ? 'xy-dialog-form-loading' : ''}
{...{
directives: [{
name: 'loading',
value: this.loading
}]
}}
element-loading-text="加载中..."
>
<el-form
class="xy-dialog-form"
style={title.length === 0 ? {'margin-top':'32px'} : {}}
@ -178,6 +195,7 @@ export default {
{$scopedSlots.extraFormBottom ? $scopedSlots.extraFormBottom() : ''}
</el-form>
</div>
)
}else{
return (
@ -226,6 +244,10 @@ export default {
overflow: scroll;
}
.xy-dialog-form-loading {
position: relative;
min-height: 260px;
}
.xy-table-item{
display: flex;
align-items: center;

@ -6,6 +6,7 @@
:is-show.sync="isShowPaymentRegistration"
type="form"
class="payment-registration"
:loading="detailLoading"
:form="paymentRegistrationForm"
:rules="paymentRegistrationRules"
@submit="submit"
@ -167,6 +168,8 @@
filterable
allow-create
clearable
popper-append-to-body
:popper-options="{ boundariesElement: 'body', gpuAcceleration: false }"
>
<el-option
v-for="item in paymentType"
@ -315,6 +318,7 @@ export default {
],
paymentType: ["首付款","进度款", "结算款", "质保金"],
isShowPaymentRegistration: false,
detailLoading: false,
paymentRegistrationForm: {
applyMoney: "",
//deductionMoney: "",
@ -448,35 +452,51 @@ export default {
//
async getContract(info) {
this.contract = await detailContract({
id: info.id,
});
this.detailLoading = true
this.contract = {}
this.payment = []
try {
this.contract = await detailContract({
id: info.id,
})
this.paymentRegistrationForm.plan = this.contract.plans.map((item) => {
return {
plan_id: item.id,
use_money: item.useMoney,
new_money: item.money,
};
});
this.paymentRegistrationForm.plan = (this.contract.plans || []).map((item) => {
return {
plan_id: item.id,
use_money: item.useMoney,
new_money: item.money,
}
})
this.form.audit_money = this.contract.audit_money;
this.form.audit_money = this.contract.audit_money
const res = await getFundLog({
contract_id: this.contract.id,
});
const res = await getFundLog({
contract_id: this.contract.id,
})
this.payment = res.data;
this.fileList = res.files_detail?.map(i => {
return {
url: i?.url,
name: i?.original_name,
response: i
}
})
this.payment = res.data || []
this.fileList = res.files_detail?.map(i => {
return {
url: i?.url,
name: i?.original_name,
response: i
}
}) || []
} catch (error) {
this.isShowPaymentRegistration = false
Message({
type: 'error',
message: '加载合同信息失败,请重试',
})
} finally {
this.detailLoading = false
}
},
submit() {
if (this.detailLoading || !this.contract?.id) {
return
}
let data = {
contract_id: this.contract.id,
apply_money: this.paymentRegistrationForm.applyMoney,
@ -598,7 +618,11 @@ export default {
watch: {
isShowPaymentRegistration(newVal) {
if (newVal) {
this.getBudgets();
this.getBudgets()
} else {
this.detailLoading = false
this.contract = {}
this.payment = []
}
},
},

@ -6,18 +6,43 @@
<el-button type="primary" icon="el-icon-plus" size="small" @click="addSection"></el-button>
</div>
<div v-if="existingSections.length" class="existing-sections">
<div class="existing-sections-title">已拆分标段不可修改</div>
<div
v-for="(item, index) in existingSections"
:key="'exist-' + (item.id || index)"
class="section-block section-block--readonly"
>
<Tag color="default">已拆分 {{ index + 1 }}</Tag>
<el-form label-width="100px" style="margin-top: 8px;">
<el-form-item label="标段名称">
<span>{{ item.section_name }}</span>
</el-form-item>
<el-form-item label="标段金额">
<span>{{ formatMoney(item.plan_price) }}</span>
</el-form-item>
<el-form-item label="占用预算金额" v-if="item.plan_links && item.plan_links.length">
<div v-for="(link, li) in item.plan_links" :key="li" class="plan-link-row">
<span class="plan-link-label">{{ formatPlanLinkLabel(link) }}</span>
<span class="plan-link-money">{{ formatMoney(link.use_money) }}</span>
</div>
</el-form-item>
</el-form>
</div>
</div>
<div v-if="isContinueMode && existingSections.length" class="new-sections-title"></div>
<el-form :model="form" ref="dynamicValidateForm" label-width="100px">
<div v-for="(item, index) in form.sections" :key="index" style="border: 1px solid #ebeef5;padding: 12px;margin-bottom: 12px;border-radius: 4px;">
<div
v-for="(item, index) in form.sections"
:key="'new-' + index"
class="section-block"
>
<div style="display: flex;justify-content: space-between;margin-bottom: 8px;">
<Tag color="primary">标段 {{ index + 1 }}</Tag>
<Tag color="primary">标段 {{ existingSections.length + index + 1 }}</Tag>
<el-button size="mini" type="danger" @click.prevent="removeSection(index)">删除</el-button>
</div>
<el-form-item
label="标段编号"
:prop="'sections.' + index + '.section_no'"
>
<el-input v-model="item.section_no" placeholder="如:二标段"></el-input>
</el-form-item>
<el-form-item
label="标段名称"
:prop="'sections.' + index + '.section_name'"
@ -26,23 +51,31 @@
<el-input v-model="item.section_name"></el-input>
</el-form-item>
<el-form-item
label="计划金额"
label="标段金额"
:prop="'sections.' + index + '.plan_price'"
:rules="{ required: true, message: '计划金额不能为空', trigger: 'blur' }"
:rules="{ required: true, message: '标段金额不能为空', trigger: 'blur' }"
>
<el-input-number :min="0" :precision="2" :controls="false" v-model="item.plan_price" style="width: 100%;" @change="syncPlanLinkMoney(item)"></el-input-number>
</el-form-item>
<el-form-item label="供应商">
<el-input v-model="item.supply" placeholder="可选,各标段可不同"></el-input>
<el-input-number
:min="0"
:precision="2"
:controls="false"
v-model="item.plan_price"
style="width: 100%;"
@change="syncPlanLinkMoney(item)"
></el-input-number>
</el-form-item>
<el-form-item label="预算分摊" v-if="sourcePlans.length">
<div v-for="(link, li) in item.plan_links" :key="li" style="display:flex;gap:8px;margin-bottom:6px;">
<el-select v-model="link.plan_id" placeholder="预算计划" style="flex:1;">
<el-form-item label="占用预算金额" v-if="sourcePlans.length">
<div v-for="(link, li) in item.plan_links" :key="li" class="plan-link-row">
<el-select
v-model="link.plan_id"
placeholder="预算计划"
style="flex:1;"
disabled
>
<el-option v-for="p in sourcePlans" :key="p.plan_id" :label="p.label" :value="p.plan_id"></el-option>
</el-select>
<el-input-number :min="0" :precision="2" :controls="false" v-model="link.use_money" style="width: 140px;"></el-input-number>
<span class="plan-link-money readonly-money">{{ formatMoney(link.use_money) }}</span>
</div>
<el-button v-if="sourcePlans.length > 1" type="text" @click="addPlanLink(item)"></el-button>
</el-form-item>
</div>
</el-form>
@ -51,14 +84,12 @@
</template>
<script>
import { splitSections, detailContract } from "@/api/contract/contract";
import { splitSections, detailContract, getSections } from "@/api/contract/contract";
function emptySection() {
return {
section_no: '',
section_name: '',
plan_price: undefined,
supply: '',
plan_links: []
};
}
@ -70,6 +101,8 @@ export default {
isShow: false,
row: {},
sourcePlans: [],
existingSections: [],
isContinueMode: false,
form: {
sections: [emptySection()]
}
@ -77,7 +110,7 @@ export default {
},
computed: {
modalTitle() {
return this.row.split_status ? '继续拆分标段' : '合同拆分';
return this.isContinueMode ? '继续采购项目拆分' : '采购项目拆分';
}
},
methods: {
@ -90,6 +123,14 @@ export default {
hide() {
this.isShow = false;
},
formatMoney(value) {
if (value == null || value === '') return '-';
return Number(value).toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
},
formatPlanLinkLabel(link) {
const plan = link.plan || {};
return this.formatPlanLabel(plan) || `计划#${link.plan_id}`;
},
addSection() {
const sec = emptySection();
this.initPlanLinks(sec);
@ -102,9 +143,6 @@ export default {
}
this.form.sections.splice(index, 1);
},
addPlanLink(item) {
item.plan_links.push({ plan_id: this.sourcePlans[0]?.plan_id, use_money: 0 });
},
syncPlanLinkMoney(item) {
if (item.plan_links.length === 1 && item.plan_price != null) {
item.plan_links[0].use_money = item.plan_price;
@ -148,6 +186,17 @@ export default {
async getDetail() {
this.row = await detailContract({ id: this.id });
this.sourcePlans = this.buildSourcePlans(this.row);
this.isContinueMode = !!(this.row.split_status || this.row.section_count > 0);
this.existingSections = [];
if (this.isContinueMode) {
const res = await getSections({ contract_id: this.id });
this.existingSections = (res.children || []).map(child => ({
id: child.id,
section_name: child.section_name,
plan_price: child.plan_price,
plan_links: child.plan_link || child.planLink || []
}));
}
this.form.sections = [emptySection()];
this.initPlanLinks(this.form.sections[0]);
},
@ -159,7 +208,7 @@ export default {
if (sec.plan_links && sec.plan_links.length) {
const sum = sec.plan_links.reduce((t, l) => t + Number(l.use_money || 0), 0);
if (Math.abs(sum - Number(sec.plan_price || 0)) > 0.009) {
this.$message.error(`标段「${sec.section_name}预算分摊须等于计划金额`);
this.$message.error(`标段「${sec.section_name}占用预算金额须等于标段金额`);
return;
}
}
@ -168,14 +217,12 @@ export default {
splitSections({
contract_id: this.id,
sections: this.form.sections.map(sec => ({
section_no: sec.section_no,
section_name: sec.section_name,
plan_price: sec.plan_price,
supply: sec.supply,
plan_links: (sec.plan_links || []).filter(l => l.plan_id && l.use_money != null)
}))
}).then(() => {
this.$message.success("拆分成功");
this.$message.success(this.isContinueMode ? '追加标段成功' : '拆分成功');
this.hide();
this.$emit('refresh');
});
@ -188,7 +235,9 @@ export default {
this.getDetail();
} else {
this.form.sections = [emptySection()];
this.existingSections = [];
this.sourcePlans = [];
this.isContinueMode = false;
}
},
id() {
@ -200,4 +249,39 @@ export default {
};
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.existing-sections-title,
.new-sections-title {
font-weight: 600;
color: #303133;
margin-bottom: 8px;
}
.new-sections-title {
margin-top: 12px;
}
.section-block {
border: 1px solid #ebeef5;
padding: 12px;
margin-bottom: 12px;
border-radius: 4px;
}
.section-block--readonly {
background: #fafafa;
}
.plan-link-row {
display: flex;
gap: 8px;
margin-bottom: 6px;
align-items: center;
}
.plan-link-label {
flex: 1;
color: #606266;
}
.plan-link-money,
.readonly-money {
width: 140px;
text-align: right;
color: #606266;
}
</style>

@ -378,7 +378,7 @@
>招标审查</Button
>
</template>
<template v-if="scope.row.FLOWSTATUS['shenpi'].isEnabled()">
<template v-if="scope.row.FLOWSTATUS['shenpi'].isEnabled() && shouldShowContractApproval(scope.row)">
<Button
class="slot-btns-item"
size="small"
@ -387,16 +387,7 @@
>合同审批
</Button>
</template>
<template v-if="scope.row.can_split && scope.row.is_contract && hasSplitSections(scope.row)">
<Button
class="slot-btns-item"
size="small"
type="warning"
@click="openSplit(scope.row)"
>继续拆分
</Button>
</template>
<template v-if="scope.row.is_split_child && scope.row.can_delete">
<template v-if="scope.row.is_split_child && scope.row.join_status === 1">
<Button
class="slot-btns-item"
size="small"
@ -600,6 +591,13 @@
>
查看
</Button>
<Button
v-if="scope.row.can_split && scope.row.is_contract && hasSplitSections(scope.row) && !scope.row.is_split_child"
class="slot-btns-item"
size="small"
type="primary"
@click="openSplit(scope.row)"
>继续拆分</Button>
<template v-if="scope.row.status != 2 || hasEdit">
<Button
class="slot-btns-item"
@ -1418,6 +1416,9 @@ export default {
purchaseWay: [], //
moneyWay: [], //
departments: [], //
rawListData: [],
splitGroupExpanded: {},
splitChildrenRowsCache: {},
list: [], //
total: 0,
//
@ -1605,15 +1606,7 @@ export default {
fixed: "left",
align: "left",
customFn:row => {
const prefix = row.is_split_child ? ' └ ' : ''
const displayName = row.section_name ? `${prefix}${row.section_no ? row.section_no + ' ' : ''}${row.section_name}` : `${prefix}${row.name}`
if (row.type === 1 && row.end_date && (this.$moment().valueOf() - this.$moment(row.end_date).valueOf()) > 0) {
return (<span style="color: red;"><span>已到期</span><span>{displayName}</span></span>)
} else {
return (
<span>{displayName}</span>
)
}
return this.renderProjectNameCell(row)
}
},
{
@ -2388,16 +2381,21 @@ export default {
this.signProcess(row)
return
}
this.$confirm('是否为多标段合同?','提示',{
confirmButtonText: '合同拆分',
cancelButtonText: '合同审批',
distinguishCancelAndClose: true,
this.$msgbox({
title: '提示',
message: '是否为多标段合同?',
showCancelButton: true,
showClose: true,
closeOnClickModal: false,
distinguishCancelAndClose: true,
confirmButtonText: '采购项目拆分',
cancelButtonText: '合同审批',
confirmButtonClass: 'el-button--primary',
cancelButtonClass: 'el-button--primary',
type: 'warning'
}).then(_ => {
}).then(() => {
this.openSplit(row)
}).catch(action => {
// OA X / ESC
if (action === 'cancel') {
this.signProcess(row)
}
@ -2415,6 +2413,95 @@ export default {
const children = row.split_children || row.splitChildren || []
return children.length > 0
},
/** 源项目已拆分标段时隐藏合同审批;标段删光后恢复 */
shouldShowContractApproval(row) {
if (row.is_split_child) {
return true
}
return !this.hasSplitSections(row)
},
isSplitGroupExpanded(sourceId) {
if (this.splitGroupExpanded[sourceId] === undefined) {
return true
}
return !!this.splitGroupExpanded[sourceId]
},
toggleSplitGroup(sourceId) {
const next = !this.isSplitGroupExpanded(sourceId)
this.$set(this.splitGroupExpanded, sourceId, next)
const sourceIndex = this.list.findIndex(r => r.id === sourceId && r._hasSplitChildren)
if (sourceIndex === -1) {
this.list = this.rebuildDisplayList(this.rawListData)
return
}
if (next) {
const source = this.rawListData.find(r => r.id === sourceId)
if (!source) return
const childRows = this.getSplitChildRows(source)
this.list.splice(sourceIndex + 1, 0, ...childRows)
return
}
let removeCount = 0
for (let i = sourceIndex + 1; i < this.list.length; i++) {
if (this.list[i]._splitParentId === sourceId) {
removeCount++
} else {
break
}
}
if (removeCount > 0) {
this.list.splice(sourceIndex + 1, removeCount)
}
},
formatSectionLabel(index) {
const n = Number(index) + 1
const cn = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
if (n >= 1 && n <= 10) {
return `标段${cn[n - 1]}`
}
return `标段${n}`
},
renderProjectNameCell(row) {
const displayName = row.is_split_child
? (row.section_name || row.name)
: row.name
const expired = row.type === 1 && row.end_date &&
(this.$moment().valueOf() - this.$moment(row.end_date).valueOf()) > 0
if (row._hasSplitChildren) {
const expanded = this.isSplitGroupExpanded(row.id)
const childCount = (row.split_children || row.splitChildren || []).length
return (
<span>
<a
style="cursor:pointer;margin-right:8px;color:#409EFF;font-weight:600;"
onClick={(e) => { e.stopPropagation(); this.toggleSplitGroup(row.id) }}
>{expanded ? '▼' : '▶'}</a>
<Tag color="success" size="small" style="margin-right:6px;">采购项目</Tag>
<span style={expired ? 'color:red;' : ''}>{expired ? '【已到期】' : ''}{displayName}</span>
<span style="color:#909399;margin-left:8px;">({childCount}个标段)</span>
</span>
)
}
if (row.is_split_child) {
const sectionLabel = this.formatSectionLabel(row._splitSectionIndex ?? 0)
return (
<span style="display:inline-flex;align-items:center;padding-left:12px;">
<Tag color="primary" size="small" style="margin-right:8px;">{sectionLabel}</Tag>
<span style={expired ? 'color:red;' : ''}>{expired ? '【已到期】' : ''}{displayName}</span>
</span>
)
}
if (expired) {
return (<span style="color: red;"><span>已到期</span><span>{displayName}</span></span>)
}
return (<span>{displayName}</span>)
},
destroySplitSection(row) {
this.$confirm('确认删除该标段?删除后不可恢复。', '提示', { type: 'warning' }).then(() => {
destroySection({ id: row.id }).then(() => {
@ -2426,11 +2513,43 @@ export default {
buildDisplayList(data) {
const rows = []
;(data || []).forEach(source => {
rows.push(source)
const children = source.split_children || source.splitChildren || []
children.forEach(child => {
rows.push(child)
})
const hasChildren = children.length > 0
rows.push({ ...source, _hasSplitChildren: hasChildren })
if (hasChildren && this.isSplitGroupExpanded(source.id)) {
children.forEach((child, index) => {
rows.push({ ...child, _splitParentId: source.id, _splitSectionIndex: index })
})
}
})
return rows
},
getSplitChildRows(source) {
const sourceId = source.id
if (this.splitChildrenRowsCache[sourceId]) {
return this.splitChildrenRowsCache[sourceId]
}
const children = source.split_children || source.splitChildren || []
const rows = children.map((child, index) => {
const row = { ...child, _splitParentId: sourceId, _splitSectionIndex: index }
this.handleContractFlow(row)
return row
})
this.$set(this.splitChildrenRowsCache, sourceId, rows)
return rows
},
rebuildDisplayList(data) {
this.splitChildrenRowsCache = {}
const rows = []
;(data || []).forEach(source => {
const children = source.split_children || source.splitChildren || []
const hasChildren = children.length > 0
const sourceRow = { ...source, _hasSplitChildren: hasChildren }
this.handleContractFlow(sourceRow)
rows.push(sourceRow)
if (hasChildren && this.isSplitGroupExpanded(source.id)) {
rows.push(...this.getSplitChildRows(source))
}
})
return rows
},
@ -2626,9 +2745,8 @@ export default {
is_auth: 1,
},noloading);
const displayList = this.buildDisplayList(res.list.data)
displayList.forEach(i => this.handleContractFlow(i))
this.list = displayList;
this.rawListData = res.list.data
this.list = this.rebuildDisplayList(res.list.data)
this.total = res.list.total;
this.tableTotal.fundLogTotal = res.fund_log_total;
this.tableTotal.moneyTotal = Number(res.money_total)
@ -2924,9 +3042,10 @@ export default {
if (item.is_contract) {
//
flowStatus["shenpi"].setStatus(item.join_status)
if (item.join_status === 1 && (item.invite_status === 3 ||
(item.purchase_way?.remark === 'false' && item.purchase_status === 3))) {
//
const canStartJoin = item.join_status === 1 && (item.invite_status === 3 ||
(item.purchase_way?.remark === 'false' && item.purchase_status === 3))
if (canStartJoin && !(item.is_split_child === 0 && this.hasSplitSections(item))) {
//
flowStatus["shenpi"].setExecutable(true)
}
}

Loading…
Cancel
Save