master
lion 4 months ago
parent 7fa7f952f3
commit 303d2889bf

@ -25,6 +25,15 @@ export function statistic(params, noloading = false) {
})
}
export function statisticDepartment(params,noLoading = false){
return request({
method:'get',
url:'/api/admin/notice/statistic-department',
params,
noLoading
})
}
export function departmentTotal(params) {
return request({
method: 'get',
@ -40,3 +49,11 @@ export function nopayTotal(params) {
params
})
}
export function typeCarry(params) {
return request({
method: 'get',
url: '/api/admin/notice/type-carry',
params
})
}

@ -0,0 +1,230 @@
<template>
<div>
<el-drawer
size="1050px"
title="预算统计"
:visible.sync="drawer"
direction="rtl">
<div style="padding: 0 10px;">
<xy-table style="width: 100%;"
ref="xyTable"
:objectSpanMethod="objectSpanMethod"
:table-item="table"
:list="list"
:show-summary="true"
:summary-method="summary"
@select="selected"
@select-all="selectAll">
<template v-slot:btns>
<div></div>
</template>
</xy-table>
<div style="display: flex;justify-content: flex-end;">
<Page :total="total"
show-elevator
@on-change="e => {
select.page = e;
getPlanProgress();
}" />
</div>
</div>
</el-drawer>
</div>
</template>
<script>
import { moneyFormatter } from '@/utils'
import { getProgress } from '@/api/budget/budget'
import { mergeTableRow } from '@/utils/mergeTableRow'
export default {
data() {
return {
type: [],
drawer: false,
select: {
page_size: 10,
page: 1,
top_pid: 1
},
total: 0,
list: [],
selections: [],
table: [
{
prop: 'selection',
type: 'selection',
width: 50,
label: ' ',
reserveSelection: true,
fixed: 'left'
},
{
label: "隶属项目",
prop: 'pid_info_name',
width: 200,
align: 'left',
sortable: false,
fixed: this.$store.getters.device === 'mobile'?false:'left'
},
{
prop: 'name',
label: '项目名称',
width: 200,
align: 'left',
fixed: this.$store.getters.device === 'mobile'?false:'left'
},
{
prop: 'type_detail.value',
label: '预算类型',
width: 120,
},
{
prop: 'year',
label: '所属年份',
width: 160
},
{
prop: 'plan_department.name',
label: "相关科室",
width: 180
},
{
prop: 'content',
label: '描述',
align: 'left',
minWidth: 300
},
{
prop: 'money',
width: 180,
label: '年初预算金额(元)',
align: 'right'
},
{
prop: 'update_money',
width: 180,
label: '调整后预算金额(元)',
align: 'right'
},
{
prop: 'use_money_total',
label: '使用金额',
align: 'right',
width: 180
},
{
prop: 'rate',
label: '进展率',
width: 200,
customFn: (row) => {
let m2 = row.update_money;
let m1 = row.money;
let m3 = row.use_money_total;
let per = 0;
if (m2 != 0) {
per = ((m3 / m2) * 100).toFixed(2);
} else if (m1 != 0) {
per = ((m3 / m1) * 100).toFixed(2);
}
let perShow = Number(per)
return ( <div><el-progress percentage={perShow}> </el-progress></div>)
}
},
]
}
},
methods: {
show () {
this.drawer = true
},
hide () {
this.drawer = false
},
objectSpanMethod({
row,
column,
}) {
const span = column['property'] + '-span'
if (row[span]) {
return row[span]
}
},
async getPlanProgress() {
const res = await getProgress(this.select)
for (let m of res.list.data) {
m.pid_info_name = m.pid_info?.name
}
this.list =
mergeTableRow({
data: res.list.data,
mergeColNames: ["pid_info_name",'selection','index'], //
firstMergeColNames: ["pid_info_name"], // firstMerge
firstMerge: 'pid_info_name' //
})
this.total = res.list.total
},
selected (selections, selected) {
if (selected['pid_info_name-span']?.rowspan > 1) {
if (this.selections.find(i => i.id === selected.id)) {
let len = selected['pid_info_name-span'].rowspan
let idx = this.selections.indexOf(selected)
this.selections.splice(idx,len)
} else {
let len = selected['pid_info_name-span'].rowspan
let idx = this.list.indexOf(selected)
this.selections.push(...this.list.slice(idx,idx+len))
}
} else {
if (this.selections.find(i => i.id === selected.id)) {
let idx = this.selections.indexOf(this.selections.find(i => i.id === selected.id))
this.selections.splice(idx,1)
} else {
this.selections.push(selected)
}
}
},
selectAll (selections) {
this.selections = selections
},
summary ({ columns,data }) {
return columns.map((column,index) => {
if (index === 0) return '总计';
if (column.property === 'money') {
return this.selections.reduce((pre,cur) => (pre + Number(cur['money'] || 0)),0).toFixed(2)
}
if (column.property === 'update_money') {
return this.selections.reduce((pre,cur) => (pre + Number(cur['update_money'] || 0)),0).toFixed(2)
}
if (column.property === 'use_money_total') {
return this.selections.reduce((pre,cur) => (pre + Number(cur['use_money_total'] || 0)),0).toFixed(2)
}
if (column.property === 'rate') {
let moneyTotal = this.selections.reduce((pre,cur) => (pre + Number(cur['money'] || 0)),0)
let useMoneyTotal = this.selections.reduce((pre,cur) => (pre + Number(cur['use_money_total'] || 0)),0)
let updateMoneyTotal = this.selections.reduce((pre,cur) => (pre + Number(cur['update_money'] || 0)),0)
if (updateMoneyTotal !== 0) return ((useMoneyTotal / updateMoneyTotal) * 100).toFixed(2) + '%';
if (moneyTotal !== 0) return ((useMoneyTotal / moneyTotal) * 100).toFixed(2) + '%';
return '0%';
}
return '';
})
}
},
computed: {},
created() {
this.getPlanProgress()
}
}
</script>
<style scoped lang="scss">
</style>

@ -0,0 +1,256 @@
<template>
<div>
<el-drawer
size="880px"
title="执行统计"
:visible.sync="drawer"
direction="rtl">
<div style="padding: 0 20px;">
<template v-if="type === 1">
<Card>
<el-checkbox :indeterminate="isIndeterminate"
v-model="checkAll"
@change="e => {
departmentSelect = e ? departments.map(i => (i.plan_department_id)) : [];
isIndeterminate = false;
}">全选</el-checkbox>
<el-checkbox-group v-model="departmentSelect"
size="small"
@change="e => {
let count = e.length;
checkAll = count === departments.length;
isIndeterminate = count > 0 && count < departments.length;
}">
<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>
</el-checkbox-group>
</Card>
<Card style="margin-top: 20px;">
<div class="content">
<div class="item">
<p class="item__title">年初预算合计金额</p>
<div class="item__value">{{moneyFormatter(totalMoneyTotal1)}}</div>
</div>
<div class="item">
<p class="item__title">调整后预算合计金额</p>
<div class="item__value">{{moneyFormatter(totalMoneyTotal2)}}</div>
</div>
<div class="item">
<p class="item__title">已使用</p>
<div class="item__value">{{moneyFormatter(totalUseMoneyTotal)}}</div>
</div>
<div class="item">
<p class="item__title">执行情况</p>
<el-progress class="item__value" text-inside :stroke-width="20" :percentage="totalPercent"></el-progress>
</div>
</div>
</Card>
</template>
<template v-else>
<Card>
<el-checkbox :indeterminate="isIndeterminate2"
v-model="checkAll2"
@change="e => {
typeSelect = e ? types.map(i => (i.id)) : [];
isIndeterminate = false;
}">全选</el-checkbox>
<el-checkbox-group v-model="typeSelect"
size="small"
@change="e => {
let count = e.length;
checkAll2 = count === types.length;
isIndeterminate2 = count > 0 && count < types.length;
}">
<el-checkbox v-for="item in types" :label="item.id" :key="item.id">{{item.name}}</el-checkbox>
</el-checkbox-group>
</Card>
<Card style="margin-top: 20px;">
<div class="content">
<div class="item">
<p class="item__title">年初预算合计金额</p>
<div class="item__value">{{moneyFormatter(totalMoneyTotal1Type)}}</div>
</div>
<div class="item">
<p class="item__title">调整后预算合计金额</p>
<div class="item__value">{{moneyFormatter(totalMoneyTotal2Type)}}</div>
</div>
<div class="item">
<p class="item__title">已使用</p>
<div class="item__value">{{moneyFormatter(totalUseMoneyTotalType)}}</div>
</div>
<div class="item">
<p class="item__title">执行情况</p>
<el-progress class="item__value" text-inside :stroke-width="20" :percentage="totalPercent"></el-progress>
</div>
</div>
</Card>
</template>
</div>
</el-drawer>
</div>
</template>
<script>
import { moneyFormatter } from "@/utils"
import { typeCarry } from "@/api/dashboard/notice"
export default {
props: {
departments: Array,
year: [Number,String]
},
data() {
return {
type: 1,//12
drawer: false,
isIndeterminate: false,
checkAll: false,
departmentSelect: [],
isIndeterminate2: false,
checkAll2: false,
types: [],
typeSelect: [],
}
},
methods: {
moneyFormatter,
show () {
this.drawer = true
},
hide () {
this.drawer = false
},
setType (type) {
this.type = type
}
},
computed: {
selectedDepartments () {
return this.departments?.filter(i => this.departmentSelect.find(j => j === i.plan_department_id)) || []
},
totalMoneyTotal1 () {
return this.selectedDepartments.reduce((pre,cur)=>(pre+Number(cur.money_total_1||0)),0) || 0
},
totalMoneyTotal2 () {
return this.selectedDepartments.reduce((pre,cur)=>(pre+Number(cur.money_total_2||0)),0) || 0
},
totalUseMoneyTotal () {
return this.selectedDepartments.reduce((pre,cur)=>(pre+Number(cur.use_money_total||0)),0) || 0
},
totalPercent () {
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
} else {
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
}
},
selectedTypes () {
return this.types?.filter(i => this.typeSelect.find(j => j === i.id)) || []
},
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
},
},
watch: {
year: {
handler:function(val) {
typeCarry({
year: val
}).then(res => {
this.types = res
console.log(res)
})
},
immediate: true
}
}
}
</script>
<style scoped lang="scss">
$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>

@ -6,11 +6,22 @@
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<SvgIcon style="color: #2D8cF0;width: 24px;height: 24px;" icon-class="notice" />
<span style="padding-left: 10px">待办事项</span>
<i class="el-icon-more" style="margin-left: auto; font-size: 20px" />
<!-- <span style="padding-left: 10px">待办事项</span>
<i class="el-icon-more" style="margin-left: auto; font-size: 20px" /> -->
<el-tabs v-model="activeName">
<el-tab-pane label="待办事项" name="todo">
<span slot="label">
<SvgIcon style="color: #2D8cF0;width: 17px;height: 17px;" icon-class="notice" /> 待办事项
</span>
</el-tab-pane>
<el-tab-pane v-if="isShowNoPay" label="合同到期待办清单" name="todolist">
<span slot="label">
<SvgIcon style="color: #2D8cF0;width: 14px;height: 14px;" icon-class="caidan" /> 合同到期待办清单
</span>
</el-tab-pane>
</el-tabs>
</div>
<div class="table-tree1">
<div class="table-tree1" v-if="activeName==='todo'">
<el-table :data="list" border class="v-table" style="width: 100%; margin-bottom: 20px;height:220px">
<el-table-column type="index" label="序号" align="center" />
<el-table-column prop="type" width="100" label="类型" align="center" :formatter="typeFormatter" />
@ -45,11 +56,16 @@
<div style="display: flex; justify-content: flex-end">
<Page :total="total" show-elevator @on-change="pageChange" />
</div>
</div>
<div v-if="isShowNoPay && activeName==='todolist'">
<xy-table :cell-style="nopayCellStyle" :height="270" :list="nopay" :table-item="nopayTable">
<template v-slot:btns />
</xy-table>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-col :span="11">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<SvgIcon style="color: #2D8cF0;width: 24px;height: 24px;" icon-class="calendar-check" />
@ -99,41 +115,34 @@
</router-link>
</el-card>
</el-col>
<el-col :span="24">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<SvgIcon style="color: #2D8cF0;width: 24px;height: 24px;" icon-class="calendar-check" />
<span style="padding-left:10px;">合同签订统计表</span>
<i class="el-icon-more" style="margin-left: auto; font-size: 20px" />
</div>
<div style="margin-top:10px">
<xy-table
ref="xyTable"
:height="300"
:list="departmentTotal"
:table-item="departmentTotalTable"
:show-summary="true"
:summary-method="summary"
>
<template v-slot:btns />
</xy-table>
</div>
</el-card>
</el-col>
<el-col v-if="isShowNoPay" :span="24">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<SvgIcon style="color: #2D8cF0;width: 24px;height: 24px;" icon-class="caidan" />
<span style="padding-left:10px;">合同到期待办清单</span>
<i class="el-icon-more" style="margin-left: auto; font-size: 20px" />
</div>
<div style="margin-top:10px">
<xy-table :cell-style="nopayCellStyle" :height="300" :list="nopay" :table-item="nopayTable">
<template v-slot:btns />
</xy-table>
<el-col :span="1">
<div style="display: flex;flex-direction: column;height: 310px;padding-top: 20px;">
<div class="show-static-btn" @click="$refs['budgetStatic'].show()">
统计查看
<i class="el-icon-d-arrow-left"></i>
</div>
<div class="show-static-btn"
@click="$confirm('选择统计的数据类型','提示',{
confirmButtonText: '预算类型',
cancelButtonText: '部门科室',
showClose: false,
callback:(action, instance) => {
if (action === 'cancel') {
$refs['carryStatic'].setType(1)
$refs['carryStatic'].show()
}
if (action === 'confirm') {
$refs['carryStatic'].setType(2)
$refs['carryStatic'].show()
}
}
})">
执行统计
<i class="el-icon-d-arrow-left"></i>
</div>
</div>
</el-card>
</el-col>
</el-col>
</el-row>
<div v-for="(sitem, index) in typeList">
<template v-if="sitem[0]&&sitem[1]">
<el-col v-if="sitem[0]" :span="12">
@ -255,9 +264,49 @@
</el-col>
</template>
</div>
<el-col :span="24">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<SvgIcon style="color: #2D8cF0;width: 24px;height: 24px;" icon-class="calendar-check" />
<span style="padding-left:10px;">合同签订统计表</span>
<i class="el-icon-more" style="margin-left: auto; font-size: 20px" />
</div>
<div style="margin-top:10px">
<xy-table
ref="xyTable"
:height="300"
:list="departmentTotal"
:table-item="departmentTotalTable"
:show-summary="true"
:summary-method="summary"
>
<template v-slot:btns />
</xy-table>
</div>
</el-card>
</el-col>
<!-- <el-col v-if="isShowNoPay" :span="24">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<SvgIcon style="color: #2D8cF0;width: 24px;height: 24px;" icon-class="caidan" />
<span style="padding-left:10px;">合同到期待办清单</span>
<i class="el-icon-more" style="margin-left: auto; font-size: 20px" />
</div>
<div style="margin-top:10px">
<xy-table :cell-style="nopayCellStyle" :height="300" :list="nopay" :table-item="nopayTable">
<template v-slot:btns />
</xy-table>
</div>
</el-card>
</el-col> -->
</el-row>
<detailContract ref="detailContract" />
<detailContract ref="detailContract" />
<budgetStatic ref="budgetStatic"></budgetStatic>
<carryStatic ref="carryStatic" :departments="statistic.departmentList" :year="select.year"></carryStatic>
</div>
</template>
@ -266,7 +315,9 @@ import detailContract from '@/views/contract/components/detailContract'
import LxHeader from '@/components/LxHeader/index.vue'
import Pagination from '@/components/Pagination'
import SvgIcon from '@/components/SvgIcon/index.vue'
import MyProgress from '@/components/Progress/index.vue'
import MyProgress from '@/components/Progress/index.vue'
import budgetStatic from '@/views/dashboard/components/budgetStatic.vue'
import carryStatic from '@/views/dashboard/components/carryStatic.vue'
import {
adminDepartmentList
} from '@/api/system/department'
@ -281,7 +332,8 @@ import {
import {
getNotice,
readNotice,
statistic
statistic,
statisticDepartment
} from '@/api/dashboard/notice'
import {
parseTime,
@ -297,12 +349,15 @@ export default {
Pagination,
detailContract,
SvgIcon,
MyProgress
MyProgress,
budgetStatic,
carryStatic
},
data() {
return {
isShowNoPay: true,
nopay: [],
nopay: [],
activeName:'todo',
nopayTable: [{
prop: 'name',
label: '合同名称',
@ -1277,6 +1332,10 @@ export default {
arr.push([res.typeList[i], res.typeList[i + 1]])
}
this.typeList = arr
}
if(res.departmentList){
this.statistic.departmentList = res.departmentList?.sort((a, b) => a.plan_department.sortnumber - b.plan_department.sortnumber)
}
// this.split = Array.from({ length: Math.ceil(this.typeList/2) },() => 0.5)
},
@ -1427,7 +1486,35 @@ export default {
}
</style>
<style scoped lang="scss">
<style scoped lang="scss">
.show-static-btn {
cursor: pointer;
font-size: 15px;
font-weight: 600;
text-align: center;
color: #fff;
background: #4d8bdc;
border-radius: 4px;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
transition: all .2s;
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 20px;
box-sizing: border-box;
& + & {
background: darkorange;
margin-top: 20px;
}
&:hover {
transform: scale(1.05,1.05);
}
}
::v-deep .el-dialog__body {
padding: 8px 20px;
}

Loading…
Cancel
Save