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.

843 lines
23 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 class="app-container">
<div
style="display: flex; justify-content: space-between; align-items: center"
>
<span>当前登录人{{ user.name }} </span>
</div>
<el-row :gutter="20">
<el-col :span="16">
<el-card class="box-card" style="margin: 20px 0">
<div slot="header" class="clearfix">
<span>预算总体执行情况</span>
<DatePicker
:value="select.year"
placeholder="选择所属年份"
placement="bottom"
style="width: 130px; float: right"
type="year"
@on-change="changeYear"
></DatePicker>
</div>
<router-link :to="`/statisticalReport/budgetProgress`">
<div class="progress-card">
<div class="progress-card-item">
<div class="progress-card-item__num">
{{
moneyFormat(
statistic.progress ? statistic.progress.money_total_1 : 0
)
}}
</div>
<div class="progress-card-item__label">年初预算合计金额</div>
</div>
<div class="progress-card-item">
<div class="progress-card-item__num">
{{
moneyFormat(
statistic.progress ? statistic.progress.money_total_2 : 0
)
}}
</div>
<div class="progress-card-item__label">调整后预算合计金额</div>
</div>
<div class="progress-card-item">
<div class="progress-card-item__num">
{{
moneyFormat(
statistic.progress
? statistic.progress.use_money_total
: 0
)
}}
</div>
<div class="progress-card-item__label">已支付金额</div>
</div>
<div class="progress-card-item">
<div class="progress-card-item__num">
{{
toper(
statistic.progress ? statistic.progress.money_total_1 : 0,
statistic.progress ? statistic.progress.money_total_2 : 0,
statistic.progress
? statistic.progress.use_money_total
: 0
)
}}%
</div>
<div class="progress-card-item__label">执行率</div>
</div>
</div>
</router-link>
</el-card>
<div ref="lxHeader">
<LxHeader
icon="iconfont/icon--kucunguanli"
text="待办事项"
:custom="true"
style="margin-bottom: 10px; border: 0px; margin-top: 15px"
>
<div slot="content" />
<slot>
<div></div>
</slot>
</LxHeader>
</div>
<div class="table-tree">
<el-table
:data="list"
border
class="v-table"
style="width: 100%; margin-bottom: 20px"
>
<el-table-column type="index" label="序号" align="center" />
<el-table-column
prop="type"
width="100"
label="类型"
align="center"
:formatter="typeFormatter"
/>
<el-table-column prop="content" label="内容" align="left" />
<el-table-column
prop="created_at"
width="200"
label="下发时间"
align="center"
:formatter="timeFormatter"
/>
<el-table-column
width="100"
label="状态"
align="center"
prop="read_count"
>
<template slot-scope="{ row, $index }">
<el-link v-if="row.read_count" type="success" :underline="false"
>已读</el-link
>
<Button
v-else
type="error"
size="small"
style="margin-left: 10px"
ghost
@click="readIt(row, $index)"
>未读</Button
>
</template>
</el-table-column>
<el-table-column
width="90"
header-align="center"
label="操作"
align="center"
>
<template slot-scope="{ row, $index }">
<Button
type="primary"
size="small"
style="margin-left: 10px"
ghost
@click="toContract(row)"
>查看</Button
>
</template>
</el-table-column>
</el-table>
<div style="display: flex; justify-content: flex-end">
<Page :total="total" show-elevator @on-change="pageChange" />
</div>
</div>
</el-col>
<el-col :span="7">
<el-card class="box-card" style="margin: 20px 0">
<div class="text item">
<pie-chart :chartData="rptChartData"></pie-chart>
</div>
</el-card>
</el-col>
<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-col>
</el-row>
<div class="demo-split">
<Split v-model="split" style="height: 440px">
<template #left>
<div class="demo-split-pane" style="padding-right: 5px">
<div ref="lxHeader" v-if="statistic.departmentList">
<LxHeader
icon="iconfont/icon--kucunguanli"
text="科室执行情况"
:custom="true"
style="margin-bottom: 10px; border: 0px; margin-top: 15px"
>
<div slot="content" />
<slot>
<div style="width: 100%">
<xy-table
:height="380"
:list="statistic.departmentList"
:table-item="departmentTable"
>
<template v-slot:btns>
<el-table-column
:width="80"
fixed="right"
label="操作"
header-align="center"
align="center"
>
<template slot-scope="scope">
<router-link
:to="`/statisticalReport/budgetProgress?departmentId=${scope.row.plan_department_id}`"
>查看
</router-link>
</template>
</el-table-column>
</template>
</xy-table>
</div>
</slot>
</LxHeader>
</div>
</div>
</template>
<template #right>
<div class="demo-split-pane" style="padding-left: 10px">
<div ref="lxHeader" v-if="statistic.typeList">
<LxHeader
icon="iconfont/icon--kucunguanli"
text="预算类型执行情况"
:custom="true"
style="margin-bottom: 10px; border: 0px; margin-top: 15px"
>
<div slot="content" />
<slot>
<div style="width: 100%">
<xy-table
:height="380"
:list="statistic.typeList"
:table-item="typeTable"
>
<template v-slot:btns>
<el-table-column
:width="80"
fixed="right"
label="操作"
header-align="center"
align="center"
>
<template slot-scope="scope">
<router-link
:to="`/statisticalReport/budgetProgress?typeId=${scope.row.type}`"
>查看
</router-link>
</template>
</el-table-column>
</template>
</xy-table>
</div>
</slot>
</LxHeader>
</div>
</div>
</template>
</Split>
</div>
<Card>
<template #title>
<div style="display: flex;justify-content: space-between;align-items: center;">
<p>各科室执行率</p>
<el-date-picker v-model="carrySelect.month"
format="yyyy-MM"
:clearable="false"
style="width: 140px;"
placeholder="月份"
type="month"
size="small"
@change="e => {
carrySelect.month = $moment(e).format('YYYY-MM');
getCarry();
}"></el-date-picker>
</div>
</template>
<lineChart :chart-data="lineChartData"></lineChart>
</Card>
<detailContract ref="detailContract"></detailContract>
<budgetStatic ref="budgetStatic"></budgetStatic>
<carryStatic ref="carryStatic"></carryStatic>
</div>
</template>
<script>
import detailContract from "@/views/contract/components/detailContract";
import LxHeader from "@/components/LxHeader/index.vue";
import Pagination from "@/components/Pagination";
import PieChart from "./components/PieChart.vue";
import { adminDepartmentList } from '@/api/system/department';
import { adminUserList, getInfo } from '@/api/user';
import { getNotice, readNotice, statistic, carry } from "@/api/dashboard/notice";
import { parseTime, moneyFormatter } from '@/utils';
import { Message } from "element-ui";
import budgetStatic from '@/views/dashboard/components/budgetStatic.vue'
import lineChart from '@/views/dashboard/components/LineChart.vue'
import carryStatic from '@/views/dashboard/components/carryStatic.vue'
export default {
name: "Manage",
components: {
LxHeader,
Pagination,
detailContract,
PieChart,
budgetStatic,
lineChart,
carryStatic
},
data() {
return {
value2: 12,
split: 0.5,
rptChartData: [],
select: {
year: "",
},
carrySelect: {
month: this.$moment().format('YYYY-MM')
},
lineChartData: {},
statistic: "",
department_id: [],
userIds: [],
list: [],
total: 0,
pageIndex: 1,
// listQuery: {
// page: 1,
// page_size: 10,
// show_self: 1,
// },
form: {
id: undefined,
},
dialogFormVisible: false,
departmentTree: [],
userList: [],
formLabelWidth: "200px",
user: {},
departmentProgress: [],
departmentTable: [
{
label: "科室",
minWidth: 180,
prop: "plan_department.name",
sortable: false,
align: "left",
fixed: "left",
},
{
label: "执行情况",
sortable: false,
minWidth: 220,
align: "left",
fixed: "right",
customFn: (row) => {
let m2 = row?.money_total_2;
let m1 = row?.money_total_1;
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);
}
return (
<div>
<el-progress percentage={Number(per)}> </el-progress>{" "}
</div>
);
},
},
{
label: "年初预算合计金额(元)",
width: 260,
align: "right",
sortable: false,
prop: "money_total_1",
formatter: (cell, data, value) => {
return moneyFormatter(value);
},
},
{
label: "调整后预算合计金额(元)",
width: 260,
align: "right",
sortable: false,
prop: "money_total_2",
formatter: (cell, data, value) => {
return moneyFormatter(value);
},
},
{
label: "已使用(元)",
width: 120,
align: "right",
sortable: false,
prop: "use_money_total",
formatter: (cell, data, value) => {
return moneyFormatter(value);
},
},
],
typeTable: [
{
label: "预算类别",
width: 100,
prop: "type_text",
sortable: false,
fixed: "left",
},
{
label: "执行情况",
sortable: false,
minWidth: 220,
align: "left",
fixed: "right",
customFn: (row) => {
let m2 = row.money_total_2;
let m1 = row.money_total_1;
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);
}
return (
<div>
<el-progress percentage={Number(per)}> </el-progress>{" "}
</div>
);
},
},
{
label: "年初预算合计金额(元)",
width: 260,
align: "right",
sortable: false,
prop: "money_total_1",
formatter: (cell, data, value) => {
return moneyFormatter(value);
},
},
{
label: "调整后预算合计金额(元)",
width: 260,
align: "right",
sortable: false,
prop: "money_total_2",
formatter: (cell, data, value) => {
return moneyFormatter(value);
},
},
{
label: "已使用(元)",
width: 140,
align: "right",
sortable: false,
prop: "use_money_total",
formatter: (cell, data, value) => {
return moneyFormatter(value);
},
},
],
};
},
created() {
this.select.year = this.$moment().format("YYYY");
this.getNotices();
this.getStatistic();
this.getCarry();
getInfo()
.then((response) => {
console.log(response);
this.user = response;
})
.catch((error) => {});
},
methods: {
async getCarry () {
const res = await carry(this.carrySelect);
this.lineChartData = {
//legendArr: [`${this.carrySelect.month}计划`,`${this.carrySelect.month}实际`,`${this.$moment(this.carrySelect.month).add(1,'months').format('YYYY-MM')}计划`],
rotate: 54,
xArr: res?.map(i => i.plan_department?.name),
series: [
{
name: `${this.carrySelect.month}计划`,
type: 'bar',
barGap: 0,
emphasis: {
focus: 'series'
},
data: res?.map(i => {
const { use_money_total,money_total_1 } = i
return Math.round((Number(use_money_total||0) / Number(money_total_1||0)) * 10000) / 100
})
},
{
name: `${this.carrySelect.month}实际`,
type: 'bar',
barGap: 0,
emphasis: {
focus: 'series'
},
data: res?.map(i => {
const { use_money_total,money_total_2 } = i
return Math.round((Number(use_money_total||0) / Number(money_total_2||0)) * 10000) / 100
})
},
{
name: `${this.$moment(this.carrySelect.month).add(1,'months').format('YYYY-MM')}计划`,
type: 'bar',
barGap: 0,
emphasis: {
focus: 'series'
},
data: res?.map(i => {
const { use_money_total_next,money_total } = i
return Math.round((Number(use_money_total_next||0) / Number(money_total||0)) * 10000) / 100
})
},
]
};
console.log(res)
},
changeYear(e) {
this.select.year = e;
this.getStatistic();
},
async getStatistic() {
const res = await statistic(this.select);
console.log(res);
this.statistic = res;
let m2 = res.progress.money_total_2;
let m1 = res.progress.money_total_1;
var user_m = res.progress.use_money_total;
var m3=m1-user_m
if (m2 != 0) {
m3=m2-user_m;
} else if (m1 != 0) {
m3=m1-user_m;
}
this.rptChartData = {
xArr: ["预算未执行金额", "预算已执行金额"],
radiusArr: "50%",
yArr: [
{
value: m3,
name: "预算未执行金额",
label: {
show: false, //这个数据高亮时不显示label就不会显示替遮住第一个数据的label值了
},
emphasis: {
label: {
show: false, //这个数据高亮时不显示label就不会显示替遮住第一个数据的label值了
},
},
},
{ value: user_m, name: "预算已执行金额" },
],
};
},
toContract(row) {
this.$refs["detailContract"].getDetail(row.contract_id);
this.$refs["detailContract"].isShowDetail = true;
},
pageChange(e) {
this.pageIndex = e;
this.getNotices();
},
timeFormatter(cell, data, value) {
return parseTime(new Date(value));
},
typeFormatter(cell, data, value) {
return value === 1 ? "合同流程" : "付款计划";
},
toper(m1, m2, m3) {
if (m2 != 0) {
return ((m3 / m2) * 100).toFixed(2);
} else if (m1 != 0) {
return ((m3 / m1) * 100).toFixed(2);
} else return 0;
},
async getNotices() {
const res = await getNotice({
page_size: 10,
page: this.pageIndex,
});
this.list = res.data;
this.total = res.total;
},
filterChildren(data) {
data.forEach((i) => {
if (i.children && i.children.length > 0) {
this.filterChildren(i.children);
} else {
delete i.children;
}
});
},
getList() {
index(this.listQuery)
.then((response) => {
this.list = response.data;
this.total = response.total;
})
.catch((error) => {});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
readIt(row) {
readNotice({
id: row.id,
}).then((res) => {
Message({
type: "success",
message: "已读",
});
this.getNotices();
});
// show({id: row.id}).then(r => { this.form = r }).catch(error => {})
// this.list[index].is_read = 1
},
save() {
this.$refs["dataForm"].validate((valid) => {
if (valid) {
const tempData = Object.assign({}, this.form);
tempData.admin_id_list = tempData.ids.map((i) => {
return {
admin_id: i,
};
});
if (tempData.id) {
save(tempData)
.then((r) => {
this.saved();
})
.catch((error) => {});
} else {
store(tempData)
.then((r) => {
this.saved();
})
.catch((error) => {});
}
}
});
},
saved() {
this.handleFilter();
this.dialogFormVisible = false;
this.$message.success("保存成功");
},
handleDelete(row, index) {
this.$confirm("确定要删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
destroy({
id: row.id,
})
.then((response) => {
this.$message.success("删除成功");
this.getList();
})
.catch((error) => {});
})
.catch(() => {});
},
},
computed: {
moneyFormat() {
return function (price) {
return moneyFormatter(price);
};
},
},
};
</script>
<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;
}
::v-deep .el-col-5 {
width: 20%;
}
::v-deep .el-card__body {
padding: 4px 10px !important;
}
.progress-card {
display: flex;
&-item {
text-align: center;
flex: 1;
&__label {
font-size: 14px;
}
&__num {
font-size: 20px;
font-weight: 600;
padding: 6px 0;
}
}
}
</style>
<style lang="scss" scoped>
.ivu-btn {
margin-right: 10px;
}
.table-tree {
margin-top: 10px;
}
.v-text {
padding: 8px 0;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
.title {
color: #338de3;
}
}
.dataGroup {
padding: 10px 0;
}
.grid-content {
border-radius: 5px;
text-align: center;
background: #ffffff;
padding: 15px 0;
p {
line-height: 1.8;
}
p:first-child {
font-weight: 700;
font-size: 16px;
}
}
.bg-org {
border: 2px solid #ef6c24;
color: #ef6c24;
}
.bg-blue {
border: 2px solid #0800ff;
color: #0800ff;
}
.bg-green {
border: 2px solid #0f9700;
color: #0f9700;
}
.bg-black {
border: 2px solid #333;
color: #333;
}
.bg-pink {
border: 2px solid #ff0000;
color: #ff0000;
}
</style>