feat: 分摊明细异构表格数据对接 + 分摊明细异构表格excel导出
This commit is contained in:
@@ -25,9 +25,9 @@ steps:
|
|||||||
- npm -v
|
- npm -v
|
||||||
- mkdir -p ./node_modules
|
- mkdir -p ./node_modules
|
||||||
- export NODE_MODULES_PATH=`pwd`/node_modules
|
- export NODE_MODULES_PATH=`pwd`/node_modules
|
||||||
# - npm config set registry https://registry.npmmirror.com
|
- npm config set registry https://registry.npmmirror.com
|
||||||
#- set NODE_OPTIONS=--openssl-legacy-provider
|
- set NODE_OPTIONS=--openssl-legacy-provider
|
||||||
# - npm install
|
- npm install
|
||||||
- npm run build
|
- npm run build
|
||||||
- ls /app/build/$DRONE_REPO_NAME/
|
- ls /app/build/$DRONE_REPO_NAME/
|
||||||
- echo $NODE_MODULES_PATH
|
- echo $NODE_MODULES_PATH
|
||||||
|
|||||||
11348
package-lock.json
generated
11348
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@
|
|||||||
"d3": "^7.8.5",
|
"d3": "^7.8.5",
|
||||||
"echarts": "^5.4.2",
|
"echarts": "^5.4.2",
|
||||||
"element-plus": "^2.6.0",
|
"element-plus": "^2.6.0",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"highlight.js": "9.18.5",
|
"highlight.js": "9.18.5",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
@@ -30,7 +31,9 @@
|
|||||||
"vue-codemirror": "^6.1.1",
|
"vue-codemirror": "^6.1.1",
|
||||||
"vue-json-viewer": "^3.0.4",
|
"vue-json-viewer": "^3.0.4",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vuedraggable": "^4.1.0"
|
"vuedraggable": "^4.1.0",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
|
"xlsx-style-vite": "^0.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@codemirror/lang-java": "^6.0.1",
|
"@codemirror/lang-java": "^6.0.1",
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ export const getAllocationDetail = (allocationId) => {
|
|||||||
method: "get"
|
method: "get"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
export const getAllocationDetails = (allocationId) => {
|
||||||
|
return request({
|
||||||
|
url: `/workflow/mosr/cost/allocation/usr/detail`,
|
||||||
|
method: "get"
|
||||||
|
});
|
||||||
|
};
|
||||||
export const getAllocationProcess = () => {
|
export const getAllocationProcess = () => {
|
||||||
return request({
|
return request({
|
||||||
url: '/workflow/mosr/cost/allocation/process',
|
url: '/workflow/mosr/cost/allocation/process',
|
||||||
|
|||||||
@@ -1,144 +1,279 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<el-table ref="reportTable" :data="tableData" style="width: 100%;height: 300px" :span-method="objectSpanMethod" v-loading="loading">
|
||||||
<fvSearchForm :searchConfig="searchConfig" @search="search"></fvSearchForm>
|
<!-- <el-table-column label="四川省国有资产经营投资管理有限责任公司-->
|
||||||
<fvTable ref="tableIns" :tableConfig="tableConfig">
|
<!-- 科技创新项目人工成本分摊明细表" align="center">-->
|
||||||
<template #empty>
|
<el-table-column v-for="column in columnInfo" :prop="column.prop" :label="column.label" align="center"
|
||||||
<el-empty description="暂无数据"/>
|
:fixed="(!column.children) ? ((column.prop === 'totalSeparation' || column.prop === 'totalSeparationCost') ? 'right' : 'left') : false"
|
||||||
|
:width="(column.prop === 'totalSeparation' || column.prop === 'totalSeparationCost') ? 160:130">
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="column.children">
|
||||||
|
<el-table-column v-for="childColumn in column.children"
|
||||||
|
:prop="column.prop + '.'+ childColumn.prop"
|
||||||
|
:label="childColumn.label"
|
||||||
|
:width="childColumn.prop === 'subtotal' ? 160 : 130">
|
||||||
|
<template #default="columnScope">
|
||||||
|
{{ columnScope.row[column.prop][childColumn.prop] }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<template v-if="column.prop === 'totalSeparation' || column.prop === 'totalSeparationCost'">
|
||||||
|
{{ getTotalSeparation(scope.row, column.prop) }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ scope.row[column.prop] }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</fvTable>
|
</el-table-column>
|
||||||
</div>
|
<!-- </el-table-column>-->
|
||||||
|
</el-table>
|
||||||
|
<el-button @click="exportExcel">导出</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="jsx">
|
<script setup lang="jsx">
|
||||||
import {getResearchUser} from "@/api/expense-manage";
|
import {getResearchUser, getAllocationDetailList, getAllocationDetails} from "@/api/expense-manage";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
// import * as XLSX from "xlsx";
|
||||||
|
import {utils} from "xlsx";
|
||||||
|
import FileSaver from 'file-saver'
|
||||||
|
import XLSX from "xlsx-style-vite";
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const searchConfig = reactive([
|
|
||||||
{
|
|
||||||
label: '项目名称',
|
|
||||||
prop: 'projectName',
|
|
||||||
component: 'el-input',
|
|
||||||
props: {
|
|
||||||
placeholder: '请输入项目名称查询',
|
|
||||||
clearable: true,
|
|
||||||
filterable: true,
|
|
||||||
checkStrictly: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '姓名',
|
|
||||||
prop: 'researchPersonnel',
|
|
||||||
component: 'el-input',
|
|
||||||
props: {
|
|
||||||
placeholder: '请输入姓名查询',
|
|
||||||
clearable: true,
|
|
||||||
filterable: true,
|
|
||||||
checkStrictly: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// label: '起始时间',
|
|
||||||
// prop: 'time',
|
|
||||||
// component: 'el-date-picker',
|
|
||||||
// props: {
|
|
||||||
// placeholder: '请选择起始时间',
|
|
||||||
// clearable: true,
|
|
||||||
// },
|
|
||||||
// colProps: {}
|
|
||||||
// },
|
|
||||||
])
|
|
||||||
const tableConfig = reactive({
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
prop: 'index',
|
|
||||||
type: 'index',
|
|
||||||
label: '序号',
|
|
||||||
align: 'center',
|
|
||||||
width: '80',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'projectName',
|
|
||||||
label: '项目名称',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'researchPersonnel',
|
|
||||||
label: '研发人员',
|
|
||||||
align: 'center',
|
|
||||||
currentRender:({row})=>{
|
|
||||||
return <span>{getResearchName(row.researchPersonnel)}</span>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'wagesPayable',
|
|
||||||
label: '应发工资',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'performance',
|
|
||||||
label: '绩效',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'reserveFund',
|
|
||||||
label: '公积金',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'socialSecurity',
|
|
||||||
label: '社保',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'annuity',
|
|
||||||
label: '年金',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'workday',
|
|
||||||
label: '工作日(天)',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prop: 'researchDuration',
|
|
||||||
label: '研发时长(天)',
|
|
||||||
align: 'center'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
api:'/workflow/mosr/cost/allocation/usr',
|
|
||||||
params:{
|
|
||||||
allocationId:route.query.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const tableIns = ref()
|
const tableIns = ref()
|
||||||
|
const reportTable = ref({});
|
||||||
|
const columnInfo = ref([])
|
||||||
|
const monthConcat = new Map()
|
||||||
|
const tableData = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
const researchOptions = ref([])
|
const researchOptions = ref([])
|
||||||
|
const objectSpanMethod = ({row, column, rowIndex, columnIndex}) => {
|
||||||
|
if (columnIndex === 0) {
|
||||||
|
if (monthConcat.has(rowIndex)) {
|
||||||
|
return {
|
||||||
|
rowspan: monthConcat.get(rowIndex),
|
||||||
|
colspan: 1,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
rowspan: 0,
|
||||||
|
colspan: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let length = Object.keys(row).length
|
||||||
|
if (length > 5) {
|
||||||
|
if (concatColumn(columnIndex, length)) {
|
||||||
|
if (rowIndex % 5 === 0) {
|
||||||
|
return {
|
||||||
|
rowspan: 5,
|
||||||
|
colspan: 1,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
rowspan: 0,
|
||||||
|
colspan: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const getTotalSeparation = (row, prop) => {
|
||||||
|
let totalSeparation = 0.00
|
||||||
|
for (let key of Object.keys(row)) {
|
||||||
|
if (key.startsWith('personInfo')) {
|
||||||
|
let value = prop === 'totalSeparation' ? row[key].separationAmount : row[key].subtotal
|
||||||
|
if ("/" !== value) {
|
||||||
|
try {
|
||||||
|
totalSeparation += parseFloat(value)
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (totalSeparation !== 0) {
|
||||||
|
return totalSeparation;
|
||||||
|
} else {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const concatColumn = (columnIndex, length) => {
|
||||||
|
let columnLength = 5 + (length - 5) * 5
|
||||||
|
if (columnIndex === 1
|
||||||
|
|| columnIndex === columnLength - 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < length - 5; i++) {
|
||||||
|
if (columnIndex === 4 + (i * 5)
|
||||||
|
|| columnIndex === 5 + (i * 5)
|
||||||
|
|| columnIndex === 7 + (i * 5)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const init = () => {
|
||||||
|
loading.value = true
|
||||||
|
getAllocationDetails().then(res => {
|
||||||
|
columnInfo.value = res.data.columns
|
||||||
|
let tableDataLet = res.data.tableData;
|
||||||
|
tableData.value = []
|
||||||
|
let rowIndex = 0;
|
||||||
|
tableDataLet.forEach((tableDatum, index) => {
|
||||||
|
let rowspan = tableDatum.rows.length * 5
|
||||||
|
monthConcat.set(rowIndex, rowspan)
|
||||||
|
rowIndex += rowspan
|
||||||
|
for (const tableDatumElement of tableDatum.rows) {
|
||||||
|
tableData.value = tableData.value.concat(tableDatumElement)
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getResearchOptions = async () => {
|
const getResearchOptions = async () => {
|
||||||
const res = await getResearchUser()
|
const res = await getResearchUser()
|
||||||
researchOptions.value = res.data
|
researchOptions.value = res.data
|
||||||
}
|
}
|
||||||
const getResearchName=(id)=>{
|
const getResearchName = (id) => {
|
||||||
if(!id)return;
|
let label = ''
|
||||||
let label=''
|
if (id !== undefined) {
|
||||||
researchOptions.value.forEach(item=>{
|
researchOptions.value.forEach(item => {
|
||||||
if(item.value==id){
|
if (item.value == id) {
|
||||||
label=item.label
|
label = item.label
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
return label
|
return label
|
||||||
}
|
}
|
||||||
const search = (val) => {
|
const search = (val) => {
|
||||||
tableConfig.params = {
|
tableConfig.params = {
|
||||||
allocationId:route.query.id,
|
allocationId: route.query.id,
|
||||||
...val
|
...val
|
||||||
}
|
}
|
||||||
tableIns.value.refresh()
|
tableIns.value.refresh()
|
||||||
}
|
}
|
||||||
getResearchOptions()
|
getResearchOptions()
|
||||||
|
init()
|
||||||
|
|
||||||
|
|
||||||
|
const exportExcel = () => {
|
||||||
|
//如果导出后多出空白行则修改rowspan,否则可不用
|
||||||
|
// var tds = document.querySelectorAll(".el-table__footer td");
|
||||||
|
// tds.forEach(td => td.setAttribute("rowspan", "1"));
|
||||||
|
const $e = reportTable.value.$el
|
||||||
|
let $table = $e.querySelector('.el-table__fixed')
|
||||||
|
if (!$table) {
|
||||||
|
$table = $e
|
||||||
|
}
|
||||||
|
//从el-table表生成工作簿对象
|
||||||
|
//使用原始的格式,保留表格中的格式如%、小数末尾的0等
|
||||||
|
let workbook = utils.table_to_book($table, {
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
//列宽,需要导出的表格有多少列这里的i就小于多少
|
||||||
|
let keys = Object.keys(tableData.value[0]).length
|
||||||
|
for (let i = 1; i < (5 + (keys - 5) * 5); i++) {
|
||||||
|
workbook.Sheets.Sheet1["!cols"].push({wpx: 100});
|
||||||
|
}
|
||||||
|
//设置单元格样式
|
||||||
|
for (const key in workbook.Sheets.Sheet1) {
|
||||||
|
if (
|
||||||
|
key !== "!cols" &&
|
||||||
|
key !== "!fullref" &&
|
||||||
|
key !== "!merges" &&
|
||||||
|
key !== "!ref" &&
|
||||||
|
key !== "!rows"
|
||||||
|
) {
|
||||||
|
//这里的s就是具体的样式,如果想设置不一样的样式可以看xlsx-style文档
|
||||||
|
workbook.Sheets.Sheet1[key].s = {
|
||||||
|
//边框
|
||||||
|
border: {
|
||||||
|
top: {style: "thin"},
|
||||||
|
bottom: {style: "thin"},
|
||||||
|
left: {style: "thin",},
|
||||||
|
right: {style: "thin",}
|
||||||
|
},
|
||||||
|
//对齐
|
||||||
|
alignment: {
|
||||||
|
horizontal: "center",
|
||||||
|
vertical: "center",
|
||||||
|
wrapText: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//修改合并单元格样式
|
||||||
|
let arr = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||||
|
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
|
||||||
|
//由于导出时合并单元格只识别左上角的单元格,合并单元格中其他单元格
|
||||||
|
//并不会存在,所以需要识别合并单元格中除左上角单元格外的单元格并添加
|
||||||
|
//带样式的单元格到其中,不理解可以看四中的第2点。
|
||||||
|
for (let item of workbook.Sheets.Sheet1["!merges"]) {
|
||||||
|
let style = {
|
||||||
|
border: {
|
||||||
|
top: {style: "thin"},
|
||||||
|
bottom: {style: "thin"},
|
||||||
|
left: {style: "thin",},
|
||||||
|
right: {style: "thin",}
|
||||||
|
},
|
||||||
|
alignment: {
|
||||||
|
horizontal: "center",
|
||||||
|
vertical: "center",
|
||||||
|
wrapText: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let merge_s = {t: "s", v: "", s: style};
|
||||||
|
if (item.s.c === item.e.c) {
|
||||||
|
//纵向合并,其中c为字母r为数字
|
||||||
|
let star = item.s.r;
|
||||||
|
let end = item.e.r;
|
||||||
|
for (let i = star + 1; i <= end; i++) {
|
||||||
|
workbook.Sheets.Sheet1[arr[item.s.c] + (i + 1)] = merge_s;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//横向合并
|
||||||
|
let star = item.s.c;
|
||||||
|
let end = item.e.c;
|
||||||
|
for (let i = star; i < end; i++) {
|
||||||
|
workbook.Sheets.Sheet1[arr[i + 1] + Number(item.s.r + 1)] = merge_s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//将表格数据中的字符串转ArrayBuffer
|
||||||
|
function s2ab(s) {
|
||||||
|
let buf = new ArrayBuffer(s.length);
|
||||||
|
let view = new Uint8Array(buf);
|
||||||
|
for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
//这里的属性可以参考xlsx-style文档
|
||||||
|
let wbout = XLSX.write(workbook, {
|
||||||
|
bookType: "xlsx",
|
||||||
|
bookSST: false,
|
||||||
|
type: "binary"
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
FileSaver.saveAs(
|
||||||
|
new Blob([s2ab(wbout)], {type: "application/octet-stream"}),
|
||||||
|
"四川省国有资产经营投资管理有限责任公司科技创新项目人工成本分摊明细表.xlsx"
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (typeof console !== "undefined") console.log(e, wbout);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
:deep(.el-table--fit ){
|
:deep(.el-table--fit ) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 400px!important;
|
height: 479px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user