<template>
<a-modal :title="titleMap[mode]" :visible="visible" :destroyOnClose="true" :maskClosable="false" @cancel="handleCancel"
width="95%">
<a-spin :spinning="spinning" tip="加载中...">
<a-table :columns="columns" bordered :data-source="newSource" :scroll="{ x: 800, y: 500 }" :pagination="false"
:rowKey="(record, index) => { return record.projectId }" :defaultExpandAllRows="true"
v-if="newSource.length">
<template slot="promote" slot-scope="text,record">
<span>
{{ rateCompute(record.preActual, record.budget) }}
</span>
</template>
<template slot="budget" slot-scope="text,record">
<div class="editable-cell">
<div v-if="editable" class="editable-cell-input-wrapper">
<div v-if="editableData[record.projectId]">
<a-input v-model="editableData[record.projectId]['budget']"
@pressEnter="save(editableData[record.projectId])" type="number"
@change="e => e.target.value = e.target.value.replace(/^0+(\d)|[^\d]+/g, '')" />
<a-icon type="check" @click="save(editableData[record.projectId])"
style="margin-right: 20px;" />
<a-popconfirm title="确定取消?" @confirm="cancel(record.projectId)">
<a-icon type="close" />
</a-popconfirm>
</div>
<div v-else>{{ text }}</div>
</div>
<div v-else class="editable-cell-text-wrapper">
<div v-if="record.children == null">
{{ text || ' ' }}
<a-icon type="edit" @click="edit(record)" />
</div>
<div v-else>
{{ text || ' ' }}
</div>
</div>
</div>
</template>
</a-table>
</a-spin>
<template slot="footer">
<a-button key="back" @click="handleCancel">
关闭
</a-button>
<a-button type="primary" :visible="visibleBtn" :loading="loadingBtn" @click="handleSubmit">
保存
</a-button>
</template>
</a-modal>
</template>
JS
import { cloneDeep } from 'lodash-es';
export default {
data() {
return {
visible: false,
dataSource: [],
newSource: [], //变化的源数据
spinning: true,
columns: [],//头部表单
editableData: {},// 修改的位置数据
editable: false, //是否编辑
visibleBtn: false,
loadingBtn: false,
mode: "add",
titleMap: {
add: '新增',
edit: '修改'
},
}
},
methods: {
//计算比率
// 本月比上月情况=(本月数字-上月数字)/上月数字,如果本月大于上月,结果就是正数,即上升,反之为下降
rateCompute(a, b) {
if (a > 0 && b > 0) {
if (a < b) return ((((b - a) / a) * 100).toFixed(2)) + '%'
else if (b < a) '-' + ((((b - a) / a) * 100).toFixed(2)) + '%'
else return '0%'
} else {
if (a == 0 && b > a) return '100%'
else return '0%'
}
},
edit(row) {
this.editable = true
this.editableData[row.projectId] = cloneDeep(row)
},
save(row) {
this.editable = false
this.calculate(row)
},
// 计算
calculate(row) {
//计算时,需要同步执行
this.fun1(row)
delete this.editableData[row.projectId];
},
fun1(row) {
if (row.inOrOut == 2) {
if(Number(row.budget) > 0){
this.getObj(this.newSource, row.projectId, Number('-' + row.budget), row, callback)
}
else{
this.getObj(this.newSource, row.projectId, Number(row.budget), row, callback)
}
} else {
this.getObj(this.newSource, row.projectId, Number(row.budget), row, callback)
}
// 使用回调,同步执行下面的方法
function callback(isFind, _this, row) {
if (isFind) {
_this.fun2(row)
}
}
},
fun2(row) {
// 获取兄弟节点的父级id
let brotherParentId = row.parentProjectId
// 获取兄弟节点数据
let brotherParentArr = this.getBrotherParentId(this.newSource, brotherParentId)
console.log(brotherParentArr)
if (brotherParentArr.length > 0) {
// 兄弟数组的和 相差值
let ParentVal = brotherParentArr.reduce((accumulator, currentValue) => Number(accumulator) + Number(currentValue));
if (ParentVal) {
this.fun3(row, ParentVal)
}
}
},
fun3(row, ParentVal) {
// 相关连的数据包括原来未改变的初始值
let joinedArr = this.get_level_all(this.dataSource, row.projectId).filter(item => item.projectId != row.projectId)
let _this = this
// 相差值
if (joinedArr.length > 0) {
joinedArr.forEach((item) => {
_this.getParentId(_this.newSource, item.projectId, Number(ParentVal))
});
}
},
// 根据id ,找到当前元素的对象,并进行赋值
getObj(data, id, val, row, callback) {
let isFind = false
data.find((item) => {
if (item.projectId === id) {
if (val == 0) item.budget = '0'
else item.budget = Number(val)
item.promote = this.rateCompute(Number(item.preActual), Number(item.budget))
isFind = true
} else if (item.children != null && item.children.length > 0) {
this.getObj(item.children, id, val, row, callback);
}
});
if (isFind) {
callback(isFind, this, row)
return isFind
}
},
// 根据id,找到所有的上级id
get_level_all(data, id, arr = []) {
data.find((item) => {
if (item.projectId === id) {
arr.push(item);
return true;
} else if (item.children != null && item.children.length > 0) {
arr = this.get_level_all(item.children, id, arr);
if (arr.length) {
arr.push(item);
return true;
} else {
return false;
}
}
return false;
});
return arr;
},
// 根据id,找到所有的同级数据
getBrotherParentId(data, id, arr = []) {
data.find((item) => {
if (item.parentProjectId == id) {
// 收支类型,1:收入,2:支出,3:其它
// 支出:减
if(item.inOrOut == '2'){
if(Number(item.budget)>0){
arr.push((item.inOrOut == '2' ? Number(('-' + item.budget)) : Number(item.budget)));
}else{
arr.push(Number(item.budget));
}
}else{
arr.push(Number(item.budget));
}
} else if (item.children != null && item.children.length > 0) {
this.getBrotherParentId(item.children, id, arr);
}
});
return arr;
},
// 根据相差值遍历相关的父级或祖级
getParentId(arr, id, numDiffer) {
arr.forEach(item => {
if (item.projectId == id) {
item.budget = Number(this.sumChildren(item.children))
item.promote = this.rateCompute(item.preActual, item.budget)
}
if (item.children != null && item.children.length > 0) {
this.getParentId(item.children, id, numDiffer)
}
})
},
// 获取子集得计算和
sumChildren(children) {
let sum = 0
children.map(item => {
if (item.inOrOut == '2' && Number(item.budget) < 0) {
sum += Number(item.budget)
} else {
sum += item.inOrOut == '2' ? Number(('-' + item.budget)) : Number(item.budget)
}
})
return sum
},
// 取消
cancel(key) {
this.editable = false
delete this.editableData[key];
},
//显示
open(mode = 'add', par) {
this.mode = mode;
this.visible = true;
//接口数据,在下面
let headers = res.data.headers
//去除不需要显示的列
headers.map(item => {
if (item.hidden == false) this.operator(item)
})
this.columns.push({
title: '提升比率',
dataIndex: 'promote',
key: 'promote',
align: 'left',
width: 120,
scopedSlots: { customRender: 'promote' }
})
let row = res.data.rows
//树形表格,children没有数据需要为空
this.handNull(row)
this.dataSource = row
this.spinning = false
this.newSource = row
},
// 子集没数据,赋值为空 null
handNull(data) {
data.forEach((item) => {
//计算提升比率
item.promote = this.rateCompute(item.preActual, item.budget)
if (item.children.length == 0) {
item.children = null
} else {
this.handNull(item.children);
}
});
},
// columns 表头赋值
operator(item) {
let obj = {}
if (item.columnId == 'projectName') {
obj['title'] = item.name
obj.dataIndex = 'name'
obj.key = 'name'
obj.align = 'left'
}
else if (item.columnId == 'preActual') {
obj['title'] = item.name
obj.dataIndex = 'preActual'
obj.key = 'preActual'
obj.align = 'left'
obj.width = 200
}
else if (item.columnId == 'budget') {
obj['title'] = item.name
obj.dataIndex = 'budget'
obj.key = 'budget'
obj.align = 'left'
obj.width = 200
obj.scopedSlots = { customRender: 'budget' }
}
else return;
this.columns.push(obj)
},
//关闭
handleCancel() {
this.visible = false;
this.editable = false
this.$emit("close");
},
// 确认
handleSubmit() {
this.loadingBtn = true;
this.visibleBtn = true;
//新增或修改
let Api = this.mode == 'add' ? add : edit
Api(par).then(res => {
if (res.code === 200) {
this.$notification['success']({
message: '提示',
description: '保存成功!',
duration: 8
})
this.visible = false
this.$emit("success");
} else {
this.$notification['error']({
message: '提示',
description: res.message,
duration: 8
})
}
this.visibleBtn = false
this.loadingBtn = false
})
},
}
json
{
"code": 200,
"message": "操作成功",
"data": {
"title": "月度预算",
"tabulatorId": "146",
"storeId": "159",
"storeName": "麓谷公园店",
"tabulator": "李生",
"headers": [{
"columnId": "projectId",
"name": "项目ID",
"hidden": true,
"primary": false
},
{
"columnId": "projectName",
"name": "项目名称",
"hidden": false,
"primary": false
},
{
"columnId": "isLeaf",
"name": "是否叶子",
"hidden": true,
"primary": false
},
{
"columnId": "inOrOut",
"name": "收支类型",
"hidden": true,
"primary": false
},
{
"columnId": "parentProjectId",
"name": "上级预算项目ID",
"hidden": true,
"primary": false
},
{
"columnId": "preActual",
"name": "10月",
"hidden": false,
"primary": false
},
{
"columnId": "budget",
"name": "11月预算",
"hidden": false,
"primary": true
}
],
"rows": [{
"projectId": "165",
"name": "利润",
"parentProjectId": null,
"inOrOut": 1,
"children": [{
"projectId": "174",
"name": "成本",
"parentProjectId": "165",
"inOrOut": 2,
"children": [{
"projectId": "175",
"name": "原材料成本",
"parentProjectId": "174",
"inOrOut": 2,
"children": [],
"budget": "0",
"preBudget": "",
"preActual": "0",
"leaf": true
}],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": false
},
{
"projectId": "173",
"name": "税额",
"parentProjectId": "165",
"inOrOut": 2,
"children": [],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": true
},
{
"projectId": "166",
"name": "营业收入",
"parentProjectId": "165",
"inOrOut": 1,
"children": [{
"projectId": "170",
"name": "外卖折后营收",
"parentProjectId": "166",
"inOrOut": 1,
"children": [{
"projectId": "172",
"name": "外卖优免",
"parentProjectId": "170",
"inOrOut": 2,
"children": [],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": true
},
{
"projectId": "171",
"name": "外卖折前营收",
"parentProjectId": "170",
"inOrOut": 1,
"children": [],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": true
}
],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": false
}],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": false
}
],
"preBudget": "",
"preActual": "0",
"budget": "0",
"leaf": false
}]
}
}
折后盈收=折前盈收-优免