master
xy 2 years ago
parent 9b6c450c9b
commit da4d344d59

@ -0,0 +1,41 @@
import request from '@/utils/request'
export function paidIndex (params) {
return request({
method: 'get',
url: '/api/admin/paid-plan/index',
params
})
}
export function paidShow (params) {
return request({
method: 'get',
url: '/api/admin/paid-plan/show',
params
})
}
export function paidStore (data) {
return request({
method: 'post',
url: '/api/admin/paid-plan/store',
data
})
}
export function paidSave (data) {
return request({
method: 'post',
url: '/api/admin/paid-plan/save',
data
})
}
export function paidDestroy (params) {
return request({
method: 'get',
url: '/api/admin/paid-plan/destroy',
params
})
}

@ -34,3 +34,12 @@ export function statistic(params,noLoading = false){
noLoading
})
}
export function carry (params) {
return request({
method: 'get',
url: '/api/admin/notice/carry',
params,
})
}

@ -32,7 +32,7 @@
:value="select.type"
clearable
size="small"
style="width: 300px"
style="width: 160px"
@change="(e) => (select.type = e[e.length - 1] || '')"
/>
</span>

@ -0,0 +1,317 @@
<template>
<div>
<xy-dialog
ref="dialog"
:is-show.sync="isShow"
type="form"
title="付款计划"
:form="form"
@reset="getDetail"
@submit="submit"
>
<template v-slot:plan_id>
<div class="xy-table-item">
<div class="xy-table-item-label">项目 </div>
<div class="xy-table-item-content">
<el-input
readonly
:value="rowName"
clearable
placeholder="请输入项目"
style="width: 300px"
></el-input>
</div>
</div>
</template>
<template #item_list>
<el-button
icon="el-icon-plus"
type="primary"
size="small"
@click="
formList.push({
plan_id: id,
paid_money: 0,
remark: '',
paid_plan_date: '',
})
"
>新增</el-button
>
<xy-table
style="margin-top: 20px"
:height="320"
:table-item="itemTable"
:list="formList"
>
<template #btns> </template>
</xy-table>
</template>
</xy-dialog>
</div>
</template>
<script>
import { paidSave, paidIndex, paidStore, paidDestroy } from "@/api/budget/paidPlan"
export default {
props: {},
data() {
return {
isShow: false,
id: "",
type: "",
itemTable: [
{
prop: "paid_money",
label: "付款金额",
width: 180,
sortable: false,
customFn: (row) => {
return (
<el-input-number
precision={2}
controls={false}
placeholder="付款金额"
style="width: 100%;"
v-model={row.paid_money}
size="small"
clearable={true}
on={{
['change']:e => {
if (row.id) {
this.editId.push(row.id)
}
}
}}
></el-input-number>
);
},
},
{
prop: "paid_plan_date",
label: "年份-月份",
sortable: false,
width: 140,
customFn: (row) => {
return (
<el-date-picker
type="month"
placeholder="年份-月份"
style="width: 100%;"
value-format="yyyy-MM"
v-model={row.paid_plan_date}
size="mini"
clearable={true}
on={{
['change']:e => {
if (row.id) {
this.editId.push(row.id)
}
}
}}
></el-date-picker>
);
},
},
{
prop: "remark",
label: "备注",
sortable: false,
minWidth: 200,
customFn: (row) => {
return (
<el-input
v-model={row.remark}
placeholder="备注"
style="width: 100%;"
size="mini"
clearable={true}
on={{
['change']:e => {
if (row.id) {
this.editId.push(row.id)
}
}
}}
></el-input>
);
},
},
{
label: "操作",
width: 180,
align: "left",
sortable: false,
customFn: (row, scope) => {
return (
<Poptip
confirm={true}
transfer={true}
placement="bottom"
title="确认要删除吗"
on={{
["on-ok"]: (_) => {
if (!row.id) {
this.formList.splice(scope.$index, 1);
} else {
paidDestroy({
id: row.id
}).then(res => {
this.$message({
type: 'success',
message: '删除成功'
})
this.getDetail();
})
}
},
}}
>
<Button
style="margin-left: 4px;"
ghost
size="small"
type="error"
>
删除
</Button>
</Poptip>
);
},
},
],
rowName: "",
formList: [],
editId: [],
form: {
plan_id: "",
item_list: [],
},
};
},
methods: {
show() {
this.isShow = true;
},
hidden() {
this.isShow = false;
},
init() {
for (let key in this.form) {
if (this.form[key] instanceof Array) {
this.form[key] = [];
} else {
this.form[key] = "";
}
}
this.$refs["dialog"].clearValidate();
},
setId(id) {
if (typeof id == "number") {
this.id = id;
} else {
console.error("error typeof id: " + typeof id);
}
},
getId() {
return this.id;
},
setType(type = "add") {
let types = ["add", "editor"];
if (types.includes(type)) {
this.type = type;
} else {
console.warn("Unknown type: " + type);
}
},
setForm(key = [], value = []) {
if (key instanceof Array) {
key.forEach((key, index) => {
this.form[key] = value[index] ?? "";
});
}
if (typeof key === "string") {
this.form[key] = value;
}
if (!key) {
this.init();
}
},
async getDetail() {
let res = await paidIndex({
plan_id: this.id
})
this.formList = res.data;
},
submit() {
for(let i = 0;i < this.formList.length;i++) {
if (!this.formList[i]?.paid_plan_date || !this.formList[i]?.paid_money) {
this.$message({
type: 'warning',
message: `${i+1}条金额或日期未填写`
})
return
}
}
let editIds = Array.from(new Set(this.editId))
let promiseAll = [];
editIds.forEach(id => {
promiseAll.push(paidSave(this.formList.find(i => i.id === id)))
})
for (let i = 0;i < this.formList.length;i++) {
if (!this.formList[i].id) {
promiseAll.push(paidStore(this.formList[i]))
}
}
if (promiseAll.length > 0) {
Promise.all(promiseAll).then(res => {
this.$message({
type: 'success',
message: '保存成功'
})
this.hidden();
})
}
},
},
watch: {
isShow(val) {
if (val) {
this.getDetail();
} else {
this.id = "";
this.type = "";
this.rowName = "";
this.init();
this.formList = [];
this.editId = [];
this.$refs["dialog"].clearValidate();
delete this.form.id;
}
},
},
created() {},
};
</script>
<style scoped lang="scss">
::v-deep .el-input__inner {
text-align: left;
}
.xy-table-item-label {
width: 150px;
}
.select {
margin-bottom: 10px;
& > * {
margin-left: 6px;
}
}
</style>

@ -48,13 +48,19 @@
<xy-table ref="xyTable" :objectSpanMethod="objectSpanMethod" :table-item="table" :list="list" :show-summary="true"
:summary-method="summary">
<template v-slot:btns>
<div></div>
<el-table-column header-align="center" align="left" :width="130" label="操作" >
<template #default="{ row }">
<Button size="small" type="primary" @click="$refs['payPlan'].rowName = row.name,$refs['payPlan'].setId(row.id),$refs['payPlan'].show();">付款计划</Button>
</template>
</el-table-column>
</template>
</xy-table>
<div style="display: flex;justify-content: flex-end;">
<Page :total="total" show-elevator @on-change="pageChange" show-sizer @on-page-size-change="pageSizeChange" />
</div>
<payPlan ref="payPlan"></payPlan>
</div>
</template>
@ -76,7 +82,11 @@
import {
mergeTableRow
} from "@/utils/mergeTableRow"
import payPlan from '@/views/budget/components/payPlan.vue'
export default {
components: {
payPlan
},
data() {
return {
isShowAdd: false,

@ -63,7 +63,7 @@
this.chart.setOption({
legend: {
orient: 'horizontal',
bottom: '4%',
bottom: '3%',
},
tooltip: {
trigger: 'item',
@ -72,25 +72,24 @@
,
series: [{
name: '数据',
type: 'pie',
radius: ['60%', '90%'],
center: ['50%', '50%'], //
avoidLabelOverlap: true, //
label: { //
show: true, //true
position: 'center', //
formatter: '{d}%', //{d}
fontSize: 20,
fontWeight: 'bold'
},
color: ['#695BF9', '#1E3E55'], //
emphasis: { //
scale: false //item
},
labelLine: {
show: true
type: 'pie',
radius: ['60%', '90%'],
center: ['50%', '50%'], //
avoidLabelOverlap: true, //
label: { //
show: true, //true
position: 'center', //
formatter: '{d}%', //{d}
fontSize: 20,
fontWeight: 'bold'
},
color: ['#695BF9', '#1E3E55'], //
emphasis: { //
scale: false //item
},
labelLine: {
show: true
},
center: ['50%', '50%'],
data: chartdata.yArr,
animationDuration
}]
@ -99,4 +98,4 @@
}
}
}
</script>
</script>

@ -0,0 +1,233 @@
<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: 'left'
},
{
prop: 'name',
label: '项目名称',
width: 200,
align: 'left',
fixed: '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);
}
return ( < div >
<el-progress percentage = {
Number(per)
} > </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,41 @@
<template>
<div>
<el-drawer
size="1050px"
title="执行统计"
:visible.sync="drawer"
direction="rtl">
<div style="padding: 0 10px;">
{{type}}
</div>
</el-drawer>
</div>
</template>
<script>
export default {
data() {
return {
type: 1,//12
drawer: false
}
},
methods: {
show () {
this.drawer = true
},
hide () {
this.drawer = false
},
setType (type) {
console.log(type)
this.type = type
}
},
computed: {}
}
</script>
<style scoped lang="scss">
</style>

@ -154,13 +154,41 @@
</div>
</div>
</el-col>
<el-col :span="8">
<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">
@ -248,7 +276,31 @@
</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>
@ -257,11 +309,14 @@ 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.js";
import { adminUserList, getInfo } from "../../api/user.js";
import { getNotice, readNotice, statistic } from "@/api/dashboard/notice";
import { parseTime, moneyFormatter } from "@/utils/index";
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: {
@ -269,6 +324,9 @@ export default {
Pagination,
detailContract,
PieChart,
budgetStatic,
lineChart,
carryStatic
},
data() {
return {
@ -278,6 +336,10 @@ export default {
select: {
year: "",
},
carrySelect: {
month: this.$moment().format('YYYY-MM')
},
lineChartData: {},
statistic: "",
department_id: [],
userIds: [],
@ -433,6 +495,7 @@ export default {
this.getNotices();
this.getStatistic();
this.getCarry();
getInfo()
.then((response) => {
@ -442,6 +505,54 @@ export default {
.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();
@ -605,6 +716,34 @@ export default {
};
</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;
}

@ -11,10 +11,19 @@
<span style="padding: 0 6px;word-break: keep-all;">预算类型</span>
<span>
<el-select size="small" clearable v-model="select.type" placeholder="请选择预算类型" type="date"
style="width: 160px">
<el-option v-for="item in type" :value="item.id" :key="item.id" :label="item.value"></el-option>
</el-select>
<el-cascader
:options="type"
:props="{
checkStrictly: false,
label: 'name',
value: 'id',
}"
:value="select.type"
clearable
size="small"
style="width: 160px"
@change="(e) => (select.type = e[e.length - 1] || '')"
/>
</span>
<span style="padding: 0 6px;">
@ -53,8 +62,8 @@
listdeptNoAuth
} from "@/api/system/department";
import {
getparameter
} from "@/api/system/dictionary";
getparameter, getparameterTree
} from '@/api/system/dictionary'
import {
moneyFormatter
} from "@/utils";
@ -95,15 +104,10 @@
fixed: 'left'
},
{
prop: 'type',
prop: 'type_detail.value',
label: '预算类型',
width: 120,
formatter: (cell, data, value) => {
let res = this.type.filter(item => {
return item.id === value
})
return res[0]?.value || '未知'
}
},
{
prop: 'year',
@ -223,10 +227,23 @@
},
async getType() {
const res = await getparameter({
number: 'money_way'
})
this.type = res.detail
const res = await getparameterTree({
id: 3
});
const dataHandler = (data) => {
data.forEach(i => {
if (i.hasOwnProperty('detail')) {
i.children = i.detail.map(j => {
j.name = j.value
return j;
})
} else {
dataHandler(i['children'])
}
})
return data;
}
this.types = dataHandler(res?.children) || []
},
//
pageChange(e) {

Loading…
Cancel
Save