This commit is contained in:
tcy
2025-08-21 18:56:49 +08:00
10 changed files with 1757 additions and 559 deletions

View File

@ -34,6 +34,7 @@
"diagram-js": "12.3.0", "diagram-js": "12.3.0",
"didi": "9.0.2", "didi": "9.0.2",
"echarts": "5.5.0", "echarts": "5.5.0",
"echarts-gl": "^2.0.9",
"element-plus": "2.8.8", "element-plus": "2.8.8",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"ezuikit-js": "^8.1.10", "ezuikit-js": "^8.1.10",

44
src/api/tender/index.ts Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
//获取版本
export const obtainAllVersionNumbers = (query: any): AxiosPromise<any> => {
return request({
url: '/tender/tenderPlanLimitList/obtainAllVersionNumbers',
method: 'get',
params: query
});
};
//获取sheet
export const sheetList = (query: any): AxiosPromise<any> => {
return request({
url: '/tender/tenderPlanLimitList/sheetList',
method: 'get',
params: query
});
};
//获取表格数据
export const getTableList = (query: any): AxiosPromise<any> => {
return request({
url: '/tender/tenderPlanLimitList/list',
method: 'get',
params: query
});
};
//修改单价数据
export const updatePrice = (query: any): AxiosPromise<any> => {
return request({
url: '/tender/tenderPlanLimitList',
method: 'put',
data: query
});
};
//导入
export const importExcelFile = (query: any, data: any): AxiosPromise<any> => {
return request({
url: '/tender/tenderPlanLimitList/importExcelFile',
method: 'post',
params: query,
data
});
};

View File

@ -0,0 +1,169 @@
<template>
<div ref="echartBox" class="echarts"></div>
</template>
<script setup lang="ts">
import china from '@/assets/china.json';
import cq from '@/assets/cq.json';
import { ref, onMounted, watchEffect, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts/core';
import {
BarChart, // 柱状图
// 系列类型的定义后缀都为 SeriesOption
BarSeriesOption,
LineChart, // 折线图
LineSeriesOption,
PieChart, // 饼图
PieSeriesOption,
PictorialBarChart,
MapChart,
ScatterChart,
EffectScatterChart,
LinesChart
} from 'echarts/charts';
import {
// 组件类型的定义后缀都为 ComponentOption
// 标题
TitleComponent,
TitleComponentOption,
// 提示框
TooltipComponent,
TooltipComponentOption,
// 直角坐标系
GridComponent,
GridComponentOption,
// 图例
LegendComponent,
LegendComponentOption,
// 数据集组件
DatasetComponent,
DatasetComponentOption,
// 内置数据转换器组件 (filter, sort)
TransformComponent,
DataZoomComponent,
DataZoomComponentOption,
// 极坐标
PolarComponent,
PolarComponentOption,
MarkLineComponentOption,
MarkLineComponent,
// MarkPoint
MarkPointComponent,
MarkPointComponentOption,
// VisualMap
VisualMapComponent,
VisualMapComponentOption,
// GeoComponent
GeoComponent,
GeoComponentOption
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import 'echarts-gl';
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = echarts.ComposeOption<
| BarSeriesOption
| LineSeriesOption
| PieSeriesOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| DatasetComponentOption
| LegendComponentOption
| DataZoomComponentOption
| PolarComponentOption
| MarkLineComponentOption
| MarkPointComponentOption
| VisualMapComponentOption
| GeoComponentOption
>;
// 注册必须的组件
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LegendComponent,
DataZoomComponent,
PolarComponent,
MarkLineComponent,
MarkPointComponent,
LabelLayout,
UniversalTransition,
CanvasRenderer,
BarChart,
LineChart,
PieChart,
VisualMapComponent,
PictorialBarChart,
GeoComponent,
MapChart,
ScatterChart,
EffectScatterChart,
LinesChart
]);
const props = defineProps({
option: {
type: Object,
default: () => {
return null;
}
}
});
const emit = defineEmits(['echartsEvent']);
const echartBox = ref(null);
let chart!: echarts.ECharts;
const setChart = (option: ECOption): void => {
if (!props.option || !echartBox.value) {
return;
}
chart.resize();
chart.setOption(option);
};
const resetChart = (): void => {
const option = chart.getOption();
if (!option || !echartBox.value) {
return;
}
chart.resize();
};
onMounted(() => {
(echarts as any).registerMap('china', { geoJSON: china });
(echarts as any).registerMap('cq', { geoJSON: cq });
chart = echarts.init(echartBox.value as any);
emit('echartsEvent', chart);
setChart(props.option);
// 界面拉伸后重设
window.addEventListener('resize', () => {
resetChart();
});
});
watchEffect(() => {
if (chart) {
chart.clear();
}
setChart(props.option);
});
onBeforeUnmount(() => {
if (chart) {
chart.dispose();
}
});
</script>
<style scoped lang="scss">
.echarts {
width: 100%;
height: 100%;
pointer-events: all;
}
</style>

View File

@ -463,7 +463,16 @@ const getDetails = (row: any) => {
} }
}); });
}; };
//监听项目id刷新数据
const listeningProject = watch(
() => currentProject.value?.id,
(nid, oid) => {
getTabsList();
}
);
onUnmounted(() => {
listeningProject();
});
onMounted(() => { onMounted(() => {
getTabsList(); getTabsList();
}); });

View File

@ -72,7 +72,7 @@ const percentageClass = computed(() => {
.progress_component { .progress_component {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin-bottom: 10px;
:deep(.el-progress-bar__outer) { :deep(.el-progress-bar__outer) {
background-color: transparent; background-color: transparent;
} }

View File

@ -2,36 +2,72 @@
<div class="leftPage"> <div class="leftPage">
<!-- --> <!-- -->
<div class="kpi_box"> <div class="kpi_box">
<TitleComponent :title="'支付KPI'"/> <TitleComponent :title="'支付KPI'" style="margin-bottom: 20px;"/>
<div style="height: 100px;"></div>
<ProgressComponent <ProgressComponent
title="营业收入" title="应收账款"
value="123,456.78" value="123,456.78"
percentageChange="+25.30%" percentageChange="+25.30%"
progressPercentage="75" progressPercentage="75"
progressColor="rgba(255, 77, 79, 1)"
/>
<ProgressComponent
title="应付账款"
value="123,456.78"
percentageChange="+25.30%"
progressPercentage="25"
progressColor="rgba(29, 214, 255, 1)"
/>
<ProgressComponent
title="本月付款"
value="123,456.78"
percentageChange="+25.30%"
progressPercentage="45"
progressColor="rgba(0, 227, 150, 1)" progressColor="rgba(0, 227, 150, 1)"
/> />
<ProgressComponent
title="本月收款"
value="123,456.78"
percentageChange="+25.30%"
progressPercentage="10"
progressColor="rgba(255, 147, 42, 1)"
/>
</div>
<div class="contract_box">
<EchartBox :option="barOption" />
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, computed, toRefs, getCurrentInstance, nextTick } from 'vue'; import { ref, reactive, onMounted, computed, toRefs, getCurrentInstance, nextTick } from 'vue';
// import echarts from 'echarts';
import TitleComponent from './TitleComponent.vue'; import TitleComponent from './TitleComponent.vue';
import ProgressComponent from './ProgressComponent.vue'; import ProgressComponent from './ProgressComponent.vue';
import EchartBox from '@/components/EchartBox/index.vue';
import { getBarOptions2 } from './optionList';
const barOption = ref();
const getCapitalData = (data) => {
barOption.value = getBarOptions2();
};
onMounted(() => {
getCapitalData();
});
</script> </script>
<style lang="scss"> <style lang="scss">
.leftPage { .leftPage {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #0c1e35; .kpi_box{
margin-bottom: 10px;
.kpi_box { }
.contract_box{
height: 35vh;
}
.kpi_box,.contract_box {
padding: 10px; padding: 10px;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid rgba(29, 214, 255, 0.3);
} }
} }
</style> </style>

View File

@ -1,4 +1,5 @@
import * as echarts from 'echarts/core'; import * as echarts from 'echarts/core';
import { text } from 'stream/consumers';
// import { PictorialBarChart } from 'echarts/charts' // import { PictorialBarChart } from 'echarts/charts'
// 客流量图 // 客流量图
export const getOption = (xData: any, yData: any) => { export const getOption = (xData: any, yData: any) => {
@ -249,9 +250,10 @@ export const getOption2 = (data: any) => {
}; };
return option; return option;
}; };
//食堂周报图 //z折线
export const getLineOption = (lineData: any) => { export const getLineOption = (lineData: any) => {
const maxData = Math.ceil(Math.max(...lineData.line1)); const maxData = Math.max(...lineData.line1.flat());
const option = { const option = {
backgroundColor: '', backgroundColor: '',
tooltip: { tooltip: {
@ -263,37 +265,41 @@ export const getLineOption = (lineData: any) => {
}, },
borderColor: '#7ec7ff' borderColor: '#7ec7ff'
}, },
// legend: { legend: {
// align: 'left', align: 'left',
// right: '5%', right: '5%',
// top: '1%', top: '1%',
// type: 'plain', type: 'plain',
// textStyle: { textStyle: {
// color: '#fff', color: '#fff',
// fontSize: 12 fontSize: 12
// }, },
// // icon:'rect', // icon:'rect',
// itemGap: 15, itemGap: 15,
// itemWidth: 18, itemWidth: 18,
// data: [ data: [
// { {
// name: '上周销售量' name: '收款金额'
// }, },
// { {
// name: '本周销售量' name: '付款金额'
// } },
// ] {
// }, name: '净现金流'
}
]
},
grid: { grid: {
top: '12%', top: '12%',
left: '1%', left: '1%',
right: '3%', right: '3%',
bottom: '12%', bottom: '5%',
containLabel: true containLabel: true
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: lineData.xLabel, data: lineData.xLabel,
boundaryGap: false,
axisLine: { axisLine: {
show: false show: false
}, },
@ -318,21 +324,21 @@ export const getLineOption = (lineData: any) => {
} }
} }
}, },
dataZoom: [ // dataZoom: [
{ // {
// show: true, // // show: true,
start: 0, // start: 0,
end: 30, // end: 30,
bottom: 2, // 下滑块距离x轴底部的距离 // bottom: 2, // 下滑块距离x轴底部的距离
height: 23 // height: 23
}, // },
{ // {
type: 'inside' // type: 'inside'
} // }
], // ],
series: [ series: [
{ {
name: '逆变器功率', name: '收款金额',
type: 'line', type: 'line',
symbol: 'circle', // 默认是空心圆(中间是白色的),改成实心圆 symbol: 'circle', // 默认是空心圆(中间是白色的),改成实心圆
showAllSymbol: false, showAllSymbol: false,
@ -373,7 +379,95 @@ export const getLineOption = (lineData: any) => {
shadowColor: 'rgba(25,163,223, 0.5)', //阴影颜色 shadowColor: 'rgba(25,163,223, 0.5)', //阴影颜色
shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。 shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
}, },
data: lineData.line1 data: lineData.line1[0]
},
{
name: '付款金额',
type: 'line',
symbol: 'none', // 默认是空心圆(中间是白色的),改成实心圆
showAllSymbol: false,
symbolSize: 0,
smooth: true,
lineStyle: {
width: 1,
color: 'rgba(255, 224, 179, 1)', // 线条颜色
borderColor: 'rgba(0,0,0,.4)'
},
itemStyle: {
color: 'rgba(255, 224, 179, 1)',
borderWidth: 2,
show: true
},
tooltip: {
show: true
},
areaStyle: {
//线性渐变前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是true则该四个值是绝对像素位置。
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0,
color: 'rgba(255, 224, 179, 0.4)'
},
{
offset: 1,
color: 'rgba(255, 224, 179, 0)'
}
],
false
),
shadowColor: 'rgba(255, 224, 179, 0.6)', //阴影颜色
shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
},
data: lineData.line1[1]
},
{
name: '净现金流',
type: 'line',
symbol: 'none', // 默认是空心圆(中间是白色的),改成实心圆
showAllSymbol: false,
symbolSize: 0,
smooth: true,
lineStyle: {
width: 1,
color: 'rgba(39, 255, 252, 1)', // 线条颜色
borderColor: 'rgba(0,0,0,.4)'
},
itemStyle: {
color: 'rgba(39, 255, 252, 1)',
borderWidth: 2,
show: false
},
tooltip: {
show: true
},
areaStyle: {
//线性渐变前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是true则该四个值是绝对像素位置。
color: new echarts.graphic.LinearGradient(
0,
0,
0,
1,
[
{
offset: 0,
color: 'rgba(39, 255, 252, 0.4)'
},
{
offset: 1,
color: 'rgba(39, 255, 252, 0)'
}
],
false
),
shadowColor: 'rgba(39, 255, 252, 0.5)', //阴影颜色
shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
},
data: lineData.line1[2]
} }
] ]
}; };
@ -733,3 +827,62 @@ export const getBarOptions = (data: any) => {
}; };
return option; return option;
}; };
// 收支合同分析
export const getBarOptions2 = (data: any) => {
const option = {
color:['#FF932A', '#678FE6', '#1DD6FF', '#00E396'],
title: {
text: '数量(个)',
subtext: '16',
bottom: 'center',
left: 'center',
textStyle: {
color: '#9DADB7',
fontSize: 16
},
subtextStyle:{
color: '#707070',
fontSize: 32,
fontWeight: 'bold'
}
},
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['50%', '60%'],
avoidLabelOverlap: false,
padAngle: 5,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: false,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 3, name: '100万一下' },
{ value: 4, name: '100-500万' },
{ value: 5, name: '500-1000万' },
{ value: 4, name: '1000万以上' },
]
}
]
};
return option;
}

View File

@ -1,13 +1,71 @@
<template> <template>
<div class="rightPage">右边</div> <div class="rightPage">
<div class="funds">
<TitleComponent :title="'资金KPI'" />
<div class="funds_echarts">
<EchartBox :option="lineOption" />
</div>
</div>
<div class="cashFlow">
<TitleComponent :title="'现金流概述'" />
</div>
</div>
</template> </template>
<script setup lang="ts"></script> <script setup lang="ts">
import TitleComponent from './TitleComponent.vue';
import EchartBox from '@/components/EchartBox/index.vue';
import { getLineOption, getBarOptions } from './optionList';
const lineOption = ref();
const getCapitalData = (data?: any) => {
// const xData = data.map((item) => item.time);
// const yData = data.map((item) => item.content);
const lineData = {
xLabel: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
line1: [
[100, 200, 150, 300, 250, 350, 400, 350, 450, 500, 400, 550],
[220, 250, 230, 280, 270, 300, 350, 320, 380, 400, 450, 500],
[300, 350, 320, 380, 400, 450, 500, 480, 520, 550, 600, 650]
]
// line2: ['20', '50', '12', '65', '30', '60']
};
lineOption.value = getLineOption(lineData);
};
onMounted(() => {
getCapitalData();
});
//资金KPI
</script>
<style scoped lang="scss"> <style scoped lang="scss">
.rightPage { .rightPage {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: #0c1e35; background: #0c1e35;
box-sizing: border-box;
// padding: 5px;
}
.funds {
width: 100%;
// height: 40%;
border: 1px solid rgba(29, 214, 255, 0.3);
box-sizing: border-box;
padding: 10px 5px;
}
.funds_echarts {
width: 100%;
height: 35vh;
padding: 10px 0 0 0;
}
.cashFlow {
width: 100%;
// height: 50%;
border: 1px solid rgba(29, 214, 255, 0.3);
box-sizing: border-box;
padding: 10px 5px;
margin-top: 20px;
} }
</style> </style>

View File

@ -0,0 +1,279 @@
<template>
<div class="p-2">
<el-tabs type="border-card" @tab-change="handleTabChange" v-model="activeTab">
<el-tab-pane v-for="(item, index) in tabList" :key="index" :label="item.label" :name="item.value">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<el-card shadow="always">
<el-form :model="queryForm" :inline="true">
<el-form-item label="版本号" prop="versions">
<el-select v-model="queryForm.versions" placeholder="选择版本号" @change="changeVersions">
<el-option v-for="item in options" :key="item.versions" :label="item.versions" :value="item.versions" />
</el-select>
</el-form-item>
<el-form-item label="表名" prop="sheet">
<el-select v-model="queryForm.sheet" placeholder="选择表名" @change="changeSheet">
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="toggleExpandAll(true)">一键展开</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="toggleExpandAll(false)">一键收起</el-button>
</el-form-item>
<el-form-item>
<el-upload
ref="uploadRef"
class="upload-demo"
:http-request="importExcel"
:show-file-list="false"
v-hasPermi="['tender:billofquantitiesLimitList:importExcelFile']"
>
<template #trigger>
<el-button type="primary">导入excel</el-button>
</template>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleExport()" v-hasPermi="['tender:billofquantitiesLimitList:export']">导出excel</el-button>
</el-form-item>
</el-form>
</el-card>
</transition>
<el-card shadow="never" class="mb8">
<el-table ref="tableRef" v-loading="loading" :data="tableData" row-key="id" border lazy default-expand-all>
<el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" />
<el-table-column prop="remark" label="单价" align="center">
<template #default="scope">
<el-input-number
:model-value="scope.row.unitPrice"
@change="(val) => (scope.row.unitPrice = val)"
:precision="2"
:step="0.1"
:controls="false"
v-if="scope.row.quantity && scope.row.quantity != 0"
/>
</template>
</el-table-column>
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{ scope.row.price }}
</template>
</el-table-column>
<el-table-column prop="price" label="操作" align="center">
<template #default="scope">
<el-button
type="primary"
size="small"
@click="handleSave(scope.row)"
v-if="scope.row.quantity && scope.row.quantity != 0"
v-hasPermi="['tender:billofquantitiesLimitList:edit']"
>确定</el-button
>
</template>
</el-table-column>
</el-table>
</el-card>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { useUserStoreHook } from '@/store/modules/user';
import { obtainAllVersionNumbers, sheetList, getTableList, updatePrice, importExcelFile } from '@/api/tender/index';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject);
const tabList = [
{
label: '招采工程量清单',
value: '2'
},
{
label: '物资设备清单',
value: '3'
}
];
const queryForm = ref({
versions: '',
sheet: ''
});
const activeTab = ref('2');
const sheets = ref([]);
const options = ref([]);
const tableData = ref([]);
const tableRef = ref();
const isExpandAll = ref(false);
const loading = ref(false);
// 切换tab
const handleTabChange = (tab: string) => {
activeTab.value = tab;
getVersionNums();
};
//切换版本
const changeVersions = () => {
getSheetName();
};
//切换表格
const changeSheet = (val: any) => {
getTableData();
};
//展开树
const toggleExpandAll = (isExpand: boolean) => {
tableData.value.forEach((row) => {
tableRef.value.toggleRowExpansion(row, isExpand);
});
isExpandAll.value = isExpand;
};
//获取版本号
const getVersionNums = async () => {
try {
const params = {
projectId: currentProject.value?.id,
workOrderType: activeTab.value,
pageSize: 1000,
pageNum: 1
};
const res = await obtainAllVersionNumbers(params);
if (res.code == 200) {
options.value = res.data;
if (res.data.length > 0) {
queryForm.value.versions = res.data[0].versions;
getSheetName();
} else {
queryForm.value.versions = '';
getSheetName();
}
}
} catch (error) {
console.log(error);
}
};
//获取表名
const getSheetName = async () => {
try {
const params = {
projectId: currentProject.value?.id,
versions: queryForm.value.versions
};
const res = await sheetList(params);
if (res.code == 200) {
sheets.value = res.data;
if (res.data.length > 0) {
queryForm.value.sheet = res.data[0];
} else {
queryForm.value.sheet = '';
}
getTableData();
}
} catch (error) {
console.log(error);
}
};
//获取表格数据
const getTableData = async () => {
try {
const params = {
projectId: currentProject.value?.id,
versions: queryForm.value.versions,
sheet: queryForm.value.sheet,
type: activeTab.value
};
const res = await getTableList(params);
if (res.code == 200) {
tableData.value = res.data;
}
} catch (error) {
console.log(error);
}
};
//导入
const importExcel = (options: any): any => {
let formData = new FormData();
formData.append('file', options.file);
loading.value = true;
importExcelFile(
{ projectId: currentProject.value?.id, sheet: queryForm.value.sheet, versions: queryForm.value.versions, type: activeTab.value },
formData
)
.then((res) => {
const { code } = res;
if (code == 200) {
proxy.$modal.msgSuccess(res.msg || '导入成功');
getTableData();
} else {
proxy.$modal.msgError(res.msg || '导入失败');
}
})
.catch((err) => {
proxy.$modal.msgError(err.msg || '导入失败');
})
.finally(() => {
loading.value = false;
});
};
//导出
const handleExport = () => {
proxy?.download(
'/tender/tenderPlanLimitList/export',
{
projectId: currentProject.value?.id,
sheet: queryForm.value.sheet,
versions: queryForm.value.versions,
type: activeTab.value
},
`招标一览表${queryForm.value.sheet}.xlsx`
);
};
//确认修改
const handleSave = (row: any) => {
try {
if (!row.unitPrice) {
ElMessage({
message: '请输入单价',
type: 'warning'
});
return;
}
loading.value = true;
updatePrice(row).then((res) => {
if (res.code == 200) {
ElMessage({
message: '修改成功',
type: 'success'
});
getTableData();
}
});
} catch (error) {
console.log(error);
loading.value = false;
} finally {
loading.value = false;
}
};
//监听项目id刷新数据
const listeningProject = watch(
() => currentProject.value?.id,
(nid, oid) => {
getVersionNums();
}
);
onUnmounted(() => {
listeningProject();
});
onMounted(() => {
getVersionNums();
});
</script>
<style scoped lang="scss"></style>

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <<<<<<< HEAD <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="110px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="110px">
@ -11,16 +12,12 @@
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="计划招标时间" prop="plannedBiddingTime"> <el-form-item label="计划招标时间" prop="plannedBiddingTime">
<el-date-picker <el-date-picker clearable v-model="queryParams.plannedBiddingTime" type="date" value-format="YYYY-MM-DD"
clearable placeholder="请选择计划招标时间" />
v-model="queryParams.plannedBiddingTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择计划招标时间"
/>
</el-form-item> </el-form-item>
<el-form-item label="招标方式" prop="plannedBiddingMethod"> <el-form-item label="招标方式" prop="plannedBiddingMethod">
<el-select v-model="queryParams.plannedBiddingMethod" placeholder="请选择招标方式" clearable @change="handleQuery"> <el-select v-model="queryParams.plannedBiddingMethod" placeholder="请选择招标方式" clearable
@change="handleQuery">
<el-option label="公招" value="公招"></el-option> <el-option label="公招" value="公招"></el-option>
<el-option label="邀标" value="邀标"></el-option> <el-option label="邀标" value="邀标"></el-option>
</el-select> </el-select>
@ -33,11 +30,23 @@
</el-form-item> </el-form-item>
<el-form-item label="合同额" prop="contractPrice"> <el-form-item label="合同额" prop="contractPrice">
<el-input v-model="queryParams.contractPrice" placeholder="请输入合同额" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.contractPrice" placeholder="请输入合同额" clearable @keyup.enter="handleQuery" />
=======
<el-tabs type="border-card" @tab-change="handleTabChange" v-model="activeTab">
<el-tab-pane v-for="(item, index) in tabList" :key="item.index" :label="item.label" :name="item.value">
<el-card shadow="always">
<el-form :model="queryForm" :inline="true" ref="queryFormRef">
<el-form-item label="名称" prop="versions">
<el-input v-model="queryForm.name" placeholder="请输入名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="内容" prop="sheet">
<el-input v-model="queryForm.content" placeholder="请输入内容" clearable
@keyup.enter="handleQuery" />
>>>>>>> aab67593ebb8d9116738d0d867a6119ba61615c8
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button> <el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> <<<<<<< HEAD </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
</div> </div>
@ -47,14 +56,17 @@
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['plan:plan:edit']">修改</el-button> <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
v-hasPermi="['plan:plan:edit']">修改</el-button>
</el-col> </el-col>
<!-- 多文件上传按钮 --> <!-- 多文件上传按钮 -->
<el-col :span="2"> <el-col :span="2">
<el-button type="primary" plain icon="Upload" @click="openFileUploadDialog()" v-hasPermi="['plan:plan:upload']"> 上传文件 </el-button> <el-button type="primary" plain icon="Upload" @click="openFileUploadDialog()"
v-hasPermi="['plan:plan:upload']"> 上传文件 </el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['plan:plan:export']">导出</el-button> <el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['plan:plan:export']">导出</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
@ -93,35 +105,29 @@
</el-table-column> </el-table-column>
<el-table-column label="招标通知书" align="center" prop="tenderFile"> <el-table-column label="招标通知书" align="center" prop="tenderFile">
<template #default="scope"> <template #default="scope">
<el-link @click="handleView(scope.row, 'tenderFile')" type="primary" v-if="scope.row.tenderFile">查看</el-link> <el-link @click="handleView(scope.row, 'tenderFile')" type="primary"
v-if="scope.row.tenderFile">查看</el-link>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['plan:plan:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
v-hasPermi="['plan:plan:edit']"></el-button>
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card> </el-card>
<!-- 多文件上传对话框 --> <!-- 多文件上传对话框 -->
<el-dialog :title="fileUploadDialog.title" v-model="fileUploadDialog.visible" width="600px" append-to-body> <el-dialog :title="fileUploadDialog.title" v-model="fileUploadDialog.visible" width="600px" append-to-body>
<div class="upload-container"> <div class="upload-container">
<el-upload <el-upload class="upload-demo" drag action="" :http-request="handleFileUpload" :multiple="true"
class="upload-demo" :on-preview="handleFilePreview" :on-remove="handleFileRemove" :file-list="fileList" :auto-upload="false">
drag
action=""
:http-request="handleFileUpload"
:multiple="true"
:on-preview="handleFilePreview"
:on-remove="handleFileRemove"
:file-list="fileList"
:auto-upload="false"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon> <el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">点击或拖拽文件到此处上传</div> <div class="el-upload__text">点击或拖拽文件到此处上传</div>
<template #tip> <template #tip>
@ -140,7 +146,8 @@
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="planFormRef" :model="form" :rules="rules" label-width="120px"> <el-form ref="planFormRef" :model="form" :rules="rules" label-width="120px">
<!-- 调整项目ID表单项与目标代码一致自动关联当前项目并显示提示 --> <!-- 调整项目ID表单项与目标代码一致自动关联当前项目并显示提示 -->
<el-input v-model="form.projectId" placeholder="请输入项目ID" :readonly="!!currentProjectId" :disabled="!!currentProjectId" v-if="false" /> <el-input v-model="form.projectId" placeholder="请输入项目ID" :readonly="!!currentProjectId"
:disabled="!!currentProjectId" v-if="false" />
<template #help> <template #help>
<span v-if="currentProjectId" class="text-success">已自动关联当前选中项目</span> <span v-if="currentProjectId" class="text-success">已自动关联当前选中项目</span>
</template> </template>
@ -154,7 +161,8 @@
</el-form-item> </el-form-item>
<el-form-item label="计划招标时间" prop="plannedBiddingTime" v-if="false"> <el-form-item label="计划招标时间" prop="plannedBiddingTime" v-if="false">
<el-date-picker clearable v-model="form.plannedBiddingTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择计划招标时间" /> <el-date-picker clearable v-model="form.plannedBiddingTime" type="date" value-format="YYYY-MM-DD"
placeholder="请选择计划招标时间" />
</el-form-item> </el-form-item>
<!-- 核心修改招标方式改为下拉框根据限价自动赋值 --> <!-- 核心修改招标方式改为下拉框根据限价自动赋值 -->
@ -171,7 +179,8 @@
<!-- 限价输入添加@input事件实时触发招标方式判断 --> <!-- 限价输入添加@input事件实时触发招标方式判断 -->
<el-form-item label="限价(单位万元)" prop="limitPrice"> <el-form-item label="限价(单位万元)" prop="limitPrice">
<el-input v-model.number="form.limitPrice" placeholder="请输入限价(单位万元)" type="number" @input="autoSetBiddingMethod" /> <el-input v-model.number="form.limitPrice" placeholder="请输入限价(单位万元)" type="number"
@input="autoSetBiddingMethod" />
</el-form-item> </el-form-item>
<el-form-item label="合同额" prop="contractPrice"> <el-form-item label="合同额" prop="contractPrice">
@ -211,68 +220,180 @@
<el-button type="primary" @click="viewVisible = false">关闭</el-button> <el-button type="primary" @click="viewVisible = false">关闭</el-button>
</span> </span>
</template> </template>
=======
<el-button type="primary" plain :icon="Plus" @click="openDialog"
v-hasPermi="['tender:segmentedIndicatorPlanning:add']">新增</el-button>
</el-form-item>
</el-form>
</el-card>
<el-table :data="tableData" row-key="id" border v-loading="loading">
<el-table-column label="序号" width="80" align="center">
<template #default="scope">
{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="name" label="名称" />
<el-table-column prop="content" label="内容" />
<el-table-column prop="plannedBiddingTime" label="计划招标时间" align="center">
<template #default="scope">
<el-date-picker v-model="scope.row.plannedBiddingTime" type="date" value-format="YYYY-MM-DD"
placeholder="选择时间" />
</template>
</el-table-column>
<el-table-column prop="price" label="操作" align="center">
<template #default="scope">
<el-button type="warning" size="small" @click="handleDetail(scope.row)"
v-hasPermi="['tender:segmentedIndicatorPlanning:getMore']">详情</el-button>
<el-button type="primary" size="small" @click="handleSave(scope.row)"
v-hasPermi="['tender:segmentedIndicatorPlanning:edit']">修改</el-button>
<el-button type="danger" size="small" @click="delHandle(scope.row)"
v-hasPermi="['tender:segmentedIndicatorPlanning:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-tab-pane>
</el-tabs>
<el-dialog title="新增" v-model="dialogVisible" width="75%" draggable>
<el-row>
<el-col :span="11">
<el-form :model="form" :rules="rules" ref="formRef" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item label="内容" prop="content">
<el-input v-model="form.content" placeholder="请输入内容" type="textarea" clearable :rows="4" />
</el-form-item>
</el-form>
</el-col>
</el-row>
<el-row style="margin-top: 20px">
<el-col :span="24">
<el-card shadow="never">
<el-form :model="treeForm" :inline="true">
<el-form-item label="表名" prop="sheet">
<el-select v-model="treeForm.sheet" placeholder="选择表名" @change="changeSheet">
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="toggleExpandAll(true)">一键展开</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="toggleExpandAll(false)">一键收起</el-button>
</el-form-item>
</el-form>
</el-card>
<el-table :data="treeData" ref="treeTableRef" v-loading="treeLoading" row-key="id" border lazy
default-expand-all @selection-change="handleSelection">
<el-table-column type="selection" width="55" />
<el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" />
<!-- <el-table-column prop="quantity" label="数量" /> -->
<el-table-column prop="selectNum" label="选择数量" align="center">
<template #default="scope">
<el-input-number :model-value="scope.row.selectNum" @change="
(val) => {
scope.row.selectNum = val;
handleNumberChange(scope.row);
}
" :precision="2" :step="1" :controls="false" :max="Math.floor(scope.row.quantity)"
v-if="scope.row.quantity && scope.row.quantity != 0" />
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" align="center" />
<!-- <el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{ scope.row.price }}
</template>
</el-table-column> -->
</el-table>
</el-col>
</el-row>
<template #footer>
<el-button @click="closeDialog">取消</el-button>
<el-button type="primary" @click="submitForm(formRef)">确定</el-button>
</template>
</el-dialog>
<el-dialog title="详情" v-model="detailDialog" width="75%" draggable>
<el-table :data="detailData" v-loading="detailLoading" row-key="id" border lazy default-expand-all>
<el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" />
<el-table-column prop="unitPrice" label="单价" align="center" />
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{ scope.row.price }}
</template>
</el-table-column>
</el-table>
>>>>>>> aab67593ebb8d9116738d0d867a6119ba61615c8
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup name="Plan" lang="ts"> <script setup name="Plan" lang="ts">
import { listPlan, getPlan, addPlan, updatePlan, getSegmentedIndicatorPlanning } from '@/api/plan/plan/index'; import { listPlan, getPlan, addPlan, updatePlan, getSegmentedIndicatorPlanning } from '@/api/plan/plan/index';
import { PlanVO, PlanQuery, PlanForm } from '@/api/plan/plan/types'; import { PlanVO, PlanQuery, PlanForm } from '@/api/plan/plan/types';
import { getCurrentInstance, onMounted, ref, reactive, toRefs, computed, watch } from 'vue'; import { getCurrentInstance, onMounted, ref, reactive, toRefs, computed, watch } from 'vue';
import type { ElFormInstance } from 'element-plus'; import type { ElFormInstance } from 'element-plus';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { UploadFilled } from '@element-plus/icons-vue'; <<<<<<< HEAD
import type { UploadFile, UploadRawFile } from 'element-plus'; import { UploadFilled } from '@element-plus/icons-vue';
import { listByIds } from '@/api/system/oss'; import type { UploadFile, UploadRawFile } from 'element-plus';
import { listByIds } from '@/api/system/oss';
// 从用户Store获取当前选中项目及ID // 从用户Store获取当前选中项目及ID
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject); const currentProject = computed(() => userStore.selectedProject);
const currentProjectId = computed(() => currentProject.value?.id); const currentProjectId = computed(() => currentProject.value?.id);
const viewVisible = ref(false); const viewVisible = ref(false);
// 类型定义 // 类型定义
interface DialogOption { interface DialogOption {
visible: boolean; visible: boolean;
title: string; title: string;
} }
interface PageData<F, Q> { interface PageData<F, Q> {
form: F; form: F;
queryParams: Q; queryParams: Q;
rules: Record<string, any[]>; rules: Record<string, any[]>;
} }
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const planList = ref<PlanVO[]>([]); const planList = ref<PlanVO[]>([]);
const buttonLoading = ref(false); const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const ids = ref<Array<string | number>>([]); const ids = ref<Array<string | number>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const planFormRef = ref<ElFormInstance>(); const planFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
// 文件上传相关状态 // 文件上传相关状态
const fileUploadDialog = reactive({ const fileUploadDialog = reactive({
visible: false, visible: false,
title: '上传文件' title: '上传文件'
}); });
const fileList = ref<UploadFile[]>([]); const fileList = ref<UploadFile[]>([]);
const viewFileList = ref([]); const viewFileList = ref([]);
const uploadLoading = ref(false); const uploadLoading = ref(false);
const selectedPlanId = ref<string | number>(''); const selectedPlanId = ref<string | number>('');
// 初始化表单数据 // 初始化表单数据
const initFormData: PlanForm & { dictName?: string; content?: string } = { const initFormData: PlanForm & { dictName?: string; content?: string } = {
id: '1958116259107012609', id: '1958116259107012609',
projectId: currentProjectId.value, projectId: currentProjectId.value,
file: [], file: [],
@ -285,10 +406,10 @@ const initFormData: PlanForm & { dictName?: string; content?: string } = {
dictName: '1', dictName: '1',
name: 'test1', name: 'test1',
content: 'trhbhbt' content: 'trhbhbt'
}; };
// 页面数据 // 页面数据
const data = reactive<PageData<typeof initFormData, PlanQuery & { dictName?: string; content?: string }>>({ const data = reactive<PageData<typeof initFormData, PlanQuery & { dictName?: string; content?: string }>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@ -313,21 +434,21 @@ const data = reactive<PageData<typeof initFormData, PlanQuery & { dictName?: str
contractPrice: [{ required: true, message: '请输入合同额', trigger: 'blur' }], contractPrice: [{ required: true, message: '请输入合同额', trigger: 'blur' }],
content: [{ required: true, message: '请输入分包内容', trigger: 'blur' }] content: [{ required: true, message: '请输入分包内容', trigger: 'blur' }]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
// 监听当前项目ID变化 // 监听当前项目ID变化
watch(currentProjectId, (newVal) => { watch(currentProjectId, (newVal) => {
queryParams.value.projectId = newVal; queryParams.value.projectId = newVal;
if (!form.value.id) { if (!form.value.id) {
form.value.projectId = newVal; form.value.projectId = newVal;
} }
getList(); getList();
}); });
/** 查询招标计划列表 */ /** 查询招标计划列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
try { try {
const res = await listPlan(queryParams.value); const res = await listPlan(queryParams.value);
@ -336,23 +457,94 @@ const getList = async () => {
} catch (error) { } catch (error) {
console.error('获取招标计划列表失败', error); console.error('获取招标计划列表失败', error);
proxy?.$modal.msgError('获取招标计划列表失败'); proxy?.$modal.msgError('获取招标计划列表失败');
=======
import { getDicts } from '@/api/system/dict/data';
import { Plus } from '@element-plus/icons-vue';
import { FormInstance } from 'element-plus';
import { treeList, sheetList, segmentedIndicatorPlanning, getPlanningList, updatePlanning, delPlanning, getDetailsList } from '@/api/contract/index';
const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject);
const tabList = ref<any[]>([]);
const tableData = ref<any[]>([]);
const queryFormRef = ref();
const formRef = ref();
const activeTab = ref();
const queryParams = ref({
pageNum: 1,
pageSize: 10
});
const total = ref(0);
const queryForm = ref({
name: '',
content: ''
});
const dialogVisible = ref(false);
const form = ref({
name: '',
content: ''
});
const rules = ref({
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
});
const loading = ref(false);
//字典获取数据
const getTabsList = async () => {
const res = await getDicts('subcontracting_type');
if (res.code == 200) {
activeTab.value = res.data[0].dictValue;
tabList.value = res.data.map((item: any) => {
return {
label: item.dictLabel,
value: item.dictValue
};
});
getList();
}
};
//获取表格数据
const getList = async () => {
try {
loading.value = true;
const params = {
projectId: currentProject.value?.id,
...queryParams.value,
...queryForm.value,
dictName: activeTab.value
};
const res = await getPlanningList(params);
if (res.code == 200) {
tableData.value = res.rows;
total.value = res.total;
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取数据失败',
type: 'error'
});
>>>>>>> aab67593ebb8d9116738d0d867a6119ba61615c8
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; };
<<<<<<< HEAD
// const handleSuccess = (list, res, fileType) => { // const handleSuccess = (list, res, fileType) => {
// console.log(list); // console.log(list);
// const arr = list.map((item) => { // const arr = list.map((item) => {
// return { fileId: item.ossId, fileUrl: item.url, fileType }; // return { fileId: item.ossId, fileUrl: item.url, fileType };
// }); // });
// console.log(arr); // console.log(arr);
// if (!form.value.file) form.value.file = []; // if (!form.value.file) form.value.file = [];
// form.value.file = [...form.value.file, ...arr]; // form.value.file = [...form.value.file, ...arr];
// console.log(form.value.file); // console.log(form.value.file);
// }; // };
const handleView = async (row, type) => { const handleView = async (row, type) => {
const res = await listByIds(row[type]); const res = await listByIds(row[type]);
if (type == 'tenderFile') { if (type == 'tenderFile') {
viewVisible.value = true; viewVisible.value = true;
@ -360,42 +552,49 @@ const handleView = async (row, type) => {
} else { } else {
window.open(res.data[0].url); window.open(res.data[0].url);
} }
}; };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
}; };
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData, projectId: currentProjectId.value }; form.value = { ...initFormData, projectId: currentProjectId.value };
planFormRef.value?.resetFields(); planFormRef.value?.resetFields();
}; };
/** 搜索按钮操作 */ =======
const handleQuery = () => { const handleTabChange = (tabs: any) => {
queryParams.value.pageNum = 1; activeTab.value = tabs;
getList(); getList();
}; };
>>>>>>> aab67593ebb8d9116738d0d867a6119ba61615c8
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
<<<<<<< HEAD
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value.projectId = currentProjectId.value; queryParams.value.projectId = currentProjectId.value;
handleQuery(); handleQuery();
}; };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: PlanVO[]) => { const handleSelectionChange = (selection: PlanVO[]) => {
ids.value = selection.map((item) => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length !== 1; single.value = selection.length !== 1;
multiple.value = !selection.length; multiple.value = !selection.length;
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: PlanVO) => { const handleUpdate = async (row?: PlanVO) => {
reset(); reset();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
if (!_id) { if (!_id) {
@ -422,17 +621,17 @@ const handleUpdate = async (row?: PlanVO) => {
console.error('获取招标计划详情失败', error); console.error('获取招标计划详情失败', error);
proxy?.$modal.msgError('获取招标计划详情失败'); proxy?.$modal.msgError('获取招标计划详情失败');
} }
}; };
/** 核心:根据限价自动设置招标方式 */ /** 核心:根据限价自动设置招标方式 */
const autoSetBiddingMethod = () => { const autoSetBiddingMethod = () => {
const limit = form.value.limitPrice; const limit = form.value.limitPrice;
// 逻辑:限价>1000万→公招否则→邀标为空则清空 // 逻辑:限价>1000万→公招否则→邀标为空则清空
form.value.plannedBiddingMethod = limit ? (limit > 1000 ? '公招' : '邀标') : ''; form.value.plannedBiddingMethod = limit ? (limit > 1000 ? '公招' : '邀标') : '';
}; };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
planFormRef.value?.validate(async (valid: boolean) => { planFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
@ -454,12 +653,250 @@ const submitForm = () => {
} finally { } finally {
buttonLoading.value = false; buttonLoading.value = false;
} }
=======
queryForm.value.content = '';
queryForm.value.name = '';
handleQuery();
};
const openDialog = () => {
dialogVisible.value = true;
getSheetName();
};
const closeDialog = () => {
dialogVisible.value = false;
form.value = {
name: '',
content: ''
};
formRef.value?.resetFields();
};
const treeData = ref<any[]>([]);
const treeForm = ref({
sheet: ''
});
const sheets = ref<any[]>([]);
const treeTableRef = ref();
const isExpandAll = ref(false);
const treeLoading = ref(false);
const selectionData = ref<any>([]);
//获取表名
const getSheetName = async () => {
try {
const params = {
projectId: currentProject.value?.id
// versions: queryForm.value.versions
};
const res = await sheetList(params);
if (res.code == 200) {
sheets.value = res.data;
if (res.data.length > 0) {
treeForm.value.sheet = res.data[0];
} else {
treeForm.value.sheet = '';
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
getTreeList();
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取表名失败',
type: 'warning'
});
}
};
const handleSelection = (selection: any) => {
selectionData.value = selection;
};
const handleNumberChange = (row: any) => {
const selectedIds = selectionData.value.map((item) => item.id);
nextTick(() => {
treeTableRef.value.toggleRowSelection(row, true);
});
restoreSelections(treeData.value, selectedIds);
};
//寻找节点选择
const restoreSelections = (data, selectedIds) => {
const traverse = (nodes) => {
nodes.forEach((node) => {
if (selectedIds.includes(node.id)) {
nextTick(() => {
treeTableRef.value.toggleRowSelection(node, true);
});
}
if (node.children && node.children.length > 0) {
traverse(node.children);
}
});
};
traverse(data);
};
watch(
() => selectionData.value,
(newVal, oldVal) => {
console.log(newVal, 55555555555555555);
}
);
//选择表名
const changeSheet = () => {
getTreeList();
};
const toggleExpandAll = (isExpand: boolean) => {
treeData.value.forEach((row) => {
treeTableRef.value.toggleRowExpansion(row, isExpand);
});
isExpandAll.value = isExpand;
};
//打开获取表数据
const getTreeList = async () => {
try {
treeLoading.value = true;
const params = {
projectId: currentProject.value?.id,
sheet: treeForm.value.sheet
};
const res = await treeList(params);
if (res.code == 200) {
res.data.forEach((item: any) => {
item.selectNum = '';
});
treeData.value = res.data;
}
} catch (error) {
console.log(error);
ElMessage({
message: '获取表格失败',
type: 'error'
});
} finally {
treeLoading.value = false;
}
};
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(async (valid, fields) => {
if (valid) {
try {
if (selectionData.value.length == 0) {
ElMessage({
message: '请选择项目材料',
type: 'warning'
});
return;
}
const newSelectionData = selectionData.value.filter((item) => item.quantity != '' && item.quantity != null);
if (newSelectionData.some((item) => item.selectNum == '' || item.selectNum == null)) {
ElMessage.error('存在未填写数量的工程或费用名称,请检查');
return;
}
const limitListBos = newSelectionData.map((item: any) => {
return {
limitListId: item.id,
num: item.selectNum
};
});
const params = {
projectId: currentProject.value?.id,
...form.value,
dictName: activeTab.value,
limitListBos,
type: activeTab.value == '2' ? '3' : '2'
};
return;
const res = await segmentedIndicatorPlanning(params);
if (res.code == 200) {
ElMessage({
message: '新增成功',
type: 'success'
});
closeDialog();
getList();
}
} catch (error) {
console.log(error);
}
} else {
console.log('error submit!', fields);
} }
}); });
}; };
//确定修改
const handleSave = (row: any) => {
try {
if (!row.plannedBiddingTime) {
ElMessage({
message: '请输入计划招标时间',
type: 'warning'
});
return;
}
updatePlanning(row).then((res) => {
if (res.code == 200) {
ElMessage({
message: '修改成功',
type: 'success'
});
getList();
}
});
} catch (error) {
console.log(error);
ElMessage({
message: '修改失败',
type: 'error'
});
}
};
//删除
const delHandle = (row: any) => {
try {
const params = { ids: row.id };
delPlanning(params).then((res) => {
if (res.code == 200) {
ElMessage({
message: '删除成功',
type: 'success'
});
getList();
}
});
} catch (error) {
console.log(error);
ElMessage({
message: '删除失败',
type: 'error'
});
}
};
const detailDialog = ref();
const detailData = ref([]);
const detailLoading = ref(false);
/** 删除按钮操作(原代码存在但模板未引用,保留逻辑) */ //详情
const handleDelete = async (row?: PlanVO) => { const handleDetail = (row: any) => {
detailDialog.value = true;
getDetails(row);
};
const getDetails = (row: any) => {
getDetailsList({ id: row.id }).then((res) => {
if (res.code == 200) {
detailData.value = res.data;
>>>>>>> aab67593ebb8d9116738d0d867a6119ba61615c8
}
});
};
<<<<<<< HEAD
/** 删除按钮操作(原代码存在但模板未引用,保留逻辑) */
const handleDelete = async (row?: PlanVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
if (!_ids || (_ids instanceof Array && _ids.length === 0)) { if (!_ids || (_ids instanceof Array && _ids.length === 0)) {
proxy?.$modal.msgWarning('请选择需要删除的招标计划'); proxy?.$modal.msgWarning('请选择需要删除的招标计划');
@ -476,10 +913,10 @@ const handleDelete = async (row?: PlanVO) => {
proxy?.$modal.msgError('删除失败,请重试'); proxy?.$modal.msgError('删除失败,请重试');
} }
} }
}; };
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
proxy?.download( proxy?.download(
'plan/plan/export', 'plan/plan/export',
{ {
@ -487,10 +924,10 @@ const handleExport = () => {
}, },
`plan_${new Date().getTime()}.xlsx` `plan_${new Date().getTime()}.xlsx`
); );
}; };
/** 打开文件上传对话框 */ /** 打开文件上传对话框 */
const openFileUploadDialog = () => { const openFileUploadDialog = () => {
if (ids.value.length !== 1) { if (ids.value.length !== 1) {
proxy?.$modal.msgWarning('请选择单个招标计划进行文件上传'); proxy?.$modal.msgWarning('请选择单个招标计划进行文件上传');
return; return;
@ -498,10 +935,10 @@ const openFileUploadDialog = () => {
selectedPlanId.value = ids.value[0]; selectedPlanId.value = ids.value[0];
fileList.value = []; fileList.value = [];
fileUploadDialog.visible = true; fileUploadDialog.visible = true;
}; };
/** 处理文件上传 */ /** 处理文件上传 */
const handleFileUpload = async (params: { file: UploadRawFile }) => { const handleFileUpload = async (params: { file: UploadRawFile }) => {
const formData = new FormData(); const formData = new FormData();
formData.append('file', params.file); formData.append('file', params.file);
formData.append('planId', selectedPlanId.value as string); formData.append('planId', selectedPlanId.value as string);
@ -513,10 +950,10 @@ const handleFileUpload = async (params: { file: UploadRawFile }) => {
console.error('文件上传失败', error); console.error('文件上传失败', error);
throw error; throw error;
} }
}; };
/** 提交文件上传 */ /** 提交文件上传 */
const submitFileUpload = async () => { const submitFileUpload = async () => {
if (fileList.value.length === 0) { if (fileList.value.length === 0) {
proxy?.$modal.msgWarning('请选择需要上传的文件'); proxy?.$modal.msgWarning('请选择需要上传的文件');
return; return;
@ -537,55 +974,67 @@ const submitFileUpload = async () => {
} finally { } finally {
uploadLoading.value = false; uploadLoading.value = false;
} }
}; };
/** 取消文件上传 */ /** 取消文件上传 */
const cancelFileUpload = () => { const cancelFileUpload = () => {
fileList.value = []; fileList.value = [];
fileUploadDialog.visible = false; fileUploadDialog.visible = false;
}; };
/** 文件预览 */ /** 文件预览 */
const handleFilePreview = (file: UploadFile) => { const handleFilePreview = (file: UploadFile) => {
console.log('预览文件:', file); console.log('预览文件:', file);
// 可扩展通过文件URL打开预览 // 可扩展通过文件URL打开预览
// if (file.url) window.open(file.url); // if (file.url) window.open(file.url);
}; };
/** 移除文件 */ /** 移除文件 */
const handleFileRemove = (file: UploadFile) => { const handleFileRemove = (file: UploadFile) => {
console.log('移除文件:', file); console.log('移除文件:', file);
}; };
// 监听项目id刷新数据 // 监听项目id刷新数据
const listeningProject = watch( const listeningProject = watch(
() => currentProject.value?.id, () => currentProject.value?.id,
(nid, oid) => { (nid, oid) => {
queryParams.value.projectId = nid; queryParams.value.projectId = nid;
form.value.projectId = nid; form.value.projectId = nid;
getList(); getList();
} }
); );
onUnmounted(() => { onUnmounted(() => {
listeningProject(); listeningProject();
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.mb-4 { <<<<<<< HEAD .mb-4 {
margin-bottom: 16px; margin-bottom: 16px;
} }
.el-table .small-padding .cell {
.el-table .small-padding .cell {
padding: 0 5px; padding: 0 5px;
} }
.el-table .fixed-width {
.el-table .fixed-width {
width: 120px !important; width: 120px !important;
} }
.el-table-column .el-button--text + .el-button--text {
.el-table-column .el-button--text+.el-button--text {
margin-left: 10px; margin-left: 10px;
} }
.upload-container {
.upload-container {
padding: 10px 0; padding: 10px 0;
}
</style> =======.Region {
width: 100%;
height: 100%;
border: 1px solid #ccc;
border-radius: 10px;
padding: 10px;
>>>>>>>aab67593ebb8d9116738d0d867a6119ba61615c8
}
</style>