Commit 8c8efd1e authored by 高滢's avatar 高滢

feat(招标计划): 填报统计

parent a51ef495
......@@ -10,14 +10,26 @@ enum Api {
SelectCount = '/pro/tenderYearManage/selectCount',
GetListAll = '/pro/tenderYearManage/statistics',
Export = '/pro/tenderYearManage/export',
Import = '/pro/tenderYearManage/import',
}
export const selectPageList = (params?: any) =>
defHttp.post({ url: Api.GetPageList, data: params });
export const addProject = (params?: any) => defHttp.post({ url: Api.AddProject, data: params });
export const addProject = (params?: any) =>
defHttp.post({
url: Api.AddProject,
data: params,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
export const updateProject = (params?: any) =>
defHttp.post({ url: Api.UpdateProject, data: params });
defHttp.post({
url: Api.UpdateProject,
data: params,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
export const deleteProject = (params?: any) =>
defHttp.get({ url: Api.DeleteProject, data: params });
......@@ -27,4 +39,8 @@ export const handelExamine = (params?: any) => defHttp.get({ url: Api.Audit, dat
export const getCount = (params?: any) => defHttp.post({ url: Api.SelectCount, data: params });
export const selectListAll = (params?: any) => defHttp.post({ url: Api.GetListAll, data: params });
export const exportTender = (params?: any) => defHttp.post({ url: Api.Export, data: params });
export const exportTender = (params?: any) =>
defHttp.post(
{ url: Api.Export, data: params, responseType: 'blob' },
{ errorMessageMode: 'none', isTransformResponse: false },
);
import { defHttp } from '@/utils/http/axios';
enum Api {
GetPageList = '/pro/tenderYearPlan/page',
AddProject = '/pro/tenderYearPlan/add',
UpdateProject = '/pro/tenderYearPlan/edit',
DeleteProject = '/pro/tenderYearPlan/del',
ProjectDetail = '/pro/tenderYearPlan/details',
Audit = '/pro/tenderYearPlan/examine',
SelectCount = '/pro/tenderYearPlan/count',
GetListAll = '/pro/tenderYearPlan/statistics',
Export = '/pro/tenderYearPlan/export',
}
export const selectPageList = (params?: any) =>
defHttp.post({ url: Api.GetPageList, data: params });
export const addProject = (params?: any) =>
defHttp.post({
url: Api.AddProject,
data: params,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
export const updateProject = (params?: any) =>
defHttp.post({
url: Api.UpdateProject,
data: params,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
export const deleteProject = (params?: any) =>
defHttp.get({ url: Api.DeleteProject, data: params });
export const selectDetail = (params?: any) => defHttp.get({ url: Api.ProjectDetail, data: params });
export const handelExamine = (params?: any) => defHttp.get({ url: Api.Audit, data: params });
export const getCount = (params?: any) => defHttp.post({ url: Api.SelectCount, data: params });
export const selectListAll = (params?: any) => defHttp.post({ url: Api.GetListAll, data: params });
export const exportTender = (params?: any) =>
defHttp.post(
{ url: Api.Export, data: params, responseType: 'blob' },
{ errorMessageMode: 'none', isTransformResponse: false },
);
......@@ -94,6 +94,16 @@ const biddingPlan: AppRouteModule = {
orderNo: 5,
},
},
{
path: 'tendeYearManage',
name: 'TendeYearManage',
component: () => import('@/views/tendeYearManage/index.vue'),
meta: {
auth: '/www/dist/index.html#/biddingManagement/index',
title: '年度招标管理填报',
orderNo: 5,
},
},
{
path: 'tendeYearManageSta',
name: 'TendeYearManageSta',
......@@ -105,13 +115,36 @@ const biddingPlan: AppRouteModule = {
},
},
{
path: 'tendeYearManage',
name: 'TendeYearManage',
component: () => import('@/views/tendeYearManage/index.vue'),
path: 'tendeYearPlanEdit',
name: 'TendeYearPlanEdit',
component: () => import('@/views/tenderYearPlan/edit.vue'),
meta: {
hideTab: true,
hideMenu: true,
hideBreadcrumb: true,
auth: '/www/dist/index.html#/biddingManagement/index',
title: '年度招标管理填报',
orderNo: 5,
title: '年度招标计划填报',
orderNo: 6,
},
},
{
path: 'tendeYearPlan',
name: 'TendeYearPlan',
component: () => import('@/views/tenderYearPlan/index.vue'),
meta: {
auth: '/www/dist/index.html#/biddingManagement/index',
title: '年度招标计划填报',
orderNo: 7,
},
},
{
path: 'tendeYearPlanSta',
name: 'TendeYearMPlanSta',
component: () => import('@/views/tenderYearPlan/statistics.vue'),
meta: {
auth: '/www/dist/index.html#/biddingManagement/index',
title: '年度招标计划统计',
orderNo: 7,
},
},
],
......
......@@ -46,7 +46,7 @@
accept=".xlsx,.xls"
:on-success="uploadFile"
>
<a-button v-if="!disabled" type="dashed" @click="excelAdd" class="ml-2" preIcon="ei:plus">
<a-button v-if="!disabled" type="dashed" class="ml-2" preIcon="ei:plus">
Excel导入
</a-button>
</Upload>
......
......@@ -157,7 +157,7 @@
import { useUserStore } from '@/store/modules/user';
import PageCard from '@/components/Page/src/PageCard.vue';
import CycleModel from '@/views/monthlyPlan/cycleModel/cycleModel.vue';
import FillDetailsModel from '@/views/biddingManagement/fillDetailsModel/fillDetailsModel.vue';
import FillDetailsModel from '@/views/monthlyPlan/fillDetailsModel/fillDetailsModel.vue';
import { Divider, Row, Col } from 'ant-design-vue';
import {getMothCycel, SetEditStatus} from "@/api/project/monthlyPlan";
......@@ -243,11 +243,15 @@
function selectCycle() {
openCycleModel(true, {
data: mothCycle.value,
type: 'year',
id: 11,
});
}
function getFillDetails() {
openFillDetailModel(true, {
data: mothCycle.value,
url: '/pro/monthEngineer/businessComDetails',
module: 'TenderMan',
});
}
function handleCreate() {
......
......@@ -106,54 +106,31 @@
loadingRef.value = true;
let data = await selectListAll(params.value);
let propsList = ['companyName', 'projectName'];
let propsList = ['companyName'];
propsList.map((item) => {
changeData(data, item);
});
loadingRef.value = false;
}
function changeData(data, field) {
if (field == 'companyName') {
let count = 0; //重复项的第一项
let indexCount = 1; //下一项
while (indexCount < data.length) {
let item = data.slice(count, count + 1)[0]; //获取没有比较的第一个对象
if (!item[`${field}rowSpan`]) {
item[`${field}rowSpan`] = 1; //初始化为1
}
if (item[field] === data[indexCount][field]) {
//第一个对象与后面的对象相比,有相同项就累加,并且后面相同项设置为0
item[`${field}rowSpan`]++;
data[indexCount][`${field}rowSpan`] = 0;
} else {
count = indexCount;
}
indexCount++;
let count = 0; // 当前组中第一个元素的索引
let indexCount = 1; // 下一个需要比较的元素的索引
while (indexCount < data.length) {
const item = data[count]; // 获取当前组的第一个元素
const nextItem = data[indexCount]; // 获取下一个需要比较的元素
if (!item[`${field}rowSpan`]) {
item[`${field}rowSpan`] = 1; // 初始化rowSpan属性为1
}
}
if (field == 'proName') {
let count = 0; //重复项的第一项
let indexCount = 1; //下一项
while (indexCount < data.length) {
let item = data.slice(count, count + 1)[0]; //获取没有比较的第一个对象
if (!item[`${field}rowSpan`]) {
item[`${field}rowSpan`] = 1; //初始化为1
}
if (
item[field] === data[indexCount][field] &&
item['companyName'] === data[indexCount]['companyName']
) {
//第一个对象与后面的对象相比,有相同项就累加,并且后面相同项设置为0
item[`${field}rowSpan`]++;
data[indexCount][`${field}rowSpan`] = 0;
} else {
count = indexCount;
}
indexCount++;
// 检查companyName和指定字段是否匹配
if (item[field] === nextItem[field] && item.companyName === nextItem.companyName) {
item[`${field}rowSpan`]++; // 增加当前元素的rowSpan值
nextItem[`${field}rowSpan`] = 0; // 标记为已合并
} else {
count = indexCount; // 将当前组的起始位置移动到下一个元素
}
indexCount++;
}
dataSource.value = data;
dataSource.value = data; // 更新数据源
}
// 列表
......
import { BasicColumn, FormSchema } from '@/components/Table';
import { useUserStore } from '@/store/modules/user';
const deptId = useUserStore().userInfo.deptParentId;
//主模块-招标计划列表页
export const columns: BasicColumn[] = [
{
title: '序号',
dataIndex: 'serialNumber',
width: 100,
},
{
title: '填报周期',
dataIndex: 'tenderYear',
width: 200,
},
{
title: '项目数量',
dataIndex: 'proNumber',
width: 180,
},
{
title: '公司名称',
dataIndex: 'companyName',
width: 180,
},
{
title: '最新更新人',
dataIndex: 'updateBy',
width: 180,
},
{
title: '审核状态',
dataIndex: 'reviewStatus',
ifShow: deptId != '100',
width: 140,
},
{
title: '审核人',
dataIndex: 'revieweUser',
width: 180,
},
{
title: '审核时间',
dataIndex: 'reviewTime',
width: 180,
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'tenderYear',
label: '',
component: 'DatePicker',
componentProps: {
placeholder: '年份',
picker: 'year',
valueFormat: 'YYYY',
format: 'YYYY',
style: {
width: '100%',
},
},
colProps: { span: 4 },
},
{
field: 'companyId',
label: '',
component: 'Select',
componentProps: {
placeholder: '公司名称',
options: [
{
label: '公司名称',
value: '公司名称',
},
],
},
colProps: { span: 4 },
},
];
export const formSchema: FormSchema[] = [
{
field: 'proName',
label: '项目名称',
required: true,
component: 'Input',
componentProps: {
readonly: true,
disabled: true,
},
colProps: { span: 7, offset: 1 },
},
{
field: 'fundingSource',
label: '资金来源',
required: true,
component: 'Input',
componentProps: {
readonly: true,
disabled: true,
},
colProps: { span: 7, offset: 1 },
},
{
field: 'projectInitiator',
label: '立项主体',
required: true,
component: 'Input',
colProps: { span: 7, offset: 1 },
},
{
field: 'investmentAmount',
label: '立项投资额',
required: true,
component: 'InputNumber',
componentProps: {
addonAfter: '万元',
},
colProps: { span: 7, offset: 1 },
},
{
field: 'biddingType',
label: '招标类型(施工、监理、设计、图审等)',
required: true,
component: 'Input',
colProps: { span: 7, offset: 1 },
labelWidth: 400,
},
{
field: 'controlPrice',
label: '预计控制价',
required: true,
component: 'InputNumber',
componentProps: {
addonAfter: '万元',
},
colProps: { span: 7, offset: 1 },
},
{
field: 'plannedPeriod',
label: '计划工期',
required: true,
component: 'RangePicker',
colProps: { span: 7, offset: 1 },
componentProps: {
valueFormat: 'YYYY/MM/DD',
format: 'YYYY/MM/DD',
},
},
{
field: 'biddingPeriod',
label: '计划招标周期',
required: true,
component: 'RangePicker',
colProps: { span: 7, offset: 1 },
componentProps: {
valueFormat: 'YYYY/MM/DD',
format: 'YYYY/MM/DD',
},
},
{
field: 'biddingMethod',
label: '招标方式',
required: true,
component: 'Select',
componentProps: {
options: [
{
label: '建委招标',
value: '建委招标',
},
{
label: '平台招标',
value: '平台招标',
},
{
label: '邀标比选',
value: '邀标比选',
},
{
label: '直接委托',
value: '直接委托',
},
{
label: '评定分离',
value: '评定分离',
},
],
},
colProps: { span: 7, offset: 1 },
},
{
label: '招标内容',
field: 'tenderContent',
required: true,
component: 'InputTextArea',
componentProps: {
maxlength: 100,
showCount: true,
},
colProps: { span: 7, offset: 1 },
},
{
label: '备注',
field: 'remark',
// required: true,
component: 'InputTextArea',
componentProps: {
maxlength: 100,
showCount: true,
},
colProps: { span: 7, offset: 1 },
},
];
<template>
<PageWrapper
:title="getTitle"
v-loading="loadingRef"
loading-tip="加载中..."
:contentBackground="false"
headerSticky
>
<template #extra>
<a-button type="primary" v-if="!disabled" :disabled="loadingRef" @click="handleSubmit('0')">
暂存
</a-button>
<a-button type="primary" v-if="!disabled" @click="handleSubmit('1')"> 提交 </a-button>
<a-button type="default" @click="router.back()"> 返回 </a-button>
</template>
<!-- page页 -->
<CollapseContainer v-for="(item, index) in tabsFormSchema" :key="index" v-show="item.show">
<template #title>
<span class="projectName">{{ item.name }}</span>
</template>
<template #action>
<a-button
v-if="!disabled"
type="text"
preIcon="ant-design:delete-outlined"
danger
@click="deleteItem(index)"
>删除项目</a-button
>
</template>
<BasicForm :loading="loading" @register="item.Form[0]" />
<!-- 左上角为项目名 -->
<CollapseContainer
v-for="(content, key) in item.list"
:key="key"
class="subCard"
:title="content.name"
v-show="content.show"
>
<template #title>
<span class="contractName">{{ content.name }}</span>
</template>
<BasicForm :loading="loading" @register="content.form" />
</CollapseContainer>
</CollapseContainer>
<a-button v-if="!disabled" type="dashed" @click="add" preIcon="ei:plus">
从项目库导入
</a-button>
<a-button v-if="!disabled" type="dashed" @click="handleAdd" class="ml-2" preIcon="ei:plus">
新建项目
</a-button>
<Upload
:action="uploadAction"
name="file"
:multiple="false"
methods="post"
:file-list="fileList"
accept=".xlsx,.xls"
:on-success="uploadFile"
>
<a-button v-if="!disabled" type="dashed" class="ml-2" preIcon="ei:plus"> Excel导入 </a-button>
</Upload>
<projectDrawer v-if="!disabled" @register="registerDrawer" @success="handleSuccess" />
<projectlibraryModel v-if="!disabled" @register="register" @close="handleNewData" />
</PageWrapper>
</template>
<script lang="ts" setup>
//历史记录
import { unref, onMounted, ref, reactive, nextTick } from 'vue';
import projectlibraryModel from '@/components/projectlibraryModel/projectlibraryModel.vue';
import { PageWrapper } from '@/components/Page';
import projectDrawer from '@/views/project/projectDrawer.vue';
import { BasicForm, useForm, FormProps, UseFormReturnType } from '@/components/Form';
import { formSchema } from './data';
import { deepMerge } from '@/utils';
import { useMessage } from '@/hooks/web/useMessage';
import { useDrawer } from '@/components/Drawer';
import { addProject, updateProject, selectDetail } from '@/api/project/yearTenderPlan';
import { editModel } from '@/api/project/model/biddingPlanModel';
import { useModal } from '@/components/Modal';
import { router } from '@/router';
import { useRoute } from 'vue-router';
import { isArray } from 'lodash-es';
import CollapseContainer from '@/components/Container/src/collapse/CollapseContainer.vue';
import { Upload } from 'ant-design-vue';
//历史记录是否可查
const historyData = ref(true);
//页面传递过来了的id
const routeId = ref(null);
const loadingRef = ref(false);
const [registerDrawer, { openDrawer }] = useDrawer();
const [register, { openModal: openModal }] = useModal();
const { createMessage } = useMessage();
const isUpdate = ref(false);
const loading = ref(false);
const formData = ref<editModel>({});
const tenderId = ref<any>('');
const disabled = ref(false);
const getTitle = ref('');
const fileList = ref([]);
const uploadAction =
import.meta.env.MODE === 'development'
? 'http://localhost:5173/api/pro/tenderYearPlan/import'
: 'http://172.72.253.84/pro/tenderYearPlan/import';
onMounted(async () => {
loadingRef.value = true;
const route = useRoute();
const id = route.query.id; // 获取名为id的参数
routeId.value = id;
if (!id) {
historyData.value = false;
formData.value.biddingQuarter = route.query.quarter;
formData.value.tenderYear = route.query.year;
formData.value.tenderPlanPro = [];
isUpdate.value = false;
getTitle.value = '创建年度招标计划';
console.log(formData);
} else {
isUpdate.value = true;
tenderId.value = id;
formData.value.tenderId = id;
disabled.value = route.query.disabled == '0' ? false : true;
if (disabled.value) {
getTitle.value = '查看年度招标计划';
} else {
getTitle.value = '编辑年度招标计划';
}
let res = await selectDetail({ id });
formData.value.tenderPlanPro = [];
for (let i = 0; i < res.length; i++) {
const source = res[i];
const item = {
name: '项目:' + source.projectName,
forceRender: true,
show: true,
proId: source.proId,
Form: useForm(
Object.assign({ schemas: formSchema, disabled }, baseFormConfig) as FormProps,
),
};
source.biddingPeriod = source.biddingPeriod ? source.biddingPeriod.split(',') : '';
source.plannedPeriod = source.plannedPeriod ? source.plannedPeriod.split(',') : '';
const { setFieldsValue } = item.Form[1];
tabsFormSchema.push(item);
formData.value.tenderPlanPro.push({
id: source.id,
proId: source.proId,
tenderId: source.tenderId,
});
nextTick(() => {
setFieldsValue({
...source,
});
});
}
}
loadingRef.value = false;
});
type TabsFormType = {
name: string;
show?: boolean;
forceRender?: boolean;
Form: UseFormReturnType;
proId: string;
};
const baseFormConfig: Partial<FormProps> = {
showActionButtonGroup: false,
labelWidth: 100,
layout: 'vertical',
};
let tabsFormSchema = reactive<TabsFormType[]>([]);
async function add() {
openModal();
}
async function handleNewData(info: any) {
if (info) {
info.map((i) => {
handleNew(i);
});
}
}
/***
*
*/
async function handleNew(info: any) {
const item = {
name: '项目:' + info.projectName,
show: true,
forceRender: true,
proId: info.id,
Form: useForm(Object.assign({ schemas: formSchema }, baseFormConfig) as FormProps),
};
const { setFieldsValue } = item.Form[1];
tabsFormSchema.push(item);
await nextTick(() => {
setFieldsValue({
projectName: info.projectName,
fundingSource: info.fundingSource,
investmentAmount: info.projectInvestment,
proId: info.id,
});
});
}
async function deleteItem(index: any) {
tabsFormSchema[index].show = false;
loadingRef.value = false;
}
async function handleAdd() {
openDrawer(true, {
isUpdate: false,
});
}
async function handleSuccess(params: any) {
console.log(params);
handleNew(params);
}
async function handleSubmit(isSubmit) {
for (let i = 0; i < tabsFormSchema.length; i++) {
let formSchema = tabsFormSchema[i].Form[1];
const { updateSchema } = formSchema;
await nextTick(() => {
if (isSubmit == '0') {
updateSchema([
{ field: 'projectName', required: false },
{ field: 'projectInitiator', required: false },
{ field: 'fundingSource', required: false },
{ field: 'investmentAmount', required: false },
{ field: 'biddingType', required: false },
{ field: 'controlPrice', required: false },
{ field: 'plannedPeriod', required: false },
{ field: 'biddingPeriod', required: false },
{ field: 'biddingMethod', required: false },
{ field: 'tenderContent', required: false },
]);
} else {
updateSchema([
{ field: 'projectName', required: true },
{ field: 'projectInitiator', required: true },
{ field: 'fundingSource', required: true },
{ field: 'investmentAmount', required: true },
{ field: 'biddingType', required: true },
{ field: 'controlPrice', required: true },
{ field: 'plannedPeriod', required: true },
{ field: 'biddingPeriod', required: true },
{ field: 'biddingMethod', required: true },
{ field: 'tenderContent', required: true },
]);
}
});
}
loadingRef.value = true;
formData.value.tenderPlanPro = [];
try {
let engineerConListIndex = 0;
for (let i = 0; i < tabsFormSchema.length; i++) {
let item = tabsFormSchema[i];
if (item.show) {
const { validate, getFieldsValue } = item.Form[1];
await validate();
let res = getFieldsValue();
res.biddingPeriod = isArray(res.biddingPeriod)
? res.biddingPeriod.join(',')
: res.biddingPeriod;
res.plannedPeriod = isArray(res.plannedPeriod)
? res.plannedPeriod.join(',')
: res.plannedPeriod;
console.log(
'deepMerge(formData.value.tenderPlanPro[i], res)',
deepMerge(formData.value.tenderPlanPro[i], res),
);
let obj = {
proId: item.proId,
};
obj = deepMerge(obj, res);
formData.value.tenderPlanPro.push(obj);
engineerConListIndex = engineerConListIndex + 1;
}
}
formData.value.proNumber = formData.value.tenderPlanPro.Length;
formData.value.isSubmit = isSubmit;
isUpdate.value ? await updateProject(unref(formData)) : await addProject(unref(formData));
loadingRef.value = true;
if (isSubmit === '1') {
createMessage.success('提交成功!');
} else if (isSubmit === '0') {
createMessage.success('暂存成功!');
}
// router.back();
} catch (e) {
// 验证失败或出错,切换到对应标签页
createMessage.error('请将项目' + e.values.projectName + '信息填写完整!');
console.log(e);
} finally {
loadingRef.value = false;
}
}
async function uploadFile(response) {
loadingRef.value = true;
if (response.code == 1) {
response.data.map((info) => {
const item = {
name: '项目:' + info.proName,
forceRender: true,
show: true,
Form: useForm(Object.assign({ schemas: formSchema }, baseFormConfig) as FormProps),
proId: info.id ? info.id : '',
};
const { setFieldsValue } = item.Form[1];
tabsFormSchema.push(item);
nextTick(() => {
setFieldsValue({
...info,
plannedPeriod: [info.plannedPeriodStart, info.plannedPeriodEnd],
biddingPeriod: [info.biddingPeriodStart, info.biddingPeriodEnd],
});
});
});
createMessage.success('导入成功');
loadingRef.value = false;
} else {
createMessage.error('导入失败,请稍后重试');
loadingRef.value = false;
return;
}
}
</script>
<style scoped>
.subCard {
border: 1px solid #e8eaec;
}
.projectName {
border-left: blue solid 6px;
padding-left: 10px;
font-weight: bold;
}
.contractName {
border-left: red solid 6px;
padding-left: 10px;
font-weight: bold;
}
</style>
<template>
<div>
<div v-if="deptId == 100 || deptId == 0" style="margin: 16px">
<PageCard title="数据填报周期">
<Row>
<Col :span="20">
<div class="cycleFrame">
<div class="card">
<div class="cycleIcon">
<img src="@/assets/icons/tianbaoriqi.png" />
</div>
<div class="characte">
<div class="top">填报周期</div>
<div @click="selectCycle" class="bottom" style="color: #0d84ff; text-decoration: underline; cursor: pointer">{{ mothCycle.fillCycle}}</div>
</div>
</div>
<Divider type="vertical" style="height: 36px; border-color: rgba(22,106,203,0.30)" dashed />
<div class="card">
<div class="cycleIcon">
<img src="@/assets/icons/shenyutianshu.png" />
</div>
<div class="characte">
<div class="top">剩余天数</div>
<div class="bottom" style="color:#DFAA21 ">{{
daysReturn() + '天'
}}</div>
</div>
</div>
<Divider type="vertical" style="height: 36px; border-color: rgba(22,106,203,0.30)" dashed />
<div class="card">
<div class="cycleIcon">
<img src="@/assets/icons/weiwancheng.png" />
</div>
<div class="characte">
<div class="top">企业未完成</div>
<div class="bottom" style="color:#DB4343">{{
mothCycle.noNum + '家'
}}</div>
</div>
</div>
<Divider type="vertical" style="height: 36px; border-color: rgba(22,106,203,0.30)" dashed />
<div class="card">
<div class="cycleIcon">
<img src="@/assets/icons/yiwancheng.png" />
</div>
<div class="characte">
<div class="top">企业已完成</div>
<div class="bottom" style="color:#16CB28">{{
mothCycle.deptNum - mothCycle.noNum + '家'
}}</div>
</div>
</div>
</div>
</Col>
<Col :span="4">
<a-button class="btn" @click="getFillDetails">
查看填报详情
</a-button>
<a-button class="btn" type="primary" @click="returnEdit">
{{ mothCycle.isEdit === '1' ? '开启本轮填报' : '结束本轮填报' }}
</a-button>
</Col>
</Row>
</PageCard>
</div>
<BasicTable @register="registerTable" :title="'招标计划填报'">
<template #toolbar>
<a-button type="primary" preIcon="mdi:plus" @click="handleCreate"> 新增招标计划 </a-button>
</template>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'reviewStatus'">
<Tag color="warning" v-if="record.reviewStatus === '0'"> 未审核</Tag>
</template>
<template v-if="column.key === 'reviewStatus'">
<Tag color="success" v-if="record.reviewStatus === '1'"> 已审核</Tag>
</template>
<template v-if="column.key === 'serialNumber'">
{{ index + 1 }}
</template>
<template v-if="column.key === 'tenderYear'">
{{ record.tenderYear + '年' + record.biddingQuarter }}
</template>
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: '详情',
onClick: handleEdit.bind(null, record, 1),
},
{
label: '修改',
onClick: handleEdit.bind(null, record, 0),
ifShow: (_action) => {
return record.isEdit === '0';
},
},
{
label: '删除',
color: 'error',
popConfirm: {
title: '是否确认删除',
placement: 'left',
confirm: handleDelete.bind(null, record),
},
ifShow: (_action) => {
return record.isEdit === '0';
},
},
{
label: '上报',
color: 'success',
popConfirm: {
title: '是否确认上报',
placement: 'left',
confirm: examine.bind(null, record, true),
},
ifShow: (_action) => {
return (record.reviewStatus === '0' || record.reviewStatus === null) && isExamine === true && record.isSubmit == '1';
},
},
]"
/>
</template>
</template>
</BasicTable>
<yearModal @register="register" @close="handleNew" />
<CycleModel @register="registerCycleModel" :user-data="mothCycle" @get-cycle="getSetCycle" />
<FillDetailsModel @register="registerFillDetail"></FillDetailsModel>
</div>
</template>
<script lang="ts" setup>
import { BasicTable, useTable, TableAction } from '@/components/Table';
import {
selectPageList,
deleteProject,
handelExamine,
getCount
} from '@/api/project/yearTenderPlan';
import { useModal } from '@/components/Modal';
import yearModal from '@/components/yearModal.vue';
import { columns, searchFormSchema } from './data';
import { useRouter } from 'vue-router';
import { useMessage } from '@/hooks/web/useMessage';
import {Tag} from "ant-design-vue";
//审核相关操作
import { operateType, addItemApi, addItemData } from '@/api/operations/operations';
import { onMounted, ref } from 'vue';
import {useUserStore} from "@/store/modules/user";
import PageCard from '@/components/Page/src/PageCard.vue';
import CycleModel from '@/views/monthlyPlan/cycleModel/cycleModel.vue';
import FillDetailsModel from '@/views/monthlyPlan/fillDetailsModel/fillDetailsModel.vue';
import {Divider,Row, Col} from 'ant-design-vue'
import { getDepartmentList } from '@/api/project/settlementManage';
import {getMothCycel, SetEditStatus} from "@/api/project/monthlyPlan";
const { createMessage } = useMessage();
const { error } = createMessage;
const { push } = useRouter();
const [registerFillDetail, { openModal: openFillDetailModel }] = useModal();
const [registerCycleModel, { openModal: openCycleModel }] = useModal();
const [register, { openModal: openModal, closeModal: closeModal }] = useModal();
const [registerTable, { reload }] = useTable({
api: selectPageList,
columns,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
useSearchForm: true,
showTableSetting: false,
bordered: true,
showIndexColumn: false,
actionColumn: {
width: 220,
title: '操作',
dataIndex: 'action',
// slots: { customRender: 'action' },
fixed: undefined,
},
});
const isExamine = ref<boolean>(false);
const deptId = ref(100);
const mothCycle = ref({
fillCycle:'2024-09',
isEdit: '0',
deptNum: 12,
noNum: 6,
});
onMounted(async () => {
deptId.value = useUserStore().userInfo.deptParentId;
let roles = useUserStore().userInfo.roles;
roles.forEach((item) => {
if (item.role_key === 'approver') {
isExamine.value = true;
}
});
const data = await getDepartmentList();
searchFormSchema[1].componentProps.options = data;
getSetCycle()
});
async function getSetCycle() {
const cycle = await getMothCycel({ id: '12', module: 'TenderPlan'});
mothCycle.value = cycle;
}
function returnEdit() {
if (mothCycle.value.isEdit == '1') {
mothCycle.value.isEdit = '0';
SetEditStatus(mothCycle.value);
} else {
mothCycle.value.isEdit = '1';
SetEditStatus(mothCycle.value);
}
reload();
}
function daysReturn() {
// 获取今天的日期
const today = new Date();
// 获取目标年份和月份
let targetYear = Number.parseInt(mothCycle.value.fillCycle);
let targetMonth = 12;
// 创建目标日期(2024年9月30日)
const targetDate = new Date(targetYear, targetMonth, 0); // 0表示上一个月的最后一天,即9月30日
// 计算时间差(毫秒)
const timeDifference = targetDate - today;
// 计算天数差(天数 = 毫秒差 / 每天的毫秒数)
const daysUntilTarget = Math.max(0, Math.ceil(timeDifference / (1000 * 60 * 60 * 24)));
return daysUntilTarget;
}
function selectCycle() {
openCycleModel(true, {
data: mothCycle.value,
});
}
function getFillDetails(){
openFillDetailModel(true,{
data: mothCycle.value,
url: '/pro/monthEngineer/businessComDetails',
module: 'TenderPlan',
})
}
function handleCreate() {
openModal(true, {
data: ['year', 'quarter'],
});
}
function handleEdit(record: Recordable, disabled: number) {
push({
path: '/biddingPlan/tendeYearPlanEdit',
query: {
id: record.id,
disabled: String(disabled),
},
});
}
function handleDelete(record: Recordable) {
deleteProject({ id: record.id });
reload();
}
async function handleNew(e) {
let res = await getCount(e);
if (res == true) {
closeModal();
push({
path: '/biddingPlan/tendeYearPlanEdit',
query: e,
});
} else {
error('该周期已填报');
}
}
async function examine(record: Recordable, disabled: boolean) {
const id = record.id;
const res = await handelExamine({ id });
addItemData.operateType = operateType.audit;
addItemData.businessId = record.id;
addItemData.businessType = '招标计划';
const showDatem = await addItemApi(addItemData);
console.log(showDatem, '记录添加成功');
console.log(res);
reload();
}
</script>
<style scoped>
.btn {
margin-right: 10px;
margin-top: 8px;
}
.cycleFrame{
border: 1px solid rgba(22, 106, 203, 0.10);
border-radius: 6px;
padding: 20px 50px;
margin-right: 20px;
display: flex;
justify-content: space-between;
.card{
display: flex;
.cycleIcon{
margin-right: 10px;
}
.characte{
display: flex;
flex-direction: column;
justify-content: space-around;
.top{}
.bottom{}
}
}
}
</style>
<template>
<div style="margin: 16px">
<PageCard title="招标计划统计">
<BasicForm ref="formElRef" @register="registerForm">
<template #formFooter>
<a-button type="primary" @click="handleSubmit"> 查询</a-button>
<a-button type="primary" @click="exportCount" style="margin-left: 10px"> 导出</a-button>
</template>
</BasicForm>
<Table
:pagination="false"
:dataSource="dataSource"
:columns="columns"
bordered
:loading="loadingRef"
:rowClassName="setRowClassName"
:scroll="{ x: 1300, y: 600 }"
/>
</PageCard>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { Table } from 'ant-design-vue';
import { exportTender, selectListAll } from '@/api/project/yearTenderPlan';
import { BasicForm, FormActionType, useForm } from '@/components/Form';
import PageCard from '@/components/Page/src/PageCard.vue';
import { downloadByData } from '@/utils/file/download';
import moment from 'moment';
import { getDepartmentList } from '@/api/project/settlementManage';
const seach = ref([
{
field: 'tenderYear',
label: '',
component: 'DatePicker',
componentProps: {
placeholder: '年份',
picker: 'year',
defaultValue: moment().format('YYYY'),
valueFormat: 'YYYY',
format: 'YYYY',
style: {
width: '100%',
},
},
colProps: { span: 4 },
},
{
field: 'companyId',
label: '',
component: 'Select',
componentProps: {
placeholder: '公司名称',
options: [
{
label: '公司名称',
value: '公司名称',
},
],
},
colProps: { span: 4 },
},
]);
const formElRef = ref<Nullable<FormActionType>>(null);
const [registerForm, { getFieldsValue }] = useForm({
labelWidth: 90,
baseColProps: { span: 24 },
schemas: seach,
showActionButtonGroup: false,
});
const params = ref({ tenderYear: '', companyName: '' });
const loadingRef = ref(false);
onMounted(async () => {
// 设置当前年份
params.value.tenderYear = moment().format('YYYY');
const data = await getDepartmentList();
seach.value[1].componentProps.options = data;
getStatisticList();
});
function handleSubmit() {
let data = getFieldsValue();
params.value = data;
getStatisticList();
}
async function exportCount() {
const data = await exportTender(params.value);
downloadByData(data, '年度招标计划统计报表' + '.xls');
}
function setRowClassName(record) {
if (record.projectName === '总计(万元)') {
return 'rowcolor';
} else if (record.companyName === '合计(万元)') {
return 'rowcolor';
} else {
return;
}
}
async function getStatisticList() {
loadingRef.value = true;
let data = await selectListAll(params.value);
let propsList = ['companyName'];
propsList.map((item) => {
changeData(data, item);
});
loadingRef.value = false;
}
function changeData(data, field) {
let count = 0; // 当前组中第一个元素的索引
let indexCount = 1; // 下一个需要比较的元素的索引
while (indexCount < data.length) {
const item = data[count]; // 获取当前组的第一个元素
const nextItem = data[indexCount]; // 获取下一个需要比较的元素
if (!item[`${field}rowSpan`]) {
item[`${field}rowSpan`] = 1; // 初始化rowSpan属性为1
}
// 检查companyName和指定字段是否匹配
if (item[field] === nextItem[field] && item.companyName === nextItem.companyName) {
item[`${field}rowSpan`]++; // 增加当前元素的rowSpan值
nextItem[`${field}rowSpan`] = 0; // 标记为已合并
} else {
count = indexCount; // 将当前组的起始位置移动到下一个元素
}
indexCount++;
}
dataSource.value = data; // 更新数据源
}
// 列表
const dataSource = ref([]);
// 表头
const columns = [
{
title: '公司名称',
dataIndex: 'companyName',
fixed: 'left',
width: 180,
customCell: (_, any) => ({
rowSpan: _.companyNamerowSpan,
}),
},
{
title: '项目名称',
dataIndex: 'projectName',
width: 180,
customCell: (_, any) => ({
rowSpan: _.projectNamerowSpan,
}),
},
{
title: '立项投资额(万元)',
dataIndex: 'investmentAmount',
width: 180,
},
{
title: '预计控制价(万元)',
dataIndex: 'controlPrice',
width: 180,
},
{
title: '招标类型',
dataIndex: 'biddingType',
width: 180,
},
{
title: '招标方式',
dataIndex: 'biddingMethod',
width: 180,
},
];
</script>
<style lang="less" scoped>
::v-deep .ant-table-tbody .ant-table-row {
&.rowcolor .ant-table-cell-fix-left {
background-color: #f5f5f5;
}
}
.action-container {
display: flex;
justify-content: space-between;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment