feat : 年度计划生成功能及分摊汇总导出功能

This commit is contained in:
2024-07-06 02:21:26 +08:00
parent fce1708f5c
commit 8180c248ac
11 changed files with 471 additions and 38 deletions

View File

@@ -1,4 +1,6 @@
import request from '@/utils/request.js'
import axios from "axios";
import {getToken} from "@/utils/auth";
export const addAllocation = (data) => {
return request({
@@ -65,3 +67,15 @@ export const deleteAllocation = (id) => {
method: "delete"
});
};
export const shareExportExcel = (allocationId) => {
return axios.get(
`${import.meta.env.VITE_BASE_URL}/workflow/mosr/cost/allocation/collect/${allocationId}`,
{
responseType: 'blob',
headers: {
Authorization: getToken()
}
}
);
};

View File

@@ -54,3 +54,37 @@ export const uploadCollectAttachment= (data) => {
data: data
});
};
// 年度计划
export const addPlan= (data) => {
return request({
url: '/workflow/annual/plan',
method: "post",
data: data
});
};
export const editPlan= (data) => {
return request({
url: '/workflow/annual/plan',
method: "put",
data: data
});
};
export const getPlan= (annualPlanId) => {
return request({
url: `/workflow/annual/plan/info/${annualPlanId}`,
method: "get"
});
};
export const deletePlan= (annualPlanId) => {
return request({
url: `/workflow/annual/plan/${annualPlanId}`,
method: "delete"
});
};
export const approvePlan= (data) => {
return request({
url: '/workflow/annual/plan/approve',
method: "post",
data: data
});
};

View File

@@ -1,5 +1,5 @@
<template>
<el-button color="#DED0B2" style="float: right;margin: 0 10px 10px 0" @click="exportTable">导出</el-button>
<el-button color="#DED0B2" style="float: right;margin: 0 10px 10px 0" @click="exportExcelHandler">导出</el-button>
<el-table ref="table" :data="tableData" style="width: 100%;height: 479px" :show-summary="true" border
:summary-method="getSummaries" v-loading="loading" :header-cell-style="{background:'#f5f7fa'}">
<el-table-column type="index" label="序号" align="center" width="60"/>
@@ -34,8 +34,8 @@
</template>
<script setup>
import {exportExcel} from "@/utils/export-excel";
import {getAllocationSummaryDetails} from "@/api/expense-manage";
import {shareExportExcel} from "@/api/expense-manage";
const tableData = ref([{
id: '12987122',
@@ -80,13 +80,28 @@ const getSummaries = (param) => {
})
return sums
}
const exportTable = () => {
const $e = table.value.$el
let $table = $e.querySelector('.el-table__fixed')
if (!$table) {
$table = $e
}
exportExcel($table, (5 + (Object.keys(tableData.value[0]).length - 5) * 5), "四川省国有资产经营投资管理有限责任公司科技创新项目费用分摊表", 2)
// const exportTable = () => {
// const $e = table.value.$el
// let $table = $e.querySelector('.el-table__fixed')
// if (!$table) {
// $table = $e
// }
// exportExcel($table, (5 + (Object.keys(tableData.value[0]).length - 5) * 5), "四川省国有资产经营投资管理有限责任公司科技创新项目费用分摊表", 2)
// }
const exportExcelHandler = () => {
shareExportExcel(route.query.id).then(res => {
console.log(res)
let reg = /filename=([^&]+)/;
let contentDisposition = decodeURI(res.headers['content-disposition'])
let result = reg.exec(contentDisposition)
let fileName = result[1]
fileName = fileName.replace(/\"/g, '')
const blob = new Blob([res.data])
let a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = fileName
a.click()
})
}
const init = () => {
loading.value = true

View File

@@ -148,7 +148,7 @@
:file-list-show="fileListShow"/>
</el-col>
<el-col :span="24">
<div v-if="data.taskId">
<div v-if="data.taskId||data.state==='5'">
<baseTitle title="审核意见"></baseTitle>
<el-form-item prop="_value">
<el-input
@@ -180,6 +180,11 @@
</div>
</div>
</el-form>
<div class="oper-page-btn" v-if="data.state==='5'">
<el-button type="danger" @click="handleRejectPlan">驳回年度计划</el-button>
<el-button color="#DED0B2" @click="handleAgreePlan">通过年度计划</el-button>
</div>
</div>
</template>
@@ -193,6 +198,8 @@ import {getFundOption} from "@/api/special-fund";
import {useCacheStore} from '@/stores/cache.js'
import {getSubCompOpt} from "@/api/user/user";
import FileComponent from "./FileComponent.vue";
import {ElNotification} from "element-plus";
import {approvePlan} from "@/api/project-demand/summary";
const emit = defineEmits(['update:value'])
const tagsViewStore = useTagsView()
@@ -225,6 +232,7 @@ const props = defineProps({
})
const changeDiagram = ref(false)
const localFormData = ref({})
const route = useRoute()
const router = useRouter()
const fundOption = ref([])
const companyOption = ref([])
@@ -240,6 +248,46 @@ const _value = computed({
emit("update:value", val);
}
})
const handleRejectPlan = async () => {
// const values = form.value.getValues()
console.log('route',route.query.projectId)
if (!_value.value) {
ElNotification({
title: '提示',
message: '请填写审核意见',
type: 'warning'
})
return
}
const params = {
auditOpinion:_value.value,
projectId:parseInt(route.query.projectId),
state:false
}
console.log('params', params)
const res = await approvePlan(params)
ElNotification({
title: '提示',
message: res.msg,
type: res.code === 1000 ? 'success' : 'error'
})
tagsViewStore.delVisitedViews(router.currentRoute.value.path)
}
const handleAgreePlan = async () => {
const params = {
auditOpinion:_value.value,
projectId:parseInt(route.query.projectId),
state:true
}
console.log('params', params)
const res = await approvePlan(params)
ElNotification({
title: '提示',
message: res.msg,
type: res.code === 1000 ? 'success' : 'error'
})
tagsViewStore.delVisitedViews(router.currentRoute.value.path)
}
const filterDict = (data, value) => {
if (data === undefined || value === undefined) return;
let label = ''

View File

@@ -8,20 +8,34 @@ import XLSX from "xlsx-style-vite";
* @param columnLength 列长度
* @param excelName 导出文件名称
* @param bigWidthIndex 更宽列的索引
* @param bigWidthArray 更宽列数组索引
*/
export function exportExcel($table, columnLength, excelName, bigWidthIndex) {
export function exportExcel($table, columnLength, excelName, bigWidthIndex,bigWidthArray) {
//从el-table表生成工作簿对象
//使用原始的格式,保留表格中的格式如%、小数末尾的0等
let workbook = utils.table_to_book($table, {
raw: true
});
//列宽需要导出的表格有多少列这里的i就小于多少
for (let i = 1; i < columnLength; i++) {
if (i === bigWidthIndex) {
workbook.Sheets.Sheet1["!cols"].push({wpx: 300});
// if(bigWidthArray&&bigWidthArray.length>0){
// for (let i = 1; i < columnLength; i++) {
// for (let j = 0; j < bigWidthArray.length; j++) {
// console.log('bigWidthArray[j]',bigWidthArray[j])
// if (i === bigWidthArray[j]) {
// workbook.Sheets.Sheet1["!cols"].push({wpx: 300});
// }
// }
// workbook.Sheets.Sheet1["!cols"].push({wpx: 100});
// }
// }else {
for (let i = 1; i < columnLength; i++) {
if (i === bigWidthIndex) {
workbook.Sheets.Sheet1["!cols"].push({wpx: 300});
}
workbook.Sheets.Sheet1["!cols"].push({wpx: 100});
}
workbook.Sheets.Sheet1["!cols"].push({wpx: 100});
}
// }
//设置单元格样式
for (const key in workbook.Sheets.Sheet1) {
if (

View File

@@ -14,12 +14,12 @@
</el-row>
</el-form>
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="分摊明细" name="first">
<expense-detail/>
</el-tab-pane>
<el-tab-pane label="分摊汇总" name="second" v-loading="loading">
<el-tab-pane label="分摊汇总" name="first" v-loading="loading">
<allocation-summary-detail/>
</el-tab-pane>
<el-tab-pane label="分摊明细" name="second">
<expense-detail/>
</el-tab-pane>
</el-tabs>
<div v-if="shareData.taskId">
<baseTitle title="审核意见"></baseTitle>

190
src/views/plan/detail.vue Normal file
View File

@@ -0,0 +1,190 @@
<template>
<div v-loading="loading">
<el-form :model="formData" class="query-form">
<el-row>
<el-col :span="8">
<el-form-item label="年度计划名称" prop="annualPlanName">
<!-- <el-input v-model="formData.annualPlanName" placeholder="请输入年度计划名称" clearable>-->
<!-- </el-input>-->
<span>{{ formData.annualPlanName }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="创建时间" prop="createTime">
<span>{{ formData.createTime }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="query-btn">
<el-button color="#DED0B2" style="float: right;margin: 0 10px 10px 0" @click="exportTable">导出</el-button>
</div>
<el-table ref="table" :data="formData.projectList" border :header-cell-style="{background:'#f5f7fa'}">
<el-table-column label="四川省国有资产经营投资管理有限责任公司科技创新项目年度计划表" align="center">
<template #default="scope">
<el-table-column type="index" label="序号" align="center" width="60"/>
<el-table-column prop="projectName" label="项目名称" align="center"/>
<el-table-column prop="time" label="起始时间" align="center" min-width="120px">
<template #default="scope">
{{ scope.row.startTime + ' 至 ' + scope.row.endTime }}
</template>
</el-table-column>
<el-table-column prop="projectType" label="项目类型" align="center">
<template #default="scope">
<div v-if="scope.row.projectType !== null">
<Tag dictType="project_type" :value="scope.row.projectType"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="investmentType" label="出资类型" align="center">
<template #default="scope">
<div v-if="scope.row.investmentType !== null">
<Tag dictType="invest_type" :value="scope.row.investmentType"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="projectImpact" label="项目影响" align="center">
<template #default="scope">
<div v-if="scope.row.projectImpact !== null">
<Tag dictType="project_impact" :value="scope.row.projectImpact"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="businessSegment" label="所属业务板块" align="center">
<template #default="scope">
<div v-if="scope.row.businessSegment !== null">
<Tag dictType="business_segment" :value="scope.row.businessSegment"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="resultForm" label="预期成果形式" align="center" min-width="100px">
<template #default="scope">
<div v-if="scope.row.resultForm !== null">
<el-tag effect="plain">{{
filterDict(cacheStore.getDict('result_form'), scope.row.resultForm)
}}
</el-tag>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="technicalStandard" label="预期技术标准制定" align="center">
<template #default="scope">
<div v-if="scope.row.technicalStandard !== null">
<Tag dictType="technical_standard" :value="scope.row.technicalStandard"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="industryUniversityResearch" label="产学研联合" align="center">
<template #default="scope">
<div v-if="scope.row.industryUniversityResearch !== null">
<Tag dictType="industry_university" :value="scope.row.industryUniversityResearch"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="governmentDeclaration" label="开展政府申报" align="center">
<template #default="scope">
<div v-if="scope.row.governmentDeclaration !== null">
<Tag dictType="government_declaration" :value="scope.row.governmentDeclaration"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="intellectualProperty" label="知识产权状况" align="center">
<template #default="scope">
<div v-if="scope.row.intellectualProperty !== null">
<Tag dictType="intellectual_property" :value="scope.row.intellectualProperty"/>
</div>
<div v-else>--</div>
</template>
</el-table-column>
<el-table-column prop="intellectualProperty" label="经济概算(元)" align="center">
<template #default="scope">
<span>{{ toThousands(scope.row.economicEstimate) }}</span>
</template>
</el-table-column>
<el-table-column prop="specialFundAmount" label="专项资金(元)" align="center">
<template #default="scope">
<span>{{ toThousands(scope.row.specialFundAmount) }}</span>
</template>
</el-table-column>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import {toThousands} from '@/utils/changePrice.js'
import {ElNotification} from "element-plus";
import {getPlan} from "@/api/project-demand/summary";
import {useCacheStore} from '@/stores/cache.js'
import {exportExcel} from "@/utils/export-excel";
const cacheStore = useCacheStore()
const loading = ref(false)
const formData = ref({})
const table = ref()
const route = useRoute()
const exportTable = () => {
const $e = table.value.$el
let $table = $e.querySelector('.el-table__fixed')
if (!$table) {
$table = $e
}
exportExcel($table, (5 + (Object.keys(formData.value.projectList[0]).length - 5) * 5), "四川省国有资产经营投资管理有限责任公司科技创新项目年度计划表", 2)
}
const filterDict = (data, value) => {
if (data === undefined || value === undefined) return;
let label = ''
let result = []
if (JSON.parse(value) instanceof Array) {
JSON.parse(value).forEach(item1 => {
data.find(item => {
if (item.value == item1) {
result.push(item.label)
}
})
})
label = result.map(item => item).join('')
} else {
if (data instanceof Array) {
data.find(item => {
if (item.value == value) {
label = item.label
}
})
}
}
return label
}
const getDetailInfo = () => {
loading.value = true
getPlan(route.query.annualPlanId).then(res => {
ElNotification({
title: '提示',
message: res.msg,
type: res.code === 1000 ? 'success' : 'error'
})
if (res.code === 1000) {
formData.value = res.data
loading.value = false
}
})
}
onMounted(() => {
if (route.query.annualPlanId) {
getDetailInfo()
}
})
</script>
<style scoped>
</style>

76
src/views/plan/index.vue Normal file
View File

@@ -0,0 +1,76 @@
<template>
<!-- <fvSearchForm :searchConfig="searchConfig" @search="search"></fvSearchForm>-->
<fvTable ref="tableIns" :tableConfig="tableConfig" @headBtnClick="headBtnClick"></fvTable>
</template>
<script setup lang="jsx">
const tableIns = ref()
const router = useRouter()
const route = useRoute()
const tableConfig = reactive({
columns: [
// {
// type: 'selection',
// prop: 'selection'
// },
{
prop: 'annualPlanName',
label: '年度计划名称',
align: 'center'
},
{
prop: 'createTime',
label: '创建时间',
align: 'center'
},
{
prop: 'oper',
label: '操作',
align: 'center',
fixed: 'right',
showOverflowTooltip: false,
currentRender: ({row, index}) => {
let btn = []
btn.push({label: '详情', func: () => handleDetail(row), type: 'primary'})
return (
<div style={{width: '100%'}}>
{
btn.map(item => (
<el-button
type={item.type}
// v-perm={item.prem}
onClick={() => item.func()}
link
>
{item.label}
</el-button>
))
}
</div>
)
}
}
],
api: '/workflow/annual/plan/list',
params: {},
})
const headBtnClick = (key) => {
switch (key) {
case 'add':
handleAdd()
break;
}
}
const handleDetail=(row)=>{
router.push({
name: 'Plan/detail',
query: {
annualPlanId: row.annualPlanId,
}
})
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -62,8 +62,10 @@ const getInfo = async (state) => {
processStore.passList.value = data.passList;
nextTick(() => {
summaryProcessViewer.value = true
if (data.formPermMap["fileList"]) {
fileListShow.value = data.formPermMap["fileList"].perm
if(data.formPermMap!=null){
if (data.formPermMap["fileList"]) {
fileListShow.value = data.formPermMap["fileList"].perm
}
}
})
loading.close()

View File

@@ -1,13 +1,16 @@
<template>
<fvSearchForm :searchConfig="searchConfig" @search="search"></fvSearchForm>
<fvTable ref="tableIns" :tableConfig="tableConfig" @headBtnClick="headBtnClick"></fvTable>
<fvTable ref="tableIns" :tableConfig="tableConfig" @headBtnClick="headBtnClick"
@selectionChange="selectionChange"></fvTable>
</template>
<script setup lang="jsx">
import fvSelect from '@/fvcomponents/fvSelect/index.vue'
import {addPlan} from "@/api/project-demand/summary";
const tableIns = ref()
const router = useRouter()
const projectSelectList = ref([])
const shortcuts = [
{
text: '上周',
@@ -104,7 +107,7 @@ const searchConfig = reactive([
props: {
placeholder: '请选择状态',
clearable: true,
cacheKey: 'project_initiation'
cacheKey: 'demand_summary'
}
},
// {
@@ -146,10 +149,10 @@ const auths = {
}
const tableConfig = reactive({
columns: [
// {
// type: 'selection',
// prop: 'selection'
// },
{
type: 'selection',
prop: 'selection'
},
{
prop: 'requirementName',
label: '征集名称',
@@ -229,7 +232,7 @@ const tableConfig = reactive({
prop: 'oper',
label: '操作',
align: 'center',
fixed:'right',
fixed: 'right',
showOverflowTooltip: false,
currentRender: ({row, index}) => {
let btn = []
@@ -254,12 +257,6 @@ const tableConfig = reactive({
</el-button>
))
}
{
buttons.has("delete") ?
<popover-delete name={row.requirementName} type={'需求'} btnType={'danger'}
perm={['mosr:collect:del']} onDelete={() => handleDelete(row)}/>
: ''
}
</div>
)
}
@@ -268,19 +265,48 @@ const tableConfig = reactive({
api: '/workflow/mosr/requirement/collect',
params: {},
btns: [
{name: '新增上报', key: 'add', color: '#DED0B2', auth:auths.report},
// {name: '年度计划导出', key: '_export', auth: ''},
{name: '新增上报', key: 'add', color: '#DED0B2', auth: auths.report},
{name: '创建年度计划', key: 'export', color: '#DED0B2', auth: ''},
// {name: '经费预算生成', key: 'preMonty', auth: ''},
]
})
// {
// buttons.has("delete") ?
// <popover-delete name={row.requirementName} type={'需求'} btnType={'danger'}
// perm={['mosr:collect:del']} onDelete={() => handleDelete(row)}/>
// : ''
// }
const headBtnClick = (key) => {
switch (key) {
case 'add':
handleAdd()
break;
case 'export':
handleExport()
break;
}
}
const selectionChange = (data) => {
console.log('data', data)
projectSelectList.value = data
}
const handleExport = () => {
console.log('projectSelectList.value', projectSelectList.value)
let projectIds=[]
projectSelectList.value.map(item=>{
projectIds.push(item.projectId)
})
if (projectSelectList.value.length > 0) {
let params = {
projectIds: projectIds
}
addPlan(params).then(res => {
if (res.code === 1000) {
}
})
}
}
const search = (val) => {
let obj = {...val}
if (obj.dateValue) {
@@ -313,7 +339,7 @@ const handleDetail = (row) => {
router.push({
name: 'Summary/detail',
query: {
id: row.requirementId==null?'-1':row.requirementId,
id: row.requirementId == null ? '-1' : row.requirementId,
projectId: row.projectId,
state: row.state
}

View File

@@ -82,6 +82,13 @@ const init = () => {
context: '审批进行中'
}
break
case '5':
timeline.value = {
color: '#f78f5f',
icon: 'MoreFilled',
context: '年度计划审批中'
}
break
case '2':
timeline.value = {
color: '#0bbd87',
@@ -103,6 +110,13 @@ const init = () => {
context: '审批通过'
}
break
case '6':
timeline.value = {
color: '#0bbd87',
icon: 'CircleCheckFilled',
context: '年度计划审批通过'
}
break
default:
break
}