feat: 分摊明细异构表格数据对接 + 分摊明细异构表格excel导出

This commit is contained in:
2024-06-20 02:27:42 +08:00
parent f3e8e12a5a
commit 994e474532
5 changed files with 264 additions and 11468 deletions

View File

@@ -25,9 +25,9 @@ steps:
- npm -v
- mkdir -p ./node_modules
- export NODE_MODULES_PATH=`pwd`/node_modules
# - npm config set registry https://registry.npmmirror.com
#- set NODE_OPTIONS=--openssl-legacy-provider
# - npm install
- npm config set registry https://registry.npmmirror.com
- set NODE_OPTIONS=--openssl-legacy-provider
- npm install
- npm run build
- ls /app/build/$DRONE_REPO_NAME/
- echo $NODE_MODULES_PATH

11348
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
"d3": "^7.8.5",
"echarts": "^5.4.2",
"element-plus": "^2.6.0",
"file-saver": "^2.0.5",
"highlight.js": "9.18.5",
"jquery": "^3.6.0",
"js-cookie": "^3.0.5",
@@ -30,7 +31,9 @@
"vue-codemirror": "^6.1.1",
"vue-json-viewer": "^3.0.4",
"vue-router": "^4.1.6",
"vuedraggable": "^4.1.0"
"vuedraggable": "^4.1.0",
"xlsx": "^0.18.5",
"xlsx-style-vite": "^0.0.2"
},
"devDependencies": {
"@codemirror/lang-java": "^6.0.1",

View File

@@ -13,6 +13,12 @@ export const getAllocationDetail = (allocationId) => {
method: "get"
});
};
export const getAllocationDetails = (allocationId) => {
return request({
url: `/workflow/mosr/cost/allocation/usr/detail`,
method: "get"
});
};
export const getAllocationProcess = () => {
return request({
url: '/workflow/mosr/cost/allocation/process',

View File

@@ -1,144 +1,279 @@
<template>
<div>
<fvSearchForm :searchConfig="searchConfig" @search="search"></fvSearchForm>
<fvTable ref="tableIns" :tableConfig="tableConfig">
<template #empty>
<el-empty description="暂无数据"/>
<el-table ref="reportTable" :data="tableData" style="width: 100%;height: 300px" :span-method="objectSpanMethod" v-loading="loading">
<!-- <el-table-column label="四川省国有资产经营投资管理有限责任公司-->
<!-- 科技创新项目人工成本分摊明细表" align="center">-->
<el-table-column v-for="column in columnInfo" :prop="column.prop" :label="column.label" align="center"
: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>
</fvTable>
</div>
</el-table-column>
<!-- </el-table-column>-->
</el-table>
<el-button @click="exportExcel">导出</el-button>
</template>
<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 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 reportTable = ref({});
const columnInfo = ref([])
const monthConcat = new Map()
const tableData = ref([])
const loading = ref(false)
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 res = await getResearchUser()
researchOptions.value = res.data
}
const getResearchName=(id)=>{
if(!id)return;
let label=''
researchOptions.value.forEach(item=>{
if(item.value==id){
label=item.label
}
})
const getResearchName = (id) => {
let label = ''
if (id !== undefined) {
researchOptions.value.forEach(item => {
if (item.value == id) {
label = item.label
}
})
}
return label
}
const search = (val) => {
tableConfig.params = {
allocationId:route.query.id,
allocationId: route.query.id,
...val
}
tableIns.value.refresh()
}
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>
<style scoped>
:deep(.el-table--fit ){
:deep(.el-table--fit ) {
width: 100%;
height: 400px!important;
height: 479px !important;
}
</style>