linyongLynn 5 months ago
commit 5d0a39b761

@ -4,6 +4,6 @@ ENV = 'production'
# base api # base api
VUE_APP_DOMIAN='' VUE_APP_DOMIAN=''
VUE_APP_BASE_API = '' VUE_APP_BASE_API = ''
#VUE_APP_OUT_URL = http://192.168.60.18:2021 VUE_APP_OUT_URL = http://192.168.60.18:2021
VUE_APP_OUT_URL = http://192.168.60.18:8001 #VUE_APP_OUT_URL = http://192.168.60.18:8001
VUE_APP_UPLOAD=http://192.168.60.99:9003/api/admin/upload-file VUE_APP_UPLOAD=http://192.168.60.99:9003/api/admin/upload-file

@ -186,6 +186,7 @@ export default {
header-align={item.headerAlign ?? 'center'} header-align={item.headerAlign ?? 'center'}
label={item.label} label={item.label}
prop={item.prop} prop={item.prop}
show-overflow-tooltip={true}
scopedSlots={{ scopedSlots={{
default(scope) { default(scope) {
return item.customFn(scope.row, scope) return item.customFn(scope.row, scope)
@ -210,6 +211,7 @@ export default {
width={item1.width ?? 'auto'} width={item1.width ?? 'auto'}
min-width={item1.minWidth ?? item1.width} min-width={item1.minWidth ?? item1.width}
sortable={item1.sortable ?? true} sortable={item1.sortable ?? true}
show-overflow-tooltip={true}
scopedSlots={{ scopedSlots={{
default(scope) { default(scope) {
return item1.customFn(scope.row, scope) return item1.customFn(scope.row, scope)
@ -228,6 +230,7 @@ export default {
width={item1.width ?? 'auto'} width={item1.width ?? 'auto'}
min-width={item1.minWidth ?? item1.width} min-width={item1.minWidth ?? item1.width}
sortable={item1.sortable ?? true} sortable={item1.sortable ?? true}
show-overflow-tooltip={true}
type={item1.type ?? ''} type={item1.type ?? ''}
formatter={item1.formatter}> formatter={item1.formatter}>
</el-table-column> </el-table-column>
@ -249,6 +252,7 @@ export default {
label={item.label} label={item.label}
prop={item.prop} prop={item.prop}
label-class-name={item.labelClassName} label-class-name={item.labelClassName}
show-overflow-tooltip={true}
sortable={item.sortable ?? true} sortable={item.sortable ?? true}
type={item.type} type={item.type}
reserve-selection={item.reserveSelection} reserve-selection={item.reserveSelection}

@ -67,7 +67,7 @@
<div class="xy-table-item-label" style="width: 200px">是否收取履约保函 <div class="xy-table-item-label" style="width: 200px">是否收取履约保函
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-switch style="width: 300px" v-model="form.is_assurance" /> <el-switch active-text="" inactive-text="" style="width: 300px" v-model="form.is_assurance" />
</div> </div>
</div> </div>
</template> </template>
@ -141,7 +141,7 @@
<template v-if="contract.is_contract===1" v-slot:other_tender_name> <template v-if="contract.is_contract===1" v-slot:other_tender_name>
<div class="xy-table-item"> <div class="xy-table-item">
<div class="xy-table-item-label"> <div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>其他投标单位 <span style="color: red;font-weight: 600;padding-right: 4px;"></span>其他参与投标单位名称
</div> </div>
<div class="xy-table-item-content "> <div class="xy-table-item-content ">
<el-button type="primary" style="margin-bottom:10px" size="small" @click="addRow"></el-button> <el-button type="primary" style="margin-bottom:10px" size="small" @click="addRow"></el-button>
@ -163,29 +163,45 @@
<template v-if="contract.is_contract===1" v-slot:quotation_situation> <template v-if="contract.is_contract===1" v-slot:quotation_situation>
<div class="xy-table-item"> <div class="xy-table-item">
<div class="xy-table-item-label"> <div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>事前询价情况 <span style="color: red;font-weight: 600;padding-right: 4px;"></span>事前询价情况
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-input v-model="form.quotation_situation" type="textarea" placeholder="第三方造价咨询单位出具的报告或标底/供应商提供的报价" <el-select v-model="form.quotation_situation" placeholder="请选择" style="width:300px">
style="width: 300px;" /> <el-option
v-for="(item,index) in ['第三方造价咨询单位出具的报告或标底','供应商提供的报价']"
:key="index"
:label="item"
:value="item">
</el-option>
</el-select>
<!-- <el-input v-model="form.quotation_situation" type="textarea" placeholder="第三方造价咨询单位出具的报告或标底/供应商提供的报价"
style="width: 300px;" /> -->
</div> </div>
</div> </div>
</template> </template>
<template v-if="contract.is_contract===1" v-slot:audit_situation> <template v-if="contract.is_contract===1" v-slot:audit_situation>
<div class="xy-table-item"> <div class="xy-table-item">
<div class="xy-table-item-label"> <div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>事后审计情况 <span style="color: red;font-weight: 600;padding-right: 4px;"></span>事后审计情况
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-input v-model="form.audit_situation" type="textarea" placeholder="外部财政局或审计局审计/内部自行委托的第三方审计/未审计。" <el-select v-model="form.audit_situation" placeholder="请选择" style="width:300px">
style="width: 300px;" /> <el-option
v-for="(item,index) in ['外部财政局或审计局审计','内部自行委托的第三方审计','未审计']"
:key="index"
:label="item"
:value="item">
</el-option>
</el-select>
<!-- <el-input v-model="form.audit_situation" type="textarea" placeholder="外部财政局或审计局审计/内部自行委托的第三方审计/未审计。"
style="width: 300px;" /> -->
</div> </div>
</div> </div>
</template> </template>
<template v-if="contract.is_contract===1" v-slot:payment_content> <template v-if="contract.is_contract===1" v-slot:payment_content>
<div class="xy-table-item"> <div class="xy-table-item">
<div class="xy-table-item-label"> <div class="xy-table-item-label">
<span style="color: red;font-weight: 600;padding-right: 4px;">*</span>合同内的支付条款 <span style="color: red;font-weight: 600;padding-right: 4px;"></span>合同内的支付条款
</div> </div>
<div class="xy-table-item-content"> <div class="xy-table-item-content">
<el-input v-model="form.payment_content" type="textarea" placeholder="合同内的支付条款" <el-input v-model="form.payment_content" type="textarea" placeholder="合同内的支付条款"
@ -620,31 +636,31 @@
} }
console.log("other",this.form.other_tender_name) console.log("other",this.form.other_tender_name)
if(this.contract.is_contract===1){ if(this.contract.is_contract===1){
if(this.form.other_tender_name.length<1){ // if(this.form.other_tender_name.length<1){
this.$Message.warning(`请添加其他投标单位`) // this.$Message.warning(``)
return // return
} // }
if(!this.form.quotation_situation){ // if(!this.form.quotation_situation){
this.$Message.warning(`事前询价情况不能为空`) // this.$Message.warning(``)
return // return
} // }
if(!this.form.audit_situation){ // if(!this.form.audit_situation){
this.$Message.warning(`事后审计情况不能为空`) // this.$Message.warning(``)
return // return
} // }
if(!this.form.payment_content){ // if(!this.form.payment_content){
this.$Message.warning(`合同内的支付条款不能为空`) // this.$Message.warning(``)
return // return
} // }
if (this.form.other_tender_name.length > 0) { // if (this.form.other_tender_name.length > 0) {
// // //
for (var p of this.form.other_tender_name) { // for (var p of this.form.other_tender_name) {
if (!p) { // if (!p) {
this.$Message.warning(`投标单位名称不能为空,请补充或删除`) // this.$Message.warning(``)
return // return
} // }
} // }
} // }
} }
if(this.contract.is_end===1){ if(this.contract.is_end===1){

@ -70,7 +70,7 @@
</div> </div>
<div v-if='detail.is_contract===1'> <div v-if='detail.is_contract===1'>
<div class="sign-info-item"> <div class="sign-info-item">
<div class="sign-info-item-title">其他投标单位</div> <div class="sign-info-item-title">其他参与投标单位名称</div>
<div class="sign-info-item-content"> <div class="sign-info-item-content">
<div v-for="item in detail.other_tender_name">{{item}}</div> <div v-for="item in detail.other_tender_name">{{item}}</div>
</div> </div>

@ -47,8 +47,8 @@
<div class="payment-registration-row-title">已付笔数</div> <div class="payment-registration-row-title">已付笔数</div>
<div class="payment-registration-row-content">{{ actNumsTotal() }}</div> <div class="payment-registration-row-content">{{ actNumsTotal() }}</div>
<div class="payment-registration-row-content" style="color: #ff0000;padding-left: 16px;cursor: pointer;"> <div class="payment-registration-row-content" style="color: #ff0000;padding-left: 16px;cursor: pointer;">
<Poptip <Poptip
:transfer="true" :transfer="true"
:close-on-click-outside="false" :close-on-click-outside="false"
:close-on-press-escape="false" :close-on-press-escape="false"
:close-on-close="false" :close-on-close="false"
@ -140,7 +140,7 @@
</div> </div>
</template> </template>
<template v-slot:act_date> <!-- <template v-slot:act_date>
<div v-if="contract_category && contract_category.show_apply_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item"> <div v-if="contract_category && contract_category.show_apply_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
<div class="xy-table-item-label"> <div class="xy-table-item-label">
<span v-if="contract_category && contract_category.required_act_date === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span> <span v-if="contract_category && contract_category.required_act_date === 1" style="color: red;font-weight: 600;padding-right: 4px;">*</span>
@ -150,7 +150,7 @@
<el-date-picker v-model="paymentRegistrationForm.act_date" type="date" value-format="yyyy-MM-dd" style="width: 150px;" /> <el-date-picker v-model="paymentRegistrationForm.act_date" type="date" value-format="yyyy-MM-dd" style="width: 150px;" />
</div> </div>
</div> </div>
</template> </template> -->
<template v-slot:applyMoney> <template v-slot:applyMoney>
<div v-if="contract_category && contract_category.show_apply_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item"> <div v-if="contract_category && contract_category.show_apply_money === 1 && (!hasPostPaymentForm || (hasPostPaymentForm && currentStep === 2))" class="xy-table-item">
@ -459,7 +459,7 @@ function numberToChinese(num) {
if (num === 0) { if (num === 0) {
return '零元' return '零元'
} }
const units = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟', '万'] const units = ['', '拾', '佰', '仟', '万', '拾', '佰', '仟', '亿', '拾', '佰', '仟', '万']
const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
const [integer, decimal] = num.toString().split('.') const [integer, decimal] = num.toString().split('.')
@ -505,7 +505,7 @@ function numberToChinese(num) {
} }
} }
} }
if (!decimal || parseInt(decimal) === 0) { if (!decimal || parseInt(decimal) === 0) {
result += '整' result += '整'
} }
@ -986,12 +986,12 @@ export default {
message: '必填' message: '必填'
} }
], ],
act_date: [ // act_date: [
{ // {
required: this.contract_category.required_act_date === 1, // required: this.contract_category.required_act_date === 1,
message: '必填' // message: ''
} // }
] // ]
} }
const res = await getFundLog({ const res = await getFundLog({
contract_id: this.contract.id, contract_id: this.contract.id,
@ -1466,7 +1466,7 @@ export default {
input.addEventListener('change', this.calculateTotal) input.addEventListener('change', this.calculateTotal)
input.addEventListener('blur', this.calculateTotal) input.addEventListener('blur', this.calculateTotal)
}) })
// fen // fen
const fenAmountInputs = dom.querySelectorAll('input[data-field^="fen"]') const fenAmountInputs = dom.querySelectorAll('input[data-field^="fen"]')
// //
@ -1506,7 +1506,7 @@ export default {
totalInput.removeEventListener('input', (e) => this.updateUpperCaseFromTotal(e, dom)) totalInput.removeEventListener('input', (e) => this.updateUpperCaseFromTotal(e, dom))
totalInput.removeEventListener('change', (e) => this.updateUpperCaseFromTotal(e, dom)) totalInput.removeEventListener('change', (e) => this.updateUpperCaseFromTotal(e, dom))
totalInput.removeEventListener('blur', (e) => this.updateUpperCaseFromTotal(e, dom)) totalInput.removeEventListener('blur', (e) => this.updateUpperCaseFromTotal(e, dom))
totalInput.addEventListener('input', (e) => this.updateUpperCaseFromTotal(e, dom)) totalInput.addEventListener('input', (e) => this.updateUpperCaseFromTotal(e, dom))
totalInput.addEventListener('change', (e) => this.updateUpperCaseFromTotal(e, dom)) totalInput.addEventListener('change', (e) => this.updateUpperCaseFromTotal(e, dom))
totalInput.addEventListener('blur', (e) => this.updateUpperCaseFromTotal(e, dom)) totalInput.addEventListener('blur', (e) => this.updateUpperCaseFromTotal(e, dom))
@ -1568,7 +1568,7 @@ export default {
const value = parseFloat(input.value) || 0 const value = parseFloat(input.value) || 0
wanTotal += value wanTotal += value
}) })
const wanInput = dom.querySelector('input[data-field="wTotal"]') const wanInput = dom.querySelector('input[data-field="wTotal"]')
if (wanInput) { if (wanInput) {
wanInput.value = wanTotal===0?'0':wanTotal wanInput.value = wanTotal===0?'0':wanTotal
@ -1747,7 +1747,7 @@ export default {
const totalInput = dom.querySelector('input[data-field="total"]') const totalInput = dom.querySelector('input[data-field="total"]')
const upperCaseInput = dom.querySelector('input[data-field="upperCaseAmount"]') const upperCaseInput = dom.querySelector('input[data-field="upperCaseAmount"]')
if (totalInput && upperCaseInput) { if (totalInput && upperCaseInput) {
this.total = parseFloat(totalInput.value.replace(/¥/g, '')) || 0 this.total = parseFloat(totalInput.value.replace(/¥/g, '')) || 0
upperCaseInput.value = numberToChinese(this.total) upperCaseInput.value = numberToChinese(this.total)
@ -2108,7 +2108,7 @@ export default {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-height: 80vh; max-height: 80vh;
::v-deep .el-dialog__body { ::v-deep .el-dialog__body {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;

@ -686,6 +686,8 @@
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
<el-date-picker size="small" type="year" placeholder="请选择年份" v-model="planSearch.year"
format="yyyy" value-format="yyyy"></el-date-picker>
<Input <Input
v-model="planSearch.name" v-model="planSearch.name"
enter-button="搜 索" enter-button="搜 索"
@ -1535,7 +1537,8 @@ export default {
plans: [], // plans: [], //
planSearch: { planSearch: {
name: '', name: '',
plan_department_id: [] // plan_department_id: [], //
year: (new Date().getFullYear() )+ ''
}, },
planTotal: 0, planTotal: 0,
plansPageIndex: 1, plansPageIndex: 1,
@ -1937,7 +1940,9 @@ export default {
page_size: 10, page_size: 10,
page: this.plansPageIndex, page: this.plansPageIndex,
plan_department_id: this.planSearch.plan_department_id, plan_department_id: this.planSearch.plan_department_id,
top_pid: 1 top_pid: 1,
year:this.planSearch.year
}) })
this.plans = res.list.data this.plans = res.list.data

@ -4,32 +4,6 @@
<div slot="content" /> <div slot="content" />
<slot> <slot>
<div class="selects"> <div class="selects">
<div>
<span style="padding: 0 6px;word-break: keep-all;">
合同分类
</span>
<Select v-model="select.contract_category" disabled placeholder="请选择合同分类" style="width:140px;" @on-change="handleCategoryChange">
<Option v-for="item in categoryOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div>
<span style="padding: 0 6px;word-break: keep-all;">
事务类型
</span>
<Select v-model="select.work_type" multiple clearable placeholder="请选择事务类型" style="width:180px;" :max-tag-count="1" @on-change="handleTransactionTypeChange">
<Option v-for="item in transactionTypes" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div>
<span style="padding: 0 6px;word-break: keep-all;">
合同类型
</span>
<Select v-model="select.contract_type" multiple clearable placeholder="请选择合同类型" style="width:160px;" :max-tag-count="1" @on-change="handleContractTypeChange">
<Option v-for="item in contractTypes" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div> <div>
<span style="padding: 0 6px;word-break: keep-all;">关键字</span> <span style="padding: 0 6px;word-break: keep-all;">关键字</span>
@ -69,6 +43,51 @@
@on-change="(e)=>select.year = e" @on-change="(e)=>select.year = e"
/> />
</span> </span>
</div>
<div v-if="type!=1">
<span style="padding: 0 6px;word-break: keep-all;">
预算金额
</span>
<InputNumber v-model="select.start_plan_price" :min="0" placeholder="最小金额" style="width: 100px;" />
<span style="padding: 0 5px;">-</span>
<InputNumber v-model="select.end_plan_price" :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.contract_category" disabled placeholder="请选择合同分类" style="width:140px;" @on-change="handleCategoryChange">
<Option v-for="item in categoryOptions" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div>
<span style="padding: 0 6px;word-break: keep-all;">
事务类型
</span>
<Select v-model="select.work_type" multiple clearable placeholder="请选择事务类型" style="width:180px;" :max-tag-count="1" @on-change="handleTransactionTypeChange">
<Option v-for="item in transactionTypes" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div>
<div>
<span style="padding: 0 6px;word-break: keep-all;">
合同类型
</span>
<Select v-model="select.contract_type" multiple clearable placeholder="请选择合同类型" style="width:160px;" :max-tag-count="1" @on-change="handleContractTypeChange">
<Option v-for="item in contractTypes" :key="item.id" :value="item.id">{{ item.value || item.name }}</Option>
</Select>
</div> </div>
<div v-if="type!=1"> <div v-if="type!=1">
<span style="padding: 0 6px;word-break: keep-all;">创建日期</span> <span style="padding: 0 6px;word-break: keep-all;">创建日期</span>
@ -144,14 +163,6 @@
</Select> </Select>
</div> </div>
<div v-if="type!=1">
<span style="padding: 0 6px;word-break: keep-all;">
预算金额
</span>
<InputNumber v-model="select.start_plan_price" :min="0" placeholder="最小金额" style="width: 100px;" />
<span style="padding: 0 5px;">-</span>
<InputNumber v-model="select.end_plan_price" :min="0" placeholder="最大金额" style="width: 100px;" />
</div>
<div v-if="type!=1"> <div v-if="type!=1">
<span style="padding: 0 6px;word-break: keep-all;"> <span style="padding: 0 6px;word-break: keep-all;">
@ -218,7 +229,8 @@
<Option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }} <Option v-for="item in options" :key="item.value" :value="item.value">{{ item.label }}
</Option> </Option>
</Select> </Select>
</div> </div>
</template>
<Button style="margin-left: 10px" type="primary" @click="searchContracts"></Button> <Button style="margin-left: 10px" type="primary" @click="searchContracts"></Button>
<Button <Button
@ -553,7 +565,8 @@ export default {
} }
} }
return { return {
pageTitle: '合同列表', pageTitle: '合同列表',
showMoreSearch:false,
type: 0, // pageType _0 _1 type: 0, // pageType _0 _1
userList: ['liuxiangyu', 'zhushulan', 'admin', 'jiangjiao'], userList: ['liuxiangyu', 'zhushulan', 'admin', 'jiangjiao'],
window: { window: {
@ -891,7 +904,7 @@ export default {
prop: 'date', prop: 'date',
formatter: (cell, data, value) => { formatter: (cell, data, value) => {
if (value) { if (value) {
return parseTime(new Date(value), '{y}-{m}-{d}') return parseTime(new Date(value), '{y}-{m}-{d}')
} }
} }
}, },

@ -0,0 +1,125 @@
<template>
<div class="bar-chart">
<!-- 图表容器 -->
<div ref="chartDom" class="chart-container"></div>
</div>
</template>
<script>
import echarts from 'echarts'; // npm install echarts@4.9.0
export default {
name: 'BarChart',
props: {
// X['A', 'B']
xAxisData: {
type: Array,
required: true,
default: () => []
},
// Y
seriesData: {
type: Array,
required: true,
//
// [
// { name: '', data: [5000, 8000] },
// { name: '', data: [6000, 7500] },
// { name: '使', data: [3000, 5000] }
// ]
default: () => []
}
},
data() {
return {
chartInstance: null //
};
},
watch: {
//
xAxisData: {
deep: true,
handler() {
this.updateChart();
}
},
seriesData: {
deep: true,
handler() {
this.updateChart();
}
}
},
mounted() {
//
this.$nextTick(() => {
this.initChart();
});
},
beforeDestroy() {
//
if (this.chartInstance) {
this.chartInstance.dispose();
}
},
methods: {
//
initChart() {
this.chartInstance = echarts.init(this.$refs.chartDom);
this.updateChart();
},
//
updateChart() {
if (!this.chartInstance) return;
const option = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' } //
},
legend: {
// 使 seriesData name
data: this.seriesData.map(item => item.name),
top: 10
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true //
},
xAxis: {
type: 'category',
data: this.xAxisData // Xprop
},
yAxis: {
type: 'value',
name: '金额(元)',
axisLabel: {
formatter: '{value}' // Y
}
},
series: this.seriesData.map(item => ({
...item,
type: 'bar' //
}))
};
this.chartInstance.setOption(option);
}
}
};
</script>
<style scoped>
.bar-chart {
width: 100%;
min-width: 600px;
}
.chart-container {
width: 100%;
height: 400px; /* 固定高度,可根据需求调整 */
}
</style>

@ -25,6 +25,7 @@
<div style="display: flex;justify-content: flex-end;"> <div style="display: flex;justify-content: flex-end;">
<Page :total="total" <Page :total="total"
show-elevator show-elevator
:page-size="select.page_size"
@on-change="e => { @on-change="e => {
select.page = e; select.page = e;
getPlanProgress(); getPlanProgress();
@ -49,9 +50,11 @@ export default {
type: [], type: [],
drawer: false, drawer: false,
select: { select: {
page_size: 10, page_size: 100,
page: 1, page: 1,
top_pid: 1, top_pid: 1,
sort_name:'pid',
sort_type:'ASC'
}, },
total: 0, total: 0,
list: [], list: [],
@ -160,7 +163,7 @@ export default {
}, },
async getPlanProgress(val) { async getPlanProgress(val) {
const res = await getProgress({ const res = await getProgress({
year:val, year:this.year,
...this.select ...this.select
}) })
for (let m of res.list.data) { for (let m of res.list.data) {
@ -235,6 +238,7 @@ export default {
watch: { watch: {
year: { year: {
handler:function(val) { handler:function(val) {
console.log("val",val)
this.getPlanProgress(val) this.getPlanProgress(val)
}, },
immediate: true immediate: true

@ -1,256 +1,434 @@
<template> <template>
<div> <div>
<el-drawer <el-drawer size="880px" title="执行统计" :visible.sync="drawer" direction="rtl">
size="880px" <div style="padding: 0 20px;">
title="执行统计" <template v-if="type === 1">
:visible.sync="drawer" <Card>
direction="rtl"> <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="e => {
<div style="padding: 0 20px;"> departmentSelect = e ? departments.map(i => (i.plan_department_id)) : [];
<template v-if="type === 1"> isIndeterminate = false;
<Card> }">全选</el-checkbox>
<el-checkbox :indeterminate="isIndeterminate" <el-checkbox-group style="max-height:200px" v-model="departmentSelect" size="small" @change="e => {
v-model="checkAll" let count = e.length;
@change="e => { checkAll = count === departments.length;
departmentSelect = e ? departments.map(i => (i.plan_department_id)) : []; isIndeterminate = count > 0 && count < departments.length;
isIndeterminate = false; }">
}">全选</el-checkbox> <el-checkbox v-for="item in departments" :label="item.plan_department_id"
<el-checkbox-group v-model="departmentSelect" :key="item.plan_department_id">{{item.plan_department ? item.plan_department.name : item.plan_department_id}}</el-checkbox>
size="small" </el-checkbox-group>
@change="e => { </Card>
let count = e.length;
checkAll = count === departments.length; <Card style="margin-top: 20px;">
isIndeterminate = count > 0 && count < departments.length; <div class="content">
}"> <div class="item">
<el-checkbox v-for="item in departments" :label="item.plan_department_id" :key="item.plan_department_id">{{item.plan_department ? item.plan_department.name : item.plan_department_id}}</el-checkbox> <p class="item__title">年初预算合计金额</p>
</el-checkbox-group> <div class="item__value">{{moneyFormatter(totalMoneyTotal1)}}</div>
</Card> </div>
<div class="item">
<Card style="margin-top: 20px;"> <p class="item__title">调整后预算合计金额</p>
<div class="content"> <div class="item__value">{{moneyFormatter(totalMoneyTotal2)}}</div>
<div class="item"> </div>
<p class="item__title">年初预算合计金额</p> <div class="item">
<div class="item__value">{{moneyFormatter(totalMoneyTotal1)}}</div> <p class="item__title">已使用</p>
</div> <div class="item__value">{{moneyFormatter(totalUseMoneyTotal)}}</div>
<div class="item"> </div>
<p class="item__title">调整后预算合计金额</p> <div class="item">
<div class="item__value">{{moneyFormatter(totalMoneyTotal2)}}</div> <p class="item__title">执行情况</p>
</div> <el-progress class="item__value" text-inside :stroke-width="20"
<div class="item"> :percentage="totalPercent"></el-progress>
<p class="item__title">已使用</p> </div>
<div class="item__value">{{moneyFormatter(totalUseMoneyTotal)}}</div> </div>
</div> </Card>
<div class="item"> </template>
<p class="item__title">执行情况</p> <template v-else>
<el-progress class="item__value" text-inside :stroke-width="20" :percentage="totalPercent"></el-progress> <Card >
</div> <el-checkbox :indeterminate="isIndeterminate2" v-model="checkAll2" @change="e => {
</div> typeSelect = e ? types.map(i => (i.id)) : [];
</Card> isIndeterminate = false;
</template> }">全选</el-checkbox>
<template v-else>
<Card>
<el-checkbox :indeterminate="isIndeterminate2" <el-checkbox-group style="max-height:200px" v-model="typeSelect" size="small" @change="e => {
v-model="checkAll2" let count = e.length;
@change="e => { checkAll2 = count === types.length;
typeSelect = e ? types.map(i => (i.id)) : []; isIndeterminate2 = count > 0 && count < types.length;
isIndeterminate = false; }">
}">全选</el-checkbox> <el-checkbox v-for="item in types" :label="item.id" :key="item.id">{{item.name}}</el-checkbox>
<el-checkbox-group v-model="typeSelect" </el-checkbox-group>
size="small" </Card>
@change="e => {
let count = e.length; <Card style="margin-top: 20px;">
checkAll2 = count === types.length; <div class="content">
isIndeterminate2 = count > 0 && count < types.length; <div class="item">
}"> <p class="item__title">年初预算合计金额</p>
<el-checkbox v-for="item in types" :label="item.id" :key="item.id">{{item.name}}</el-checkbox> <div class="item__value">{{moneyFormatter(totalMoneyTotal1Type)}}</div>
</el-checkbox-group> </div>
</Card> <div class="item">
<p class="item__title">调整后预算合计金额</p>
<Card style="margin-top: 20px;"> <div class="item__value">{{moneyFormatter(totalMoneyTotal2Type)}}</div>
<div class="content"> </div>
<div class="item"> <div class="item">
<p class="item__title">年初预算合计金额</p> <p class="item__title">已使用</p>
<div class="item__value">{{moneyFormatter(totalMoneyTotal1Type)}}</div> <div class="item__value">{{moneyFormatter(totalUseMoneyTotalType)}}</div>
</div> </div>
<div class="item"> <div class="item">
<p class="item__title">调整后预算合计金额</p> <p class="item__title">执行情况</p>
<div class="item__value">{{moneyFormatter(totalMoneyTotal2Type)}}</div> <el-progress class="item__value" text-inside :stroke-width="20"
</div> :percentage="totalPercent"></el-progress>
<div class="item"> </div>
<p class="item__title">已使用</p> </div>
<div class="item__value">{{moneyFormatter(totalUseMoneyTotalType)}}</div> </Card>
</div> </template>
<div class="item">
<p class="item__title">执行情况</p> <!-- <barChart v-if="drawer" :xAxisData="xAxis" :seriesData="seriesData" /> -->
<el-progress class="item__value" text-inside :stroke-width="20" :percentage="totalPercent"></el-progress> <div class="bar-chart" v-if="drawer">
</div> <!-- 图表容器 -->
</div> <div ref="chartDom" class="chart-container" style="background: #fff; min-height: 400px;"></div>
</Card> </div>
</template>
</div> </div>
</el-drawer> </el-drawer>
</div> </div>
</template> </template>
<script> <script>
import { moneyFormatter } from "@/utils" import * as echarts from 'echarts'
import { typeCarry } from "@/api/dashboard/notice" import {
export default { moneyFormatter
props: { } from "@/utils"
departments: Array, import {
year: [Number,String] typeCarry
}, } from "@/api/dashboard/notice"
data() { // import barChart from "@/views/dashboard/components/barChart.vue"
return { export default {
type: 1,//12 props: {
drawer: false, departments: Array,
isIndeterminate: false, year: [Number, String]
checkAll: false, },
components: {
departmentSelect: [], // barChart
},
data() {
isIndeterminate2: false, return {
checkAll2: false, type: 1, //12
types: [], drawer: false,
typeSelect: [], isIndeterminate: false,
} checkAll: false,
},
methods: { departmentSelect: [],
moneyFormatter,
show () {
this.drawer = true isIndeterminate2: false,
}, checkAll2: false,
hide () { types: [],
this.drawer = false typeSelect: [],
}, chartInstance: null,
setType (type) { }
this.type = type },
} mounted() {
},
computed: { },
selectedDepartments () { methods: {
return this.departments?.filter(i => this.departmentSelect.find(j => j === i.plan_department_id)) || [] moneyFormatter,
}, show() {
totalMoneyTotal1 () { this.drawer = true
return this.selectedDepartments.reduce((pre,cur)=>(pre+Number(cur.money_total_1||0)),0) || 0 },
}, hide() {
totalMoneyTotal2 () { this.drawer = false
return this.selectedDepartments.reduce((pre,cur)=>(pre+Number(cur.money_total_2||0)),0) || 0 },
}, setType(type) {
totalUseMoneyTotal () { this.type = type
return this.selectedDepartments.reduce((pre,cur)=>(pre+Number(cur.use_money_total||0)),0) || 0 },
}, //
totalPercent () { initChart() {
if (this.type === 1) { this.chartInstance = echarts.init(this.$refs.chartDom);
return (Number(this.totalMoneyTotal2 !== 0) ? (Math.round(Number(this.totalUseMoneyTotal) / Number(this.totalMoneyTotal2) * 10000)/100) : (Math.round(Number(this.totalUseMoneyTotal) / Number(this.totalMoneyTotal1) * 10000)/100)) || 0 console.log("this.chartInstance", this.chartInstance)
} else { this.updateChart()
return (Number(this.totalMoneyTotal2Type !== 0) ? (Math.round(Number(this.totalUseMoneyTotalType) / Number(this.totalMoneyTotal2Type) * 10000)/100) : (Math.round(Number(this.totalUseMoneyTotalType) / Number(this.totalMoneyTotal1Type) * 10000)/100)) || 0 },
}
}, //
updateChart() {
selectedTypes () { if (!this.chartInstance) return;
return this.types?.filter(i => this.typeSelect.find(j => j === i.id)) || [] console.log('xAxisData', this.xAxisData);
}, console.log('ydata1', this.ydata1);
totalMoneyTotal1Type () { const option = {
return this.selectedTypes?.reduce((pre,cur)=>(pre+Number(cur.money_total_1||0)),0) || 0 tooltip: {
}, trigger: 'axis',
totalMoneyTotal2Type () { axisPointer: {
return this.selectedTypes?.reduce((pre,cur)=>(pre+Number(cur.money_total_2||0)),0) || 0 type: 'shadow'
}, }, //
totalUseMoneyTotalType () { formatter: function(params) {
return this.selectedTypes?.reduce((pre,cur)=>(pre+Number(cur.use_money_total||0)),0) || 0 console.log("params",params)
}, let result = params[0].axisValue + '<br/>';
}, params.forEach(item => {
watch: { result += item.marker + item.seriesName + ': ' + item.value + '<br/>';
year: { });
handler:function(val) { return result;
typeCarry({ }
year: val },
}).then(res => { legend: {
this.types = res // 使 seriesData name
console.log(res) data: ['年初预算合计金额', '调整后预算合计金额', '已使用'],
}) top: 10
}, },
immediate: true grid: {
} left: '3%',
} right: '4%',
} bottom: '3%',
</script> containLabel: true //
},
<style scoped lang="scss"> xAxis: {
$color: linear-gradient(to top left, #ff6641, #ec3634), type: 'category',
linear-gradient(to top left, #4bfbb2, #49f2ac), data: this.xAxisData, // computed xAxisData
linear-gradient(to top left, #efd458, #ba840a), axisLabel: {
linear-gradient(to top left, #05e6ff, #0069fe), interval: 0,
linear-gradient(to top left, #a5ffff, #8fccd9), rotate: 30 //
linear-gradient(to top left, #fca7ff, #7a519a), }
linear-gradient(to top left, #a4e829, #06ac2e); },
yAxis: {
@for $index from 1 through length($color) { type: 'value',
.card#{$index} { // name: '',
color: #fff; axisLabel: {
display: flex; formatter: '{value}' // Y
flex-direction: column; }
justify-content: center; },
background: nth($color, $index); series: [{
grid-area: card#{$index}; type:'bar',
border-radius: 4px; name: '年初预算合计金额',
filter: drop-shadow(0 2px 8px #0004); data: this.ydata1, // X
itemStyle: {
padding: 20px; color: '#5470C6'
} //
.item__title { },
font-weight: 600; {
text-align: center; type:'bar',
name: '调整后预算合计金额',
padding-bottom: 20px; data: this.ydata2,
position: relative; itemStyle: {
color: '#91CC75'
&::after { }
content: ''; },
height: 2px; {
background: linear-gradient(to right,#fff 40%,#0000); type:'bar',
name: '已使用',
position: absolute; data: this.ydata3,
bottom: -1px; itemStyle: {
left: 0; color: '#FAC858'
right: 0; }
} }
} ],
.item__value { };
text-align: center; this.$nextTick(() => {
flex: 1; this.chartInstance.setOption(option);
padding-top: 20px; })
}
} }
} },
.content { computed: {
display: grid; selectedDepartments() {
grid-template-columns: repeat(4,1fr); return this.departments?.filter(i => this.departmentSelect.find(j => j === i.plan_department_id)) || []
grid-gap: 20px; },
totalMoneyTotal1() {
padding: 20px 0; return this.selectedDepartments.reduce((pre, cur) => (pre + Number(cur.money_total_1 || 0)), 0) || 0
.item__title { },
text-align: center; totalMoneyTotal2() {
} return this.selectedDepartments.reduce((pre, cur) => (pre + Number(cur.money_total_2 || 0)), 0) || 0
.item__value { },
font-weight: 600; totalUseMoneyTotal() {
font-size: 17px; return this.selectedDepartments.reduce((pre, cur) => (pre + Number(cur.use_money_total || 0)), 0) || 0
text-align: center; },
totalPercent() {
padding-top: 20px; if (this.type === 1) {
} return (Number(this.totalMoneyTotal2 !== 0) ? (Math.round(Number(this.totalUseMoneyTotal) / Number(this
} .totalMoneyTotal2) * 10000) / 100) : (Math.round(Number(this.totalUseMoneyTotal) / Number(this
.totalMoneyTotal1) * 10000) / 100)) || 0
::v-deep .el-checkbox { } else {
display: block; return (Number(this.totalMoneyTotal2Type !== 0) ? (Math.round(Number(this.totalUseMoneyTotalType) / Number(
margin-bottom: 6px; this.totalMoneyTotal2Type) * 10000) / 100) : (Math.round(Number(this.totalUseMoneyTotalType) / Number(
} this.totalMoneyTotal1Type) * 10000) / 100)) || 0
::v-deep .el-checkbox-group { }
max-height: 300px; },
overflow-y: scroll;
selectedTypes() {
margin-left: 20px; return this.types?.filter(i => this.typeSelect.find(j => j === i.id)) || []
} },
</style> totalMoneyTotal1Type() {
return this.selectedTypes?.reduce((pre, cur) => (pre + Number(cur.money_total_1 || 0)), 0) || 0
},
totalMoneyTotal2Type() {
return this.selectedTypes?.reduce((pre, cur) => (pre + Number(cur.money_total_2 || 0)), 0) || 0
},
totalUseMoneyTotalType() {
return this.selectedTypes?.reduce((pre, cur) => (pre + Number(cur.use_money_total || 0)), 0) || 0
},
xAxisData() {
if (this.type == 1) {
return this.selectedDepartments.map(item => item.plan_department?.name || '');
} else {
return this.selectedTypes.map(item => item?.name || '');
}
},
ydata1() {
if (this.type == 1) {
return this.selectedDepartments.map(item => Number(item.money_total_1) || 0)
} else {
return this.selectedTypes.map(item => Number(item.money_total_1) || 0)
}
},
ydata2() {
if (this.type == 1) {
return this.selectedDepartments.map(item => Number(item.money_total_2) || 0)
} else {
return this.selectedTypes.map(item => Number(item.money_total_2) || 0)
}
},
ydata3() {
if (this.type == 1) {
return this.selectedDepartments.map(item => Number(item.use_money_total) || 0)
} else {
return this.selectedTypes.map(item => Number(item.use_money_total) || 0)
}
}
},
watch: {
year: {
handler: function(val) {
typeCarry({
year: val
}).then(res => {
this.types = res
// this.xAxisData = types.map(item=>item.name?item.name:'') // xAxisData computed
console.log(res)
})
},
immediate: true
},
departments:{
handler: function(val) {
// this.xAxisData = val.map(item=>item.plan_department?item.plan_department.name:'') // xAxisData computed
},
immediate: true
},
drawer(newVal) {
if (newVal) {
this.$nextTick(() => {
// this.updateChart();
this.initChart()
})
} else {
if (this.chartInstance) {
this.chartInstance.dispose();
}
}
},
departmentSelect: {
handler() {
if (this.drawer && this.chartInstance) {
this.updateChart();
}
},
deep: true
},
typeSelect: {
handler() {
if (this.drawer && this.chartInstance) {
this.updateChart();
}
},
deep: true
}
}
}
</script>
<style scoped lang="scss">
.chart-container {
width: 80%;
height: 250px;
margin:0 auto;
/* 固定高度,可根据需求调整 */
}
$color: linear-gradient(to top left, #ff6641, #ec3634),
linear-gradient(to top left, #4bfbb2, #49f2ac),
linear-gradient(to top left, #efd458, #ba840a),
linear-gradient(to top left, #05e6ff, #0069fe),
linear-gradient(to top left, #a5ffff, #8fccd9),
linear-gradient(to top left, #fca7ff, #7a519a),
linear-gradient(to top left, #a4e829, #06ac2e);
@for $index from 1 through length($color) {
.card#{$index} {
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
background: nth($color, $index);
grid-area: card#{$index};
border-radius: 4px;
filter: drop-shadow(0 2px 8px #0004);
padding: 20px;
.item__title {
font-weight: 600;
text-align: center;
padding-bottom: 20px;
position: relative;
&::after {
content: '';
height: 2px;
background: linear-gradient(to right, #fff 40%, #0000);
position: absolute;
bottom: -1px;
left: 0;
right: 0;
}
}
.item__value {
text-align: center;
flex: 1;
padding-top: 20px;
}
}
}
.content {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 20px;
padding: 20px 0;
.item__title {
text-align: center;
}
.item__value {
font-weight: 600;
font-size: 17px;
text-align: center;
padding-top: 20px;
}
}
::v-deep .el-checkbox {
display: block;
margin-bottom: 6px;
}
::v-deep .el-checkbox-group {
max-height: 300px;
overflow-y: scroll;
margin-left: 20px;
}
</style>

@ -60,16 +60,18 @@
<Button type="primary" style="margin-left: 10px" @click="getPlanProgress"></Button> <Button type="primary" style="margin-left: 10px" @click="getPlanProgress"></Button>
</slot> </slot>
</lx-header> </lx-header>
<!-- :object-span-method="objectSpanMethod" -->
<xy-table ref="xyTable" :object-span-method="objectSpanMethod" :table-item="table" :list="list" :show-summary="true" :summary-method="summary"> <xy-table ref="xyTable"
:defaultExpandAll="false" row-key="id"
:table-item="table" :list="list" :show-summary="true" :summary-method="summary">
<template v-slot:btns> <template v-slot:btns>
<div /> <div />
</template> </template>
</xy-table> </xy-table>
<div style="display: flex;justify-content: flex-end;"> <!-- <div style="display: flex;justify-content: flex-end;">
<Page :total="total" show-elevator @on-change="pageChange" /> <Page :total="total" :page-size="select.page_size" show-elevator @on-change="pageChange" />
</div> </div> -->
</div> </div>
</template> </template>
@ -96,6 +98,7 @@ export default {
type: [], // type: [], //
departments: [], departments: [],
select: { select: {
page_size:999,
pageIndex: 1, pageIndex: 1,
year: '', year: '',
type: '', type: '',
@ -126,33 +129,6 @@ export default {
align: 'left', align: 'left',
fixed: 'left' fixed: 'left'
}, },
{
prop: 'type',
label: '预算类型',
width: 120,
formatter: (cell, data, value) => {
const res = this.type.filter(item => {
return item.id === value
})
return res[0]?.value || '未知'
}
},
{
prop: 'year',
label: '所属年份',
width: 160
},
{
prop: 'plan_department.name',
label: '相关科室',
width: 180
},
{
prop: 'content',
label: '描述',
align: 'left',
minWidth: 300
},
{ {
prop: 'money', prop: 'money',
width: 180, width: 180,
@ -174,13 +150,16 @@ export default {
return ( return (
<a on={{ <a on={{
'click': _ => { 'click': _ => {
this.$router.push({ if(!row.isParent){
path: '/contract/paymentRegistrationList_1', this.$router.push({
query: { path: '/contract/paymentRegistrationList_1',
plan_id: row.id, query: {
plan_name: `[${row.year}]-${row.name}` plan_id: row.id,
} plan_name: `[${row.year}]-${row.name}`
}) }
})
}
} }
}}>{ row.use_money_total }</a> }}>{ row.use_money_total }</a>
) )
@ -199,7 +178,34 @@ export default {
} > </el-progress> </div> } > </el-progress> </div>
) )
} }
} },
{
prop: 'type',
label: '预算类型',
width: 120,
formatter: (cell, data, value) => {
const res = this.type.filter(item => {
return item.id === value
})
return res[0]?.value || '未知'
}
},
{
prop: 'year',
label: '所属年份',
width: 160
},
{
prop: 'plan_department.name',
label: '相关科室',
width: 180
},
{
prop: 'content',
label: '描述',
align: 'left',
width: 300
},
] ]
} }
}, },
@ -296,7 +302,7 @@ export default {
async getPlanProgress() { async getPlanProgress() {
const res = await getProgress({ const res = await getProgress({
page_size: 10, page_size: this.select.page_size,
page: this.select.pageIndex, page: this.select.pageIndex,
year: this.select.year, year: this.select.year,
type: this.select.type, type: this.select.type,
@ -304,23 +310,73 @@ export default {
top_pid: 1, top_pid: 1,
...this.select ...this.select
}) })
for (var m of res.list.data) { // for (var m of res.list.data) {
m.pid_info_name = m.pid_info?.name // m.pid_info_name = m.pid_info?.name
} // }
this.list = // this.list =
mergeTableRow({ // mergeTableRow({
data: res.list.data, // data: res.list.data,
mergeColNames: ['pid_info_name'], // // mergeColNames: ['pid_info_name'], //
firstMergeColNames: ['pid_info_name'], // firstMerge // firstMergeColNames: ['pid_info_name'], // firstMerge
firstMerge: 'pid_info_name' // // firstMerge: 'pid_info_name' //
}) // })
this.list = this.concactPid(res.list.data)
this.total = res.list.total this.total = res.list.total
this.useMoneyTotal = res.use_money_total this.useMoneyTotal = res.use_money_total
this.moneyTotal = res.money this.moneyTotal = res.money
this.updateMoneyTotal = res.update_money this.updateMoneyTotal = res.update_money
this.rateTotal = this.toper(this.updateMoneyTotal, this.moneyTotal, this.useMoneyTotal) this.rateTotal = this.toper(this.updateMoneyTotal, this.moneyTotal, this.useMoneyTotal)
console.log(res) console.log("list",this.list)
},
concactPid(arr){
const groupByPid = {};
arr.forEach(item => {
const key = item.pid;
if (!groupByPid[key]) {
groupByPid[key] = [];
}
groupByPid[key].push(item);
});
// 2.
const mergedResult = Object.values(groupByPid).map(children => {
// pid_info pid pid_info
const pidInfo = children[0].pid_info;
// use_money_totalchildren use_money_total null 0
const useMoneyTotal = children.reduce((sum, child) => {
const value = child.use_money_total ?? 0; // null
return parseFloat(sum) + parseFloat(value);
}, 0);
//
return {
pid: children[0].pid, // pid pid
id:pidInfo.pid+'-'+pidInfo.id,
pid_info_name: pidInfo.name,
money: parseFloat(pidInfo.money).toFixed(2),
type: pidInfo.type,
isParent:true,
update_money: parseFloat(pidInfo.update_money).toFixed(2),
use_money_total: parseFloat(useMoneyTotal).toFixed(2), //
children: children //
};
});
const arrayWithPer = mergedResult.map(row => {
// peruse_money_total / (update_money || money) null NaN
const useMoneyTotal = Number(row.use_money_total ?? 0); // null 0
const denominator = Number(row.update_money) || Number(row.money); // update_money money
const calculation_result = isNaN(useMoneyTotal / denominator) ? 0 : useMoneyTotal / denominator;
return {
...row,
calculation_result: calculation_result // per
};
});
const sortedArray = arrayWithPer.sort((a, b) => b.calculation_result - a.calculation_result);
return sortedArray
} }
} }
} }

Loading…
Cancel
Save