合并
@ -34,6 +34,7 @@
|
||||
"diagram-js": "12.3.0",
|
||||
"didi": "9.0.2",
|
||||
"echarts": "5.5.0",
|
||||
"echarts-gl": "^2.0.9",
|
||||
"element-plus": "2.8.8",
|
||||
"esbuild": "^0.25.0",
|
||||
"ezuikit-js": "^8.1.10",
|
||||
|
@ -8,7 +8,7 @@ import { ListOfWinningBidsVO, ListOfWinningBidsForm, ListOfWinningBidsQuery } fr
|
||||
* @returns {*}
|
||||
*/
|
||||
|
||||
export const listListOfWinningBids = (query?: ListOfWinningBidsQuery): AxiosPromise<ListOfWinningBidsVO[]> => {
|
||||
export const listListOfWinningBids = (query) => {
|
||||
return request({
|
||||
url: '/bidding/listOfWinningBids/list',
|
||||
method: 'get',
|
||||
|
93
src/api/tender/index.ts
Normal file
@ -0,0 +1,93 @@
|
||||
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
|
||||
});
|
||||
};
|
||||
|
||||
//招标计划列表
|
||||
|
||||
export const tenderPlanList = (query: any): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/tender/biddingPlan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
//新增招标计划
|
||||
export const addTenderPlan = (query: any): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/tender/biddingPlan',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
//删除招标计划
|
||||
export const delTenderPlan = (query: any): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/tender/biddingPlan/' + query.ids,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
//修改招标计划
|
||||
export const updateTenderPlan = (query: any): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/tender/biddingPlan',
|
||||
method: 'put',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
//弹窗数据
|
||||
export const treeList = (query: any): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/tender/tenderPlanLimitList/getTree',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
//获取招标计划详情
|
||||
export const getTenderPlanDetail = (query: any): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/tender/biddingPlan/getMore',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
BIN
src/assets/large/down.png
Normal file
After Width: | Height: | Size: 438 B |
BIN
src/assets/large/title_bottom.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
src/assets/large/title_icon.png
Normal file
After Width: | Height: | Size: 731 B |
BIN
src/assets/large/top1.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/large/top2.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/assets/large/top3.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/large/top4.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/large/up.png
Normal file
After Width: | Height: | Size: 386 B |
10
src/assets/projectLarge/border.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="440" height="1" viewBox="0 0 440 1" fill="none">
|
||||
<path fill="url(#linear_border_216_180_0)" d="M440 0L439 0L439 1L440 1L440 0ZM437 0L435 0L435 1L437 1L437 0ZM433 0L431 0L431 1L433 1L433 0ZM429 0L427 0L427 1L429 1L429 0ZM425 0L423 0L423 1L425 1L425 0ZM421 0L419 0L419 1L421 1L421 0ZM417 0L415 0L415 1L417 1L417 0ZM413 0L411 0L411 1L413 1L413 0ZM409 0L407 0L407 1L409 1L409 0ZM405 0L403 0L403 1L405 1L405 0ZM401 0L399 0L399 1L401 1L401 0ZM397 0L395 0L395 1L397 1L397 0ZM393 0L391 0L391 1L393 1L393 0ZM389 0L387 0L387 1L389 1L389 0ZM385 0L383 0L383 1L385 1L385 0ZM381 0L379 0L379 1L381 1L381 0ZM377 0L375 0L375 1L377 1L377 0ZM373 0L371 0L371 1L373 1L373 0ZM369 0L367 0L367 1L369 1L369 0ZM365 0L363 0L363 1L365 1L365 0ZM361 0L359 0L359 1L361 1L361 0ZM357 0L355 0L355 1L357 1L357 0ZM353 0L351 0L351 1L353 1L353 0ZM349 0L347 0L347 1L349 1L349 0ZM345 0L343 0L343 1L345 1L345 0ZM341 0L339 0L339 1L341 1L341 0ZM337 0L335 0L335 1L337 1L337 0ZM333 0L331 0L331 1L333 1L333 0ZM329 0L327 0L327 1L329 1L329 0ZM325 0L323 0L323 1L325 1L325 0ZM321 0L319 0L319 1L321 1L321 0ZM317 0L315 0L315 1L317 1L317 0ZM313 0L311 0L311 1L313 1L313 0ZM309 0L307 0L307 1L309 1L309 0ZM305 0L303 0L303 1L305 1L305 0ZM301 0L299 0L299 1L301 1L301 0ZM297 0L295 0L295 1L297 1L297 0ZM293 0L291 0L291 1L293 1L293 0ZM289 0L287 0L287 1L289 1L289 0ZM285 0L283 0L283 1L285 1L285 0ZM281 0L279 0L279 1L281 1L281 0ZM277 0L275 0L275 1L277 1L277 0ZM273 0L271 0L271 1L273 1L273 0ZM269 0L267 0L267 1L269 1L269 0ZM265 0L263 0L263 1L265 1L265 0ZM261 0L259 0L259 1L261 1L261 0ZM257 0L255 0L255 1L257 1L257 0ZM253 0L251 0L251 1L253 1L253 0ZM249 0L247 0L247 1L249 1L249 0ZM245 0L243 0L243 1L245 1L245 0ZM241 0L239 0L239 1L241 1L241 0ZM237 0L235 0L235 1L237 1L237 0ZM233 0L231 0L231 1L233 1L233 0ZM229 0L227 0L227 1L229 1L229 0ZM225 0L223 0L223 1L225 1L225 0ZM221 0L219 0L219 1L221 1L221 0ZM217 0L215 0L215 1L217 1L217 0ZM213 0L211 0L211 1L213 1L213 0ZM209 0L207 0L207 1L209 1L209 0ZM205 0L203 0L203 1L205 1L205 0ZM201 0L199 0L199 1L201 1L201 0ZM197 0L195 0L195 1L197 1L197 0ZM193 0L191 0L191 1L193 1L193 0ZM189 0L187 0L187 1L189 1L189 0ZM185 0L183 0L183 1L185 1L185 0ZM181 0L179 0L179 1L181 1L181 0ZM177 0L175 0L175 1L177 1L177 0ZM173 0L171 0L171 1L173 1L173 0ZM169 0L167 0L167 1L169 1L169 0ZM165 0L163 0L163 1L165 1L165 0ZM161 0L159 0L159 1L161 1L161 0ZM157 0L155 0L155 1L157 1L157 0ZM153 0L151 0L151 1L153 1L153 0ZM149 0L147 0L147 1L149 1L149 0ZM145 0L143 0L143 1L145 1L145 0ZM141 0L139 0L139 1L141 1L141 0ZM137 0L135 0L135 1L137 1L137 0ZM133 0L131 0L131 1L133 1L133 0ZM129 0L127 0L127 1L129 1L129 0ZM125 0L123 0L123 1L125 1L125 0ZM121 0L119 0L119 1L121 1L121 0ZM117 0L115 0L115 1L117 1L117 0ZM113 0L111 0L111 1L113 1L113 0ZM109 0L107 0L107 1L109 1L109 0ZM105 0L103 0L103 1L105 1L105 0ZM101 0L99 0L99 1L101 1L101 0ZM97 0L95 0L95 1L97 1L97 0ZM93 0L91 0L91 1L93 1L93 0ZM89 0L87 0L87 1L89 1L89 0ZM85 0L83 0L83 1L85 1L85 0ZM81 0L79 0L79 1L81 1L81 0ZM77 0L75 0L75 1L77 1L77 0ZM73 0L71 0L71 1L73 1L73 0ZM69 0L67 0L67 1L69 1L69 0ZM65 0L63 0L63 1L65 1L65 0ZM61 0L59 0L59 1L61 1L61 0ZM57 0L55 0L55 1L57 1L57 0ZM53 0L51 0L51 1L53 1L53 0ZM49 0L47 0L47 1L49 1L49 0ZM45 0L43 0L43 1L45 1L45 0ZM41 0L39 0L39 1L41 1L41 0ZM37 0L35 0L35 1L37 1L37 0ZM33 0L31 0L31 1L33 1L33 0ZM29 0L27 0L27 1L29 1L29 0ZM25 0L23 0L23 1L25 1L25 0ZM21 0L19 0L19 1L21 1L21 0ZM17 0L15 0L15 1L17 1L17 0ZM13 0L11 0L11 1L13 1L13 0ZM9 0L7 0L7 1L9 1L9 0ZM5 0L3 0L3 1L5 1L5 0ZM1 0L0 0L0 1L1 1L1 0Z">
|
||||
</path>
|
||||
<defs>
|
||||
<linearGradient id="linear_border_216_180_0" x1="440" y1="0.5" x2="0" y2="0.5" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#1DD6FF" stop-opacity="0" />
|
||||
<stop offset="1" stop-color="#1DD6FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/assets/projectLarge/leftarrow.png
Normal file
After Width: | Height: | Size: 652 B |
12
src/assets/projectLarge/map.svg
Normal file
After Width: | Height: | Size: 370 KiB |
33
src/assets/projectLarge/people.svg
Normal file
@ -0,0 +1,33 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="42.20001220703125" height="44.0025634765625" viewBox="0 0 42.20001220703125 44.0025634765625" fill="none">
|
||||
<g opacity="0.8">
|
||||
<ellipse transform="matrix(0.9915240406990051, -0.12992359697818756, 0.13317112624645233, 0.9910931587219238, 0, 4.8818359375)" cx="18.787446539492898" cy="18.56099636814998" rx="18.787446539492898" ry="18.56099636814998" fill="url(#linear_fill_216_484)" >
|
||||
</ellipse>
|
||||
</g>
|
||||
<g clip-path="url(#clip-path-216_485)">
|
||||
<g filter="url(#filter_216_487)">
|
||||
<path d="M18.4577 26.933C16.6094 26.933 14.8691 26.2223 13.5609 24.9334C12.2528 23.6445 11.5316 21.9297 11.5316 20.1085C11.5316 18.2873 12.2528 16.5755 13.5609 15.2866C14.8691 13.9976 16.6094 13.287 18.4577 13.287C20.306 13.287 22.0463 13.9976 23.3545 15.2866C24.6626 16.5755 25.3839 18.2902 25.3839 20.1114C25.3839 21.9326 24.6626 23.6473 23.3545 24.9363C22.0463 26.2252 20.306 26.933 18.4577 26.933ZM18.4577 15.5886C15.9261 15.5886 13.8675 17.617 13.8675 20.1114C13.8675 22.6058 15.9261 24.6342 18.4577 24.6342C20.9893 24.6342 23.0479 22.6058 23.0479 20.1114C23.0479 17.617 20.9893 15.5886 18.4577 15.5886ZM29.2353 42.6332C28.59 42.6332 28.0673 42.1182 28.0673 41.4823L28.0673 35.5326C28.0673 33.1129 26.1606 31.122 23.7487 30.9436L21.0185 32.8597C19.4972 33.9271 17.4532 33.93 15.929 32.8684L13.1667 30.9436C10.7549 31.122 8.84812 33.1129 8.84812 35.5326L8.84812 41.4823C8.84812 42.1182 8.32544 42.6332 7.68013 42.6332C7.03482 42.6332 6.51215 42.1182 6.51215 41.4823L6.51215 35.5326C6.51215 33.6883 7.24214 31.9535 8.56488 30.6502C9.88762 29.3468 11.6484 28.6276 13.5201 28.6276C13.7624 28.6276 13.996 28.7024 14.1946 28.8376L17.278 30.9868C17.9934 31.4845 18.9512 31.4816 19.6637 30.9839L22.7179 28.8405C22.9165 28.7024 23.153 28.6276 23.3954 28.6276C25.2671 28.6276 27.0278 29.3468 28.3505 30.6502C29.6733 31.9535 30.4033 33.6883 30.4033 35.5326L30.4033 41.4823C30.4033 42.1182 29.8806 42.6332 29.2353 42.6332Z" fill="#FFFFFF" >
|
||||
</path>
|
||||
</g>
|
||||
<path d="M11.7564 42.6332C11.1111 42.6332 10.5884 42.1182 10.5884 41.4824L10.5884 38.7204C10.5884 38.0845 11.1111 37.5695 11.7564 37.5695C12.4017 37.5695 12.9244 38.0845 12.9244 38.7204L12.9244 41.4824C12.9244 42.1182 12.4017 42.6332 11.7564 42.6332ZM25.159 42.6332C24.5137 42.6332 23.991 42.1182 23.991 41.4824L23.991 38.7204C23.991 38.0845 24.5137 37.5695 25.159 37.5695C25.8043 37.5695 26.327 38.0845 26.327 38.7204L26.327 41.4824C26.327 42.1182 25.8043 42.6332 25.159 42.6332ZM23.7283 20.2093L8.39845 20.2093C7.75314 20.2093 7.23047 19.6943 7.23047 19.0584C7.23047 18.4226 7.75314 17.9076 8.39845 17.9076L23.7283 17.9076C24.3736 17.9076 24.8962 18.4226 24.8962 19.0584C24.8962 19.6943 24.3736 20.2093 23.7283 20.2093Z" fill="#FFFFFF" >
|
||||
</path>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient id="linear_fill_216_484" cx="0" cy="0" r="1" gradientTransform="translate(31.006426019848288 5.783235248388171) rotate(-151.06468116612422) scale(28.374047672813088, 28.374047672813088)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#1DD6FF" stop-opacity="0.78" />
|
||||
<stop offset="0.9861" stop-color="#1DD6FF" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
<clipPath id="clip-path-216_485">
|
||||
<path d="M2.54681 44.0025L34.2695 44.0025L34.2695 12.2961L2.54681 12.2961L2.54681 44.0025Z" fill="white"/>
|
||||
</clipPath>
|
||||
<filter id="filter_216_487" x="4.51214599609375" y="11.2869873046875" width="27.89111328125" height="33.34619140625" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="feFloodId_216_487"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha_216_487"/>
|
||||
<feOffset dx="0" dy="0"/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feComposite in2="hardAlpha_216_487" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.7450980392156863 0 0 0 0 0.9686274509803922 0 0 0 0.8 0"/>
|
||||
<feBlend mode="normal" in2="feFloodId_216_487" result="dropShadow_1_216_487"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="dropShadow_1_216_487" result="shape_216_487"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/assets/projectLarge/rightarrow.png
Normal file
After Width: | Height: | Size: 665 B |
4
src/assets/projectLarge/robot.svg
Normal file
After Width: | Height: | Size: 21 KiB |
6
src/assets/projectLarge/round.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12.4189453125" height="12" viewBox="0 0 12.4189453125 12" fill="none">
|
||||
<ellipse cx="6.20947265625" cy="6" rx="6.20947265625" ry="6" fill="#1DD6FF" fill-opacity="0.2">
|
||||
</ellipse>
|
||||
<ellipse cx="6.209228515625" cy="6" rx="3.104736328125" ry="3" fill="#1DD6FF" >
|
||||
</ellipse>
|
||||
</svg>
|
After Width: | Height: | Size: 370 B |
12
src/assets/projectLarge/section.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15" height="35" viewBox="0 0 15 35" fill="none">
|
||||
<rect x="1" y="0" width="14" height="35" fill="url(#linear_fill_216_187)" fill-opacity="1">
|
||||
</rect>
|
||||
<rect x="0" y="0" width="4" height="35" fill="#1DD6FF" >
|
||||
</rect>
|
||||
<defs>
|
||||
<linearGradient id="linear_fill_216_187" x1="1" y1="17.5" x2="13.62772" y2="17.5" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#43E2CB" stop-opacity="0.4" />
|
||||
<stop offset="1" stop-color="#43E2CB" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 580 B |
BIN
src/assets/projectLarge/swiper.png
Normal file
After Width: | Height: | Size: 104 KiB |
169
src/components/EchartBox/index.vue
Normal 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>
|
@ -75,6 +75,11 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/error/401.vue'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/ProjectScreen',
|
||||
component: () => import('@/views/ProjectScreen/index.vue'),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
component: Layout,
|
||||
|
174
src/views/ProjectScreen/components/centerPage.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="centerPage">
|
||||
<div class="topPage">
|
||||
<!-- 暂无 -->
|
||||
</div>
|
||||
<div class="endPage">
|
||||
<Title title="AI安全巡检" :prefix="true" />
|
||||
|
||||
<div class="swiper">
|
||||
<div class="arrow" :class="{ 'canUse': canLeft }" @click="swiperClick('left')">
|
||||
<el-icon size="16" color="skyblue">
|
||||
<ArrowLeft />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="swiper_content" ref="swiperContent">
|
||||
<div class="swiper_item" v-for="(item, index) in swiperList" :key="index">
|
||||
<img src="@/assets/projectLarge/swiper.png" alt="" class="swiper_img">
|
||||
<div class="swiper_date">{{ item.date }}</div>
|
||||
<div class="swiper_tip">{{ item.tip }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow" :class="{ 'canUse': canRight }" @click="swiperClick('right')">
|
||||
<el-icon size="16">
|
||||
<ArrowRight />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import Title from './title.vue'
|
||||
|
||||
const swiperList = ref([
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽1' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽2' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽3' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽4' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽5' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽6' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽7' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽8' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽9' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽10' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽11' },
|
||||
{ date: '03-18 15:00', tip: '未佩戴安全帽12' },
|
||||
])
|
||||
|
||||
const swiperContent = ref<HTMLDivElement>()
|
||||
const swiperItemWidth = ref(100)
|
||||
const canLeft = ref(false)
|
||||
const canRight = ref(true)
|
||||
|
||||
const swiperClick = (direction: 'left' | 'right') => {
|
||||
|
||||
if (direction === 'right') {
|
||||
if (swiperContent.value.scrollLeft >= swiperContent.value.scrollWidth - swiperContent.value.clientWidth) {
|
||||
canRight.value = false
|
||||
canLeft.value = true
|
||||
return
|
||||
}
|
||||
swiperContent.value.scrollLeft += swiperItemWidth.value
|
||||
} else {
|
||||
if (swiperContent.value.scrollLeft <= 0) {
|
||||
canLeft.value = false
|
||||
canRight.value = true
|
||||
return
|
||||
}
|
||||
swiperContent.value.scrollLeft -= swiperItemWidth.value
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
swiperItemWidth.value = swiperContent.value.children[0].clientWidth + 20
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.centerPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50vw;
|
||||
height: 100%;
|
||||
|
||||
.topPage,
|
||||
.endPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.topPage {
|
||||
flex: 1;
|
||||
margin-bottom: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.swiper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 20px 20px 10px 20px;
|
||||
|
||||
.swiper_content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
transition: all 0.3s ease-in-out;
|
||||
overflow-x: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.swiper_item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 133px;
|
||||
height: 84px;
|
||||
|
||||
.swiper_img {
|
||||
width: 133px;
|
||||
height: 84px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.swiper_date {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
}
|
||||
|
||||
.swiper_tip {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid skyblue;
|
||||
color: skyblue;
|
||||
|
||||
&:canUse {
|
||||
color: #000 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
198
src/views/ProjectScreen/components/header.vue
Normal file
@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<div class="header">
|
||||
<div class="header_left">
|
||||
<div class="header_left_img">
|
||||
<img src="@/assets/large/secure.png" style="width: 100%; height: 100%" />
|
||||
</div>
|
||||
<div style="font-size: 12px; padding-left: 10px">安全生产天数:</div>
|
||||
<div class="header_left_text">
|
||||
1,235
|
||||
<span style="font-size: 12px">天</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title">
|
||||
<div>XXX智慧工地管理平台</div>
|
||||
<div>XXX Smart Construction Stic Management Dashboard</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="top-bar">
|
||||
<!-- 左侧:天气图标 + 日期文字 -->
|
||||
<div class="left-section">
|
||||
<img src="@/assets/large/weather.png" alt="天气图标" />
|
||||
|
||||
<span>
|
||||
<span>多云 9°/18°</span>
|
||||
<span style="padding-left: 20px"> {{ week[date.week] }} ({{ date.ymd }})</span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- 分割线 -->
|
||||
<div class="divider">
|
||||
<div class="top-block"></div>
|
||||
<div class="bottom-block"></div>
|
||||
</div>
|
||||
<!-- 右侧:管理系统图标 + 文字 -->
|
||||
<div class="right-section">
|
||||
<img src="@/assets/large/setting.png" alt="设置图标" />
|
||||
<span>管理系统</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const week = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
const date = ref({
|
||||
ymd: '',
|
||||
hms: '',
|
||||
week: 0
|
||||
});
|
||||
|
||||
const setTime = () => {
|
||||
let date1 = new Date();
|
||||
let year: any = date1.getFullYear();
|
||||
let month: any = date1.getMonth() + 1;
|
||||
let day: any = date1.getDate();
|
||||
let hours: any = date1.getHours();
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
let minutes: any = date1.getMinutes();
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
let seconds: any = date1.getSeconds();
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
date.value.ymd = year + '-' + month + '-' + day;
|
||||
date.value.hms = hours + ':' + minutes + ':' + seconds;
|
||||
date.value.week = date1.getDay();
|
||||
};
|
||||
|
||||
// 添加定时器,每秒更新一次时间
|
||||
const timer = setInterval(setTime, 1000);
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
onUnmounted(() => {
|
||||
clearInterval(timer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header_left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.header_left_img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
box-sizing: border-box;
|
||||
// padding-right: 10px;
|
||||
}
|
||||
|
||||
.header_left_text {
|
||||
font-weight: 500;
|
||||
text-shadow: 0px 1.24px 6.21px rgba(25, 179, 250, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title>div:first-child {
|
||||
/* 第一个子元素的样式 */
|
||||
font-size: 38px;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.title>div:last-child {
|
||||
/* 最后一个子元素的样式 */
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* 顶部栏容器:Flex 水平布局 + 垂直居中 */
|
||||
.top-bar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
// background-color: #1e2128;
|
||||
color: #fff;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 左侧区域(天气 + 日期):自身也用 Flex 水平排列,确保元素在一行 */
|
||||
.left-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// margin-right: auto; /* 让右侧元素(管理系统)居右 */
|
||||
}
|
||||
|
||||
.left-section img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 8px;
|
||||
/* 图标与文字间距 */
|
||||
}
|
||||
|
||||
/* 分割线(视觉分隔,可根据需求调整样式) */
|
||||
.divider {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
height: 100%;
|
||||
/* 根据需要调整高度 */
|
||||
padding: 14px 10px;
|
||||
}
|
||||
|
||||
.divider .top-block {
|
||||
width: 2px;
|
||||
height: 7px;
|
||||
background: #19b5fb;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.divider .bottom-block {
|
||||
width: 2px;
|
||||
height: 7px;
|
||||
background: #19b5fb;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
/* 右侧区域(管理系统):图标 + 文字水平排列 */
|
||||
.right-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.right-section img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 6px;
|
||||
/* 图标与文字间距 */
|
||||
}
|
||||
</style>
|
204
src/views/ProjectScreen/components/leftPage.vue
Normal file
@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="leftPage">
|
||||
<div class="topPage">
|
||||
<Title title="项目公告" />
|
||||
|
||||
<div class="content">
|
||||
<div class="content_item" v-for="item in 6" :key="item">
|
||||
<div class="round">
|
||||
<div class="sub_round"></div>
|
||||
</div>
|
||||
<div class="ellipsis">2025年6月23日 重庆市两江新区广场前期准备与审批完毕区广场前期准备与审批完毕前期准备与审批完毕区广场前期准备与审批完毕</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="endPage">
|
||||
<Title title="人员情况" />
|
||||
|
||||
<div class="map">
|
||||
<img src="@/assets/projectLarge/map.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div class="attendance_tag">
|
||||
<div class="tag_item" v-for="(item, index) in tagList" :key="index">
|
||||
<img src="@/assets/projectLarge/people.svg" alt="">
|
||||
<div class="tag_title">{{ item.title }}</div>
|
||||
<div class="tag_info">
|
||||
{{ item.number }}
|
||||
<span style="font-size: 14px;">{{ index === 2 ? '%' : '人' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="attendance_list">
|
||||
<div class="attendance_item subfont">
|
||||
<div class="attendance_item_title"></div>
|
||||
<div class="attendance_item_title">在岗人数</div>
|
||||
<div class="attendance_item_title">出勤率</div>
|
||||
<div class="attendance_item_title">出勤时间</div>
|
||||
</div>
|
||||
<div v-for="item in list" :key="item.title" class="attendance_item">
|
||||
<div class="attendance_item_title">{{ item.title }}</div>
|
||||
<div class="attendance_item_number">{{ item.number }} <span class="subfont">人/{{ item.number }}</span></div>
|
||||
<div class="attendance_item_rate">{{ item.attendanceRate }} %</div>
|
||||
<div class="attendance_item_date subfont">{{ item.date }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import Title from './title.vue'
|
||||
|
||||
const list = ref([
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
{ title: '智慧系统运维', number: 30, attendanceRate: 100, date: '2025-08-05 08:10' },
|
||||
])
|
||||
|
||||
const tagList = ref([
|
||||
{ title: '出勤人数', number: 259 },
|
||||
{ title: '在岗人数', number: 100 },
|
||||
{ title: '出勤率', number: 100 },
|
||||
])
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.leftPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(25vw - 30px);
|
||||
margin: 0 15px;
|
||||
height: 100%;
|
||||
|
||||
.topPage,
|
||||
.endPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.endPage {
|
||||
flex: 1;
|
||||
margin-top: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
max-height: 100px;
|
||||
margin: 0 15px;
|
||||
padding: 0 10px;
|
||||
margin-top: 15px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: rgba(204, 204, 204, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(29, 214, 255, 0.78);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.content_item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
// position: relative;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
|
||||
.ellipsis {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.round {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-top: 3px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: rgba(29, 214, 255, 0.3);
|
||||
|
||||
.sub_round {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #1DD6FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.map {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.attendance_tag {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 30px;
|
||||
margin-top: 15px;
|
||||
|
||||
.tag_item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border: 1px dashed rgba(29, 214, 255, 0.3);
|
||||
padding: 10px 25px;
|
||||
|
||||
.tag_info {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
text-shadow: 0px 1.24px 6.21px rgba(0, 190, 247, 1);
|
||||
}
|
||||
|
||||
.tag_title {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.attendance_list {
|
||||
padding: 0px 30px;
|
||||
font-size: 14px;
|
||||
|
||||
.attendance_item {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 2fr 2fr 3fr;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.subfont {
|
||||
color: rgba(138, 149, 165, 1);
|
||||
}
|
||||
</style>
|
0
src/views/ProjectScreen/components/optionList.ts
Normal file
274
src/views/ProjectScreen/components/rightPage.vue
Normal file
@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<div class="leftPage">
|
||||
<div class="topPage">
|
||||
<Title title="项目概况" />
|
||||
|
||||
<div class="content">
|
||||
<div class="content_item">项目名称:智慧生态工地社区开发项目</div>
|
||||
<div class="content_item">项目位置:贵州省贵阳市乌当区(具体地块编号:01-123-11)</div>
|
||||
<div class="content_item">占地面积:约10000亩</div>
|
||||
<div class="content_item"> 土地性质:城镇住宅用地(兼容商业用地,容积率≤2.5)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="endPage">
|
||||
<!-- 饼图容器 -->
|
||||
<Title title="形象进度" />
|
||||
|
||||
<div ref="pieChartRef" class="echart" />
|
||||
<!-- 折线图容器 -->
|
||||
<div ref="lineChartRef" class="echart" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick } from "vue"
|
||||
import Title from './title.vue'
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 饼图相关
|
||||
const pieChartRef = ref<HTMLDivElement | null>(null);
|
||||
let pieChart: any = null;
|
||||
|
||||
// 折线图相关
|
||||
const lineChartRef = ref<HTMLDivElement | null>(null);
|
||||
let lineChart: any = null;
|
||||
|
||||
// 饼图数据
|
||||
const pieData = [
|
||||
{ name: '桩点浇筑', value: 13 },
|
||||
{ name: '水泥灌注', value: 7 },
|
||||
{ name: '箱变安装', value: 40 },
|
||||
{ name: '支架安装', value: 20 },
|
||||
{ name: '组件安装', value: 20 },
|
||||
]
|
||||
|
||||
// 折线图数据
|
||||
const barData = {
|
||||
xAxis: ['地块1', '地块2', '地块3', '地块4', '地块5', '地块6'],
|
||||
series: [
|
||||
{
|
||||
name: '计划流转面积',
|
||||
data: [70, 25, 45, 115, 70, 85]
|
||||
},
|
||||
{
|
||||
name: '已流转面积',
|
||||
data: [105, 30, 150, 65, 80, 200]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 饼图配置
|
||||
const pieOption = {
|
||||
series: {
|
||||
type: 'pie',
|
||||
data: pieData,
|
||||
radius: [50, 80],
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth: 1
|
||||
},
|
||||
label: {
|
||||
alignTo: 'edge',
|
||||
formatter: '{name|{b}}\n{percent|{c} %}',
|
||||
minMargin: 10,
|
||||
edgeDistance: 20,
|
||||
lineHeight: 15,
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 12,
|
||||
color: '#fff'
|
||||
},
|
||||
percent: {
|
||||
fontSize: 12,
|
||||
color: '#fff'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: 'bottom'
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// 柱状图配置
|
||||
const barOption = {
|
||||
legend: {
|
||||
data: ['计划流转面积', '已流转面积'],
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: barData.xAxis
|
||||
},
|
||||
yAxis: {
|
||||
name: '单位:m²',
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: [], // 空数据,仅用于承载markArea
|
||||
markArea: {
|
||||
silent: true, // 背景不响应交互
|
||||
data: (() => {
|
||||
const groupCount = 3; // 共3组(6个月 ÷ 2)
|
||||
const groupWidth = 1.8; // 每组背景宽度(覆盖2根柱子)
|
||||
const bgData = [];
|
||||
|
||||
for (let i = 0; i < groupCount; i++) {
|
||||
const startX = i * 2 - 0.9; // 每组起始位置
|
||||
const endX = startX + groupWidth; // 每组结束位置
|
||||
bgData.push([
|
||||
{ xAxis: startX, yAxis: 0 },
|
||||
{ xAxis: endX, yAxis: 100 }
|
||||
]);
|
||||
}
|
||||
return bgData;
|
||||
})(),
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 255, 255, 0.05)',
|
||||
borderRadius: 4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '计划流转面积',
|
||||
type: 'bar',
|
||||
data: barData.series[0].data,
|
||||
barWidth: 15, // 柱形宽度
|
||||
itemStyle: {
|
||||
color: 'rgb(29, 253, 253)'
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '已流转面积',
|
||||
type: 'bar',
|
||||
data: barData.series[1].data,
|
||||
barWidth: 15,
|
||||
itemStyle: {
|
||||
color: '#rgb(25, 181, 251)'
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 初始化饼图
|
||||
const initPieChart = () => {
|
||||
if (!pieChartRef.value) {
|
||||
console.error('未找到饼图容器元素');
|
||||
return;
|
||||
}
|
||||
pieChart = echarts.init(pieChartRef.value, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false
|
||||
});
|
||||
pieChart.setOption(pieOption);
|
||||
}
|
||||
|
||||
// 初始化折线图
|
||||
const initLineChart = () => {
|
||||
if (!lineChartRef.value) {
|
||||
console.error('未找到折线图容器元素');
|
||||
return;
|
||||
}
|
||||
lineChart = echarts.init(lineChartRef.value, null, {
|
||||
renderer: 'canvas',
|
||||
useDirtyRect: false
|
||||
});
|
||||
lineChart.setOption(barOption);
|
||||
}
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (pieChart) pieChart.resize();
|
||||
if (lineChart) lineChart.resize();
|
||||
};
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initPieChart();
|
||||
initLineChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
});
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (pieChart) {
|
||||
pieChart.dispose();
|
||||
pieChart = null;
|
||||
}
|
||||
if (lineChart) {
|
||||
lineChart.dispose();
|
||||
lineChart = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.leftPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(25vw - 30px);
|
||||
margin: 0 15px;
|
||||
height: 100%;
|
||||
|
||||
.topPage,
|
||||
.endPage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.endPage {
|
||||
flex: 1;
|
||||
margin-top: 23px;
|
||||
|
||||
.echart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 10px 35px;
|
||||
|
||||
.content_item {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 247, 255, 1);
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ellipse {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subfont {
|
||||
color: rgba(138, 149, 165, 1);
|
||||
}
|
||||
</style>
|
45
src/views/ProjectScreen/components/title.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="title">
|
||||
<div class="title_icon">
|
||||
<img src="@/assets/projectLarge/section.svg" alt="">
|
||||
<img src="@/assets/projectLarge/border.svg" alt="">
|
||||
</div>
|
||||
<div v-if="prefix">
|
||||
<img src="@/assets/projectLarge/robot.svg" alt="" style="width: 20px; height: 20px;margin-right: 5px;">
|
||||
</div>
|
||||
<div>{{ title }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题'
|
||||
},
|
||||
prefix: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
font-family: 'AlimamaShuHeiTi', sans-serif;
|
||||
|
||||
.title_icon {
|
||||
position: relative;
|
||||
|
||||
&>img:last-child {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
46
src/views/ProjectScreen/index.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="large-screen">
|
||||
<Header />
|
||||
<div class="nav">
|
||||
<leftPage />
|
||||
<centerPage />
|
||||
<rightPage />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Header from './components/header.vue';
|
||||
import leftPage from './components/leftPage.vue';
|
||||
import centerPage from './components/centerPage.vue';
|
||||
import rightPage from './components/rightPage.vue';
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.large-screen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: url('@/assets/large/bg.png') no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-color: rgba(4, 7, 17, 1);
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 15rpx;
|
||||
width: 100%;
|
||||
height: calc(100vh - 100px);
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav_left,
|
||||
.nav_right {
|
||||
margin: 0 15px 15px 15px;
|
||||
}
|
||||
|
||||
.nav_center {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
@ -32,7 +32,7 @@
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleExport()" v-hasPermi="['bidding:biddingLimitList:export']">导出excel</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<!-- <el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
v-if="versionObj.status == 'draft' || versionObj.status == 'back'"
|
||||
@ -51,7 +51,7 @@
|
||||
v-if="versionObj.status != 'draft'"
|
||||
>查看流程</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
</el-card>
|
||||
</transition>
|
||||
|
@ -7,9 +7,22 @@
|
||||
<div class="bg-blue-50 px-6 py-4 rounded-t-xl mb-0">
|
||||
<h3 class="el-card__header-title text-lg font-semibold text-blue-800">投标项目信息填写</h3>
|
||||
<span>{{ currentProject.name }}</span>
|
||||
<div style="margin-top: 10px">
|
||||
<el-button @click="isDisabled = false" type="primary" class="px-8 py-2.5 transition-all duration-300 font-medium" v-if="isDisabled">
|
||||
点击编辑
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="listOfWinningBidsFormRef" :model="form" :rules="rules" label-width="150px" class="p-6 pt-4" style="background-color: #ffffff">
|
||||
<el-form
|
||||
:disabled="isDisabled"
|
||||
ref="listOfWinningBidsFormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="150px"
|
||||
class="p-6 pt-4"
|
||||
style="background-color: #ffffff"
|
||||
>
|
||||
<el-row :gutter="32">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="中标价(美元)" prop="winningBidOriginal" class="rounded-lg border border-gray-100 p-1 mb-5">
|
||||
@ -100,14 +113,14 @@
|
||||
<el-input v-model="form.projectNumbering" placeholder="请输入项目编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<!-- <el-col :span="12">
|
||||
<el-form-item label="项目状态" prop="projectStatus" class="rounded-lg border border-gray-100 p-1 mb-5">
|
||||
<el-input v-model="form.projectStatus" placeholder="请输入项目状态(如:进行中/已完成)" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-row class="mt-8">
|
||||
<el-row v-if="!isDisabled" class="mt-8">
|
||||
<el-col :span="24" class="text-center">
|
||||
<el-button
|
||||
:loading="buttonLoading"
|
||||
@ -129,7 +142,7 @@
|
||||
|
||||
<script setup name="ListOfWinningBidsForm" lang="ts">
|
||||
import { ref, reactive, toRefs, watch, onMounted, onUnmounted, getCurrentInstance, ComponentInternalInstance, computed } from 'vue';
|
||||
import { addListOfWinningBids, updateListOfWinningBids, getListOfWinningBids } from '@/api/bidding/listOfWinningBids';
|
||||
import { addListOfWinningBids, updateListOfWinningBids, listListOfWinningBids, getListOfWinningBids } from '@/api/bidding/listOfWinningBids';
|
||||
import { ListOfWinningBidsVO, ListOfWinningBidsForm } from '@/api/bidding/listOfWinningBids/types';
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { ElFormInstance, ElMessage } from 'element-plus';
|
||||
@ -145,6 +158,7 @@ const currentProject = computed(() => userStore.selectedProject);
|
||||
const listOfWinningBidsFormRef = ref<ElFormInstance>();
|
||||
// 加载状态
|
||||
const buttonLoading = ref(false);
|
||||
const isDisabled = ref(false);
|
||||
// 表单初始数据
|
||||
const initFormData: ListOfWinningBidsForm = {
|
||||
id: undefined,
|
||||
@ -189,7 +203,6 @@ const data = reactive({
|
||||
|
||||
// 解构响应式数据
|
||||
const { form, rules } = toRefs(data);
|
||||
|
||||
/**
|
||||
* 计算人民币中标价
|
||||
* 显式触发的计算函数,确保执行时机可靠
|
||||
@ -216,16 +229,21 @@ const calculateWinningBid = () => {
|
||||
const initData = async () => {
|
||||
try {
|
||||
if (currentProject.value?.id) {
|
||||
const res = await getListOfWinningBids(currentProject.value.id);
|
||||
if (res.data && res.data.id) {
|
||||
Object.assign(form.value, res.data);
|
||||
// 初始化时手动触发一次计算
|
||||
setTimeout(calculateWinningBid, 0);
|
||||
const res = await listListOfWinningBids({ projectId: currentProject.value.id });
|
||||
if (res.code == 200) {
|
||||
console.log(res.data);
|
||||
resetForm();
|
||||
if (!res.data) {
|
||||
isDisabled.value = false;
|
||||
return;
|
||||
} else {
|
||||
Object.assign(form.value, res.data);
|
||||
}
|
||||
isDisabled.value = true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化数据失败:', error);
|
||||
ElMessage.error('初始化数据失败');
|
||||
// ElMessage.error('初始化数据失败');
|
||||
}
|
||||
};
|
||||
|
||||
@ -239,16 +257,10 @@ const submitForm = () => {
|
||||
try {
|
||||
// 提交前确保计算正确
|
||||
calculateWinningBid();
|
||||
|
||||
form.value.projectId = currentProject.value?.id;
|
||||
if (form.value.id) {
|
||||
await updateListOfWinningBids(form.value);
|
||||
} else {
|
||||
await addListOfWinningBids(form.value);
|
||||
}
|
||||
|
||||
await addListOfWinningBids(form.value);
|
||||
isDisabled.value = true;
|
||||
ElMessage.success('提交成功');
|
||||
resetForm();
|
||||
} catch (error) {
|
||||
ElMessage.error('提交失败,请重试');
|
||||
console.error('提交表单失败:', error);
|
||||
@ -258,7 +270,6 @@ const submitForm = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
@ -266,7 +277,6 @@ const resetForm = () => {
|
||||
form.value = { ...initFormData, projectId: currentProject.value?.id };
|
||||
listOfWinningBidsFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/**
|
||||
* 监听项目ID变化 - 重新初始化数据
|
||||
*/
|
||||
|
@ -64,6 +64,12 @@
|
||||
<el-col :span="24">
|
||||
<el-card shadow="never">
|
||||
<el-form :model="treeForm" :inline="true">
|
||||
<el-form-item label="版本" prop="versions">
|
||||
<el-select v-model="treeForm.versions" placeholder="选择版本" @change="changeSheet">
|
||||
<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="treeForm.sheet" placeholder="选择表名" @change="changeSheet">
|
||||
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" />
|
||||
@ -146,7 +152,16 @@ import { useUserStoreHook } from '@/store/modules/user';
|
||||
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';
|
||||
import {
|
||||
treeList,
|
||||
sheetList,
|
||||
segmentedIndicatorPlanning,
|
||||
getPlanningList,
|
||||
updatePlanning,
|
||||
delPlanning,
|
||||
getDetailsList,
|
||||
obtainAllVersionNumbers
|
||||
} from '@/api/contract/index';
|
||||
|
||||
const userStore = useUserStoreHook();
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
@ -198,7 +213,8 @@ const getList = async () => {
|
||||
projectId: currentProject.value?.id,
|
||||
...queryParams.value,
|
||||
...queryForm.value,
|
||||
dictName: activeTab.value
|
||||
dictName: activeTab.value,
|
||||
type: '1'
|
||||
};
|
||||
const res = await getPlanningList(params);
|
||||
if (res.code == 200) {
|
||||
@ -234,7 +250,7 @@ const resetQuery = () => {
|
||||
|
||||
const openDialog = () => {
|
||||
dialogVisible.value = true;
|
||||
getSheetName();
|
||||
getVersionNums();
|
||||
};
|
||||
const closeDialog = () => {
|
||||
dialogVisible.value = false;
|
||||
@ -246,19 +262,54 @@ const closeDialog = () => {
|
||||
};
|
||||
const treeData = ref<any[]>([]);
|
||||
const treeForm = ref({
|
||||
sheet: ''
|
||||
sheet: '',
|
||||
versions: ''
|
||||
});
|
||||
const sheets = ref<any[]>([]);
|
||||
const treeTableRef = ref();
|
||||
const isExpandAll = ref(false);
|
||||
const treeLoading = ref(false);
|
||||
const selectionData = ref<any>([]);
|
||||
const options = ref<any[]>([]);
|
||||
//获取版本号
|
||||
const getVersionNums = async () => {
|
||||
try {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
workOrderType: '1',
|
||||
pageSize: 1000,
|
||||
pageNum: 1
|
||||
};
|
||||
|
||||
const res = await obtainAllVersionNumbers(params);
|
||||
if (res.code == 200) {
|
||||
options.value = res.data;
|
||||
if (res.data.length > 0) {
|
||||
treeForm.value.versions = res.data[0].versions;
|
||||
|
||||
getSheetName();
|
||||
} else {
|
||||
treeForm.value.versions = '';
|
||||
ElMessage({
|
||||
message: '获取版本号失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '获取版本号失败',
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
};
|
||||
//获取表名
|
||||
const getSheetName = async () => {
|
||||
try {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id
|
||||
// versions: queryForm.value.versions
|
||||
projectId: currentProject.value?.id,
|
||||
versions: treeForm.value.versions
|
||||
};
|
||||
const res = await sheetList(params);
|
||||
if (res.code == 200) {
|
||||
@ -333,7 +384,9 @@ const getTreeList = async () => {
|
||||
treeLoading.value = true;
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: treeForm.value.sheet
|
||||
sheet: treeForm.value.sheet,
|
||||
versions: treeForm.value.versions,
|
||||
type: '1'
|
||||
};
|
||||
const res = await treeList(params);
|
||||
if (res.code == 200) {
|
||||
@ -380,7 +433,10 @@ const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
projectId: currentProject.value?.id,
|
||||
...form.value,
|
||||
dictName: activeTab.value,
|
||||
limitListBos
|
||||
limitListBos,
|
||||
type: '1',
|
||||
versions: treeForm.value.versions,
|
||||
sheet: treeForm.value.sheet
|
||||
};
|
||||
const res = await segmentedIndicatorPlanning(params);
|
||||
if (res.code == 200) {
|
||||
@ -409,7 +465,7 @@ const handleSave = (row: any) => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
updatePlanning(row).then((res) => {
|
||||
updatePlanning({ ...row, type: '1' }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
@ -420,10 +476,6 @@ const handleSave = (row: any) => {
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '修改失败',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
//删除
|
||||
@ -441,10 +493,6 @@ const delHandle = (row: any) => {
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
ElMessage({
|
||||
message: '删除失败',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
const detailDialog = ref();
|
||||
@ -457,13 +505,22 @@ const handleDetail = (row: any) => {
|
||||
getDetails(row);
|
||||
};
|
||||
const getDetails = (row: any) => {
|
||||
getDetailsList({ id: row.id }).then((res) => {
|
||||
getDetailsList({ id: row.id, type: '1' }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
detailData.value = res.data;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//监听项目id刷新数据
|
||||
const listeningProject = watch(
|
||||
() => currentProject.value?.id,
|
||||
(nid, oid) => {
|
||||
getTabsList();
|
||||
}
|
||||
);
|
||||
onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
onMounted(() => {
|
||||
getTabsList();
|
||||
});
|
||||
|
@ -3,11 +3,11 @@
|
||||
<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-form-item label="版本号" prop="versions">
|
||||
<el-select v-model="queryForm.versions" placeholder="选择版本号" @change="changeVersions">
|
||||
<el-option v-for="item in options" :key="item" :label="item" :value="item" />
|
||||
<el-option v-for="item in options" :key="item.versions" :label="item.versions" :value="item.versions" />
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
</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" />
|
||||
@ -100,6 +100,7 @@ const getVersionNums = async () => {
|
||||
try {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
workOrderType: '1',
|
||||
pageSize: 1000,
|
||||
pageNum: 1
|
||||
};
|
||||
@ -108,7 +109,8 @@ const getVersionNums = async () => {
|
||||
if (res.code == 200) {
|
||||
options.value = res.data;
|
||||
if (res.data.length > 0) {
|
||||
queryForm.value.versions = res.data[0];
|
||||
queryForm.value.versions = res.data[0].versions;
|
||||
|
||||
getSheetName();
|
||||
} else {
|
||||
queryForm.value.versions = '';
|
||||
@ -140,8 +142,8 @@ const changeSheet = () => {
|
||||
const getSheetName = async () => {
|
||||
try {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id
|
||||
// versions: queryForm.value.versions
|
||||
projectId: currentProject.value?.id,
|
||||
versions: queryForm.value.versions
|
||||
};
|
||||
const res = await sheetList(params);
|
||||
if (res.code == 200) {
|
||||
@ -172,7 +174,8 @@ const getTableData = async () => {
|
||||
const params = {
|
||||
projectId: currentProject.value?.id,
|
||||
versions: queryForm.value.versions,
|
||||
sheet: queryForm.value.sheet
|
||||
sheet: queryForm.value.sheet,
|
||||
type: '1'
|
||||
};
|
||||
const res = await listBillofquantitiesLimitList(params);
|
||||
if (res.code == 200) {
|
||||
@ -201,7 +204,7 @@ const handleSave = (row: any) => {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
updatePrice(row).then((res) => {
|
||||
updatePrice({ ...row, type: '1' }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
@ -230,7 +233,7 @@ const importExcel = (options: any): any => {
|
||||
let formData = new FormData();
|
||||
formData.append('file', options.file);
|
||||
loading.value = true;
|
||||
importExcelFile({ projectId: currentProject.value?.id }, formData)
|
||||
importExcelFile({ projectId: currentProject.value?.id, versions: queryForm.value.versions, sheet: queryForm.value.sheet, type: '1' }, formData)
|
||||
.then((res) => {
|
||||
const { code } = res;
|
||||
if (code == 200) {
|
||||
@ -259,7 +262,9 @@ const handleExport = () => {
|
||||
'/tender/billofquantitiesLimitList/export',
|
||||
{
|
||||
projectId: currentProject.value?.id,
|
||||
sheet: queryForm.value.sheet
|
||||
sheet: queryForm.value.sheet,
|
||||
versions: queryForm.value.versions,
|
||||
type: '1'
|
||||
},
|
||||
`限价一览表${queryForm.value.sheet}.xlsx`
|
||||
);
|
||||
@ -268,8 +273,8 @@ onUnmounted(() => {
|
||||
listeningProject();
|
||||
});
|
||||
onMounted(() => {
|
||||
// getVersionNums();
|
||||
getSheetName();
|
||||
getVersionNums();
|
||||
// getSheetName();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
121
src/views/largeScreen/components/ProgressComponent.vue
Normal file
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div class="progress_component">
|
||||
<div class="title">
|
||||
<span class="progress_title">{{ title }}</span>
|
||||
<span :class="percentageClass" class="roboto">{{ percentageChange }}</span>
|
||||
</div>
|
||||
<div class="roboto" v-if="isShowPrice">
|
||||
<span>{{ value }}</span>
|
||||
<span>{{ unit }}</span>
|
||||
</div>
|
||||
<div class="my_el_progress">
|
||||
<el-progress :percentage="progressPercentage" :color="progressColor" :show-text="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue';
|
||||
|
||||
// 定义组件属性
|
||||
const props = defineProps({
|
||||
// 标题文本
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '指标名称'
|
||||
},
|
||||
// 数值
|
||||
value: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '0.00'
|
||||
},
|
||||
// 单位
|
||||
unit: {
|
||||
type: String,
|
||||
default: '万元'
|
||||
},
|
||||
// 百分比变化值(如:-327.55%)
|
||||
percentageChange: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '0.00%'
|
||||
},
|
||||
// 进度条百分比
|
||||
progressPercentage: {
|
||||
type: Number,
|
||||
required: true,
|
||||
default: 0
|
||||
},
|
||||
// 进度条颜色,默认红色
|
||||
progressColor: {
|
||||
type: String,
|
||||
default: 'rgba(255, 77, 79, 1)'
|
||||
},
|
||||
// 是否显示价格
|
||||
isShowPrice: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
// 计算百分比变化的样式类(红色或绿色)
|
||||
const percentageClass = computed(() => {
|
||||
// 检查变化值是否为正数
|
||||
const isPositive = props.percentageChange.startsWith('+') ||
|
||||
(!props.percentageChange.startsWith('-') && props.percentageChange !== '0.00%');
|
||||
return isPositive ? 'green' : 'red';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.progress_component {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-bottom: 10px;
|
||||
|
||||
:deep(.el-progress-bar__outer) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:deep(.el-progress-bar__inner),
|
||||
:deep(.el-progress-bar__outer) {
|
||||
border-radius: unset;
|
||||
}
|
||||
|
||||
.my_el_progress {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.progress_title {
|
||||
color: rgba(143, 171, 191, 1);
|
||||
font-size: 12px;
|
||||
font-family: SourceHanSansCN-Regular;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.roboto {
|
||||
font-family: Roboto-Regular;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: rgba(255, 77, 79, 1);
|
||||
}
|
||||
|
||||
.green {
|
||||
color: rgba(0, 227, 150, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
167
src/views/largeScreen/components/RevenueContractCard.vue
Normal file
@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div class="stat-card" :style="customStyles">
|
||||
<!-- 标题区域 -->
|
||||
<div class="stat-card__title">{{ title }}</div>
|
||||
|
||||
<!-- 数值区域 -->
|
||||
<div class="stat-card__value-container">
|
||||
<span class="stat-card__value">{{ formattedValue }}</span>
|
||||
<span class="stat-card__unit">{{ unit }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 底部信息区域 -->
|
||||
<div class="stat-card__footer">
|
||||
<div class="stat-card__trend">
|
||||
<img
|
||||
class="stat-card__trend-icon"
|
||||
:src="'/src/assets/large/' + trendIcon+'.png'"
|
||||
:alt="trendDirection === 'up' ? '上升' : '下降'"
|
||||
>
|
||||
<span class="stat-card__trend-text">{{ trendText }}</span>
|
||||
</div>
|
||||
<img
|
||||
class="stat-card__badge"
|
||||
:src="'/src/assets/large/'+badgeIcon+'.png'"
|
||||
alt="徽章图标"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
// 定义组件属性
|
||||
const props = defineProps({
|
||||
// 卡片标题
|
||||
title: {
|
||||
type: String,
|
||||
default: '收入合同'
|
||||
},
|
||||
// 数值
|
||||
value: {
|
||||
type: Number,
|
||||
default: 205805.17
|
||||
},
|
||||
// 单位
|
||||
unit: {
|
||||
type: String,
|
||||
default: '万元'
|
||||
},
|
||||
// 增长率
|
||||
growthRate: {
|
||||
type: Number,
|
||||
default: 3.2
|
||||
},
|
||||
// 增长对比周期
|
||||
period: {
|
||||
type: String,
|
||||
default: '较上月'
|
||||
},
|
||||
// 趋势方向 (up/down)
|
||||
trendDirection: {
|
||||
type: String,
|
||||
default: 'up',
|
||||
validator: (value) => ['up', 'down'].includes(value)
|
||||
},
|
||||
// 趋势图标
|
||||
trendIcon: {
|
||||
type: String,
|
||||
default: 'up'
|
||||
},
|
||||
// 徽章图标
|
||||
badgeIcon: {
|
||||
type: String,
|
||||
default: 'top1'
|
||||
},
|
||||
// 卡片自定义样式
|
||||
customStyles: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
// 格式化数值为带千分位的字符串
|
||||
const formattedValue = computed(() => {
|
||||
return props.value.toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
});
|
||||
|
||||
// 生成趋势文本
|
||||
const trendText = computed(() => {
|
||||
return `${props.growthRate}% ${props.period}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stat-card {
|
||||
width: 225px;
|
||||
height: 147px;
|
||||
background-color: rgba(29, 214, 255, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(29, 214, 255, 0.1);
|
||||
border-radius: 4px; // 增加轻微圆角,提升视觉效果
|
||||
|
||||
&__title {
|
||||
font-size: 14px;
|
||||
color: #8FABBF;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&__value-container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
&__value {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
line-height: 30px;
|
||||
margin-right: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__unit {
|
||||
color: #8FABBF;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__trend-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&__trend-text {
|
||||
font-size: 14px;
|
||||
color: #8FABBF;
|
||||
}
|
||||
|
||||
&__badge {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
// 为下降趋势添加不同颜色
|
||||
:deep(.stat-card__trend-text) {
|
||||
color: v-bind('trendDirection === "up" ? "#8FABBF" : "#ff4d4f"');
|
||||
}
|
||||
</style>
|
56
src/views/largeScreen/components/TitleComponent.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="large_title">
|
||||
<div class="title">
|
||||
<img class="title_icon" src="@/assets/large/title_icon.png" alt=""></img>
|
||||
<div class="title_text">{{ title }}</div>
|
||||
</div>
|
||||
<img class="title_bottom" src="@/assets/large/title_bottom.png" alt="">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
// 定义组件属性,使组件可配置
|
||||
const props = defineProps({
|
||||
// 标题文本
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: '标题'
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.large_title {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.title_icon {
|
||||
width: 10px;
|
||||
height: 24px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.title_text {
|
||||
font-size: 24px;
|
||||
font-family: Rang_men_zheng_title;
|
||||
font-weight: 400;
|
||||
color: rgba(226, 235, 241, 1);
|
||||
}
|
||||
|
||||
.title_bottom {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
45
src/views/largeScreen/components/bottomboxconpoent.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="bottom_box">
|
||||
<div class="bottom_box_title">收入合同</div>
|
||||
<div>
|
||||
<span class="bottom_box_number">205,805.17</span>
|
||||
<span>万元</span>
|
||||
</div>
|
||||
<div class="bottom_box_bottom">
|
||||
<el-progress :percentage="50" color="rgba(255, 147, 42, 1)" />
|
||||
</div>
|
||||
<div class="bottom_box_text">
|
||||
成本率
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.bottom_box {
|
||||
width: 225px;
|
||||
height: 147px;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
|
||||
.bottom_box_title,
|
||||
.bottom_box_text {
|
||||
color: rgba(143, 171, 191, 1);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.bottom_box_number {
|
||||
font-size: 24px;
|
||||
color: #fff;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,8 +1,55 @@
|
||||
<template>
|
||||
<div class="centerPage">
|
||||
<div>
|
||||
<div style="height: 147px;width: 100%;display: flex;justify-content: space-between;">
|
||||
<!-- <div class="top_box">
|
||||
<div class="top_box_title">收入合同</div>
|
||||
<div>
|
||||
<span class="top_box_number">205,805.17</span>
|
||||
<span>万元</span>
|
||||
</div>
|
||||
<div class="top_box_bottom">
|
||||
<div>
|
||||
<img class="up_img" src="@/assets/large/up.png" alt=""></img>
|
||||
<span class="top_box_title"> 3.2% 较上月</span>
|
||||
</div>
|
||||
<img class="top_img" src="@/assets/large/top1.png" alt=""></img>
|
||||
</div>
|
||||
</div> -->
|
||||
<RevenueContractCard title="收入合同" :value="156234.89" :growthRate="-1.5" trendDirection="up" trendIcon="up"
|
||||
badgeIcon="top1" period="较上月" />
|
||||
<RevenueContractCard title="支出合同" :value="156234.89" :growthRate="-1.5" trendDirection="up" trendIcon="up"
|
||||
badgeIcon="top2" period="较上月" />
|
||||
<RevenueContractCard title="合同利润" :value="156234.89" :growthRate="-1.5" trendDirection="up" trendIcon="down"
|
||||
badgeIcon="top3" period="较上月" />
|
||||
<RevenueContractCard title="工程变更" :value="156234.89" :growthRate="-1.5" trendDirection="up" trendIcon="up"
|
||||
badgeIcon="top4" period="较上月" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="centerPage_map">
|
||||
<div ref="mapRef" class="map-container" style="width: 100%; height: 100%" />
|
||||
</div>
|
||||
<div>
|
||||
<div style="height: 147px;width: 100%;display: flex;justify-content: space-between;">
|
||||
<!-- <div class="bottom_box">
|
||||
<div class="bottom_box_title">收入合同</div>
|
||||
<div>
|
||||
<span class="bottom_box_number">205,805.17</span>
|
||||
<span>万元</span>
|
||||
</div>
|
||||
<div class="bottom_box_bottom">
|
||||
<el-progress :percentage="50" color="rgba(255, 147, 42, 1)" />
|
||||
</div>
|
||||
<div class="bottom_box_text">
|
||||
成本率
|
||||
</div>
|
||||
</div> -->
|
||||
<bottomboxconpoent> </bottomboxconpoent>
|
||||
<bottomboxconpoent> </bottomboxconpoent>
|
||||
<bottomboxconpoent> </bottomboxconpoent>
|
||||
<bottomboxconpoent> </bottomboxconpoent>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -10,6 +57,8 @@
|
||||
// import { getPowerStationOverview } from '@/api/large';
|
||||
import * as echarts from 'echarts';
|
||||
import china from '@/assets/china.json';
|
||||
import RevenueContractCard from './RevenueContractCard.vue';
|
||||
import bottomboxconpoent from './bottomboxconpoent.vue';
|
||||
const data = ref<any>({});
|
||||
|
||||
// 地图容器引用
|
||||
@ -168,9 +217,12 @@ onUnmounted(() => {
|
||||
padding: 0 10px 10px 10px;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
.centerPage_map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: 60%;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</style>
|
||||
|
@ -1,13 +1,73 @@
|
||||
<template>
|
||||
<div class="leftPage">左边</div>
|
||||
<div class="leftPage">
|
||||
<!-- -->
|
||||
<div class="kpi_box">
|
||||
<TitleComponent :title="'支付KPI'" style="margin-bottom: 20px;"/>
|
||||
<ProgressComponent
|
||||
title="应收账款"
|
||||
value="123,456.78"
|
||||
percentageChange="+25.30%"
|
||||
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)"
|
||||
/>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, toRefs, getCurrentInstance, nextTick } from 'vue';
|
||||
// import echarts from 'echarts';
|
||||
import TitleComponent from './TitleComponent.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>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style lang="scss">
|
||||
.leftPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #0c1e35;
|
||||
.kpi_box{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.contract_box{
|
||||
height: 35vh;
|
||||
}
|
||||
.kpi_box,.contract_box {
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(29, 214, 255, 0.3);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import * as echarts from 'echarts/core';
|
||||
import { text } from 'stream/consumers';
|
||||
// import { PictorialBarChart } from 'echarts/charts'
|
||||
// 客流量图
|
||||
export const getOption = (xData: any, yData: any) => {
|
||||
@ -249,9 +250,10 @@ export const getOption2 = (data: any) => {
|
||||
};
|
||||
return option;
|
||||
};
|
||||
//食堂周报图
|
||||
//z折线
|
||||
export const getLineOption = (lineData: any) => {
|
||||
const maxData = Math.ceil(Math.max(...lineData.line1));
|
||||
const maxData = Math.max(...lineData.line1.flat());
|
||||
|
||||
const option = {
|
||||
backgroundColor: '',
|
||||
tooltip: {
|
||||
@ -263,42 +265,49 @@ export const getLineOption = (lineData: any) => {
|
||||
},
|
||||
borderColor: '#7ec7ff'
|
||||
},
|
||||
// legend: {
|
||||
// align: 'left',
|
||||
// right: '5%',
|
||||
// top: '1%',
|
||||
// type: 'plain',
|
||||
// textStyle: {
|
||||
// color: '#fff',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// // icon:'rect',
|
||||
// itemGap: 15,
|
||||
// itemWidth: 18,
|
||||
// data: [
|
||||
// {
|
||||
// name: '上周销售量'
|
||||
// },
|
||||
// {
|
||||
// name: '本周销售量'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
legend: {
|
||||
align: 'left',
|
||||
right: '5%',
|
||||
top: '1%',
|
||||
type: 'plain',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 12
|
||||
},
|
||||
// icon:'rect',
|
||||
itemGap: 15,
|
||||
itemWidth: 18,
|
||||
data: [
|
||||
{
|
||||
name: '收款金额'
|
||||
},
|
||||
{
|
||||
name: '付款金额'
|
||||
},
|
||||
{
|
||||
name: '净现金流'
|
||||
}
|
||||
]
|
||||
},
|
||||
grid: {
|
||||
top: '12%',
|
||||
left: '1%',
|
||||
right: '3%',
|
||||
bottom: '12%',
|
||||
bottom: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: lineData.xLabel,
|
||||
boundaryGap: false,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: true
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
@ -311,28 +320,28 @@ export const getLineOption = (lineData: any) => {
|
||||
type: 'value',
|
||||
max: maxData,
|
||||
splitLine: {
|
||||
show: true,
|
||||
show: false,
|
||||
lineStyle: {
|
||||
type: 'solid',
|
||||
color: 'rgba(73, 169, 191, 0.2)'
|
||||
}
|
||||
}
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
// show: true,
|
||||
start: 0,
|
||||
end: 30,
|
||||
bottom: 2, // 下滑块距离x轴底部的距离
|
||||
height: 23
|
||||
},
|
||||
{
|
||||
type: 'inside'
|
||||
}
|
||||
],
|
||||
// dataZoom: [
|
||||
// {
|
||||
// // show: true,
|
||||
// start: 0,
|
||||
// end: 30,
|
||||
// bottom: 2, // 下滑块距离x轴底部的距离
|
||||
// height: 23
|
||||
// },
|
||||
// {
|
||||
// type: 'inside'
|
||||
// }
|
||||
// ],
|
||||
series: [
|
||||
{
|
||||
name: '逆变器功率',
|
||||
name: '收款金额',
|
||||
type: 'line',
|
||||
symbol: 'circle', // 默认是空心圆(中间是白色的),改成实心圆
|
||||
showAllSymbol: false,
|
||||
@ -373,7 +382,95 @@ export const getLineOption = (lineData: any) => {
|
||||
shadowColor: 'rgba(25,163,223, 0.5)', //阴影颜色
|
||||
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]
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -481,10 +578,10 @@ export const getDishesOption = (data?: any) => {
|
||||
// 菜品库存图
|
||||
export const getInventoryOption = () => {
|
||||
const res = {
|
||||
data: [2800, 300, 3900, 3000, 2450, 2670, 3320],
|
||||
name: ['麻辣牛肉', '水煮肉片', '酸菜鱼', '辣子鸡丁', '烧白', '冬瓜排骨汤', '清炒油麦菜'],
|
||||
ratio: [4000, 4000, 4000, 4000, 4000, 4000, 4000]
|
||||
},
|
||||
data: [2800, 300, 3900, 3000, 2450, 2670, 3320],
|
||||
name: ['麻辣牛肉', '水煮肉片', '酸菜鱼', '辣子鸡丁', '烧白', '冬瓜排骨汤', '清炒油麦菜'],
|
||||
ratio: [4000, 4000, 4000, 4000, 4000, 4000, 4000]
|
||||
},
|
||||
dataIndex = 1;
|
||||
const option = {
|
||||
xAxis: {
|
||||
@ -600,16 +697,16 @@ export const getBarOptions = (data: any) => {
|
||||
const option = {
|
||||
backgroundColor: '',
|
||||
grid: {
|
||||
left: '7%',
|
||||
left: '8%',
|
||||
top: '4%',
|
||||
bottom: '25%',
|
||||
bottom: '8%',
|
||||
right: '2%'
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
backgroundColor: '',
|
||||
trigger: 'axis',
|
||||
formatter: '{b0}:{c0}元',
|
||||
formatter: '{b0}:{c0}万元',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
}
|
||||
@ -634,7 +731,7 @@ export const getBarOptions = (data: any) => {
|
||||
// show: true,
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: 'rgba(108, 128, 151, 0.3)',
|
||||
type: 'dashed'
|
||||
@ -646,9 +743,7 @@ export const getBarOptions = (data: any) => {
|
||||
{
|
||||
axisLabel: {
|
||||
formatter: function (value) {
|
||||
if (value >= 1000) {
|
||||
value = (value / 1000).toFixed(1) + 'k'; // 大于等于1000的数字显示为1k、2.5k等
|
||||
}
|
||||
value = value + '万';
|
||||
return value;
|
||||
},
|
||||
color: 'rgba(255, 255, 255, 0.8)'
|
||||
@ -662,7 +757,7 @@ export const getBarOptions = (data: any) => {
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: 'rgba(108, 128, 151, 0.3)',
|
||||
type: 'dashed'
|
||||
@ -670,24 +765,25 @@ export const getBarOptions = (data: any) => {
|
||||
}
|
||||
}
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
// show: true,
|
||||
start: 0,
|
||||
end: 30,
|
||||
bottom: 2, // 下滑块距离x轴底部的距离
|
||||
height: 23
|
||||
},
|
||||
{
|
||||
type: 'inside'
|
||||
}
|
||||
],
|
||||
// dataZoom: [
|
||||
// {
|
||||
// // show: true,
|
||||
// start: 0,
|
||||
// end: 30,
|
||||
// bottom: 2, // 下滑块距离x轴底部的距离
|
||||
// height: 23
|
||||
// },
|
||||
// {
|
||||
// type: 'inside'
|
||||
// }
|
||||
// ],
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: data.value,
|
||||
stack: '合并',
|
||||
barWidth: '15',
|
||||
data: data.value[0],
|
||||
// stack: '合并',
|
||||
barWidth: '5',
|
||||
barGap: '100%',
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
@ -697,22 +793,60 @@ export const getBarOptions = (data: any) => {
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 111, 255, 0)' // 0% 处的颜色
|
||||
color: ' rgba(29, 214, 255, 1)' // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 0.7,
|
||||
color: 'rgba(0, 111, 255, 0.5)' // 0% 处的颜色
|
||||
color: ' rgba(29, 214, 255, 0.5)' // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(0, 111, 255, 1)' // 100% 处的颜色
|
||||
color: ' rgba(29, 214, 255, 0.1)' // 100% 处的颜色
|
||||
}
|
||||
],
|
||||
false
|
||||
)
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
show: false,
|
||||
formatter: '{c}',
|
||||
position: 'top',
|
||||
color: '#fff',
|
||||
fontSize: 10
|
||||
// padding: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
data: data.value[1],
|
||||
// stack: '合并',
|
||||
barWidth: '5',
|
||||
barGap: '100%',
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: ' rgba(255, 77, 79, 1)' // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 0.7,
|
||||
color: ' rgba(255, 77, 79, 0.5)' // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: ' rgba(255, 77, 79, 0.1)' // 100% 处的颜色
|
||||
}
|
||||
],
|
||||
false
|
||||
)
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
formatter: '{c}',
|
||||
position: 'top',
|
||||
color: '#fff',
|
||||
@ -720,16 +854,66 @@ export const getBarOptions = (data: any) => {
|
||||
// padding: 5
|
||||
}
|
||||
}
|
||||
// {
|
||||
// type: 'bar',
|
||||
// stack: '合并',
|
||||
// data: topData,
|
||||
// barWidth: '15',
|
||||
// itemStyle: {
|
||||
// color: 'rgba(252, 217, 18, 1)'
|
||||
// }
|
||||
// }
|
||||
]
|
||||
};
|
||||
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;
|
||||
}
|
@ -1,13 +1,183 @@
|
||||
<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 class="inflowData">
|
||||
<div class="inflow">
|
||||
<div class="title">现金流入</div>
|
||||
<div class="number">1000000</div>
|
||||
<div class="unit">万元</div>
|
||||
</div>
|
||||
<div class="inflow">
|
||||
<div class="title">现金流出</div>
|
||||
<div class="number">1000000</div>
|
||||
<div class="unit">万元</div>
|
||||
</div>
|
||||
<div class="inflow">
|
||||
<div class="title">净现金流</div>
|
||||
<div class="number">1000000</div>
|
||||
<div class="unit">万元</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inflow_echarts">
|
||||
<EchartBox :option="barOption" />
|
||||
</div>
|
||||
<div class="progress">
|
||||
<!-- <div class="progress_item">
|
||||
<div class="title">项目进度</div>
|
||||
<div class="number">100%</div>
|
||||
</div> -->
|
||||
<ProgressComponent title="现金比率" value="123,456.78" percentageChange="3479.61%" :progressPercentage="100"
|
||||
progressColor="rgba(29, 214, 255, 1)" :isShowPrice="false" class="progress_text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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';
|
||||
import ProgressComponent from './ProgressComponent.vue';
|
||||
|
||||
const lineOption = ref();
|
||||
const barOption = 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);
|
||||
};
|
||||
const getTurnoverList = (data?: any) => {
|
||||
// const xData = data.map((item) => item.time);
|
||||
// const yData = data.map((item) => {
|
||||
// // 先将content转换为数字,再调用toFixed
|
||||
// const num = Number(item.content);
|
||||
// return isNaN(num) ? 0 : Number(num.toFixed(2));
|
||||
// });
|
||||
|
||||
const barData = {
|
||||
name: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
|
||||
value: [
|
||||
[2, 5, 15, 30, 25, 35, 40, 35, 45, 50, 40, 55],
|
||||
[4, 3, 6, 11, 15, 22, 30, 14, 48, 22, 25, 60]
|
||||
]
|
||||
};
|
||||
barOption.value = getBarOptions(barData);
|
||||
};
|
||||
onMounted(() => {
|
||||
getCapitalData();
|
||||
getTurnoverList();
|
||||
});
|
||||
|
||||
//资金KPI
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.rightPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
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: 25vh;
|
||||
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: 10px;
|
||||
}
|
||||
|
||||
.inflowData {
|
||||
width: 100%;
|
||||
height: 12vh;
|
||||
// background: #fff;
|
||||
padding-top: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 10px;
|
||||
|
||||
.inflow {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// background: #f5f5f5;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
background: rgba(29, 214, 255, 0.1);
|
||||
border-left: 1px solid rgba(29, 214, 255, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
// font-weight: 500;
|
||||
color: #fff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.number {
|
||||
font-size: 24px;
|
||||
// font-weight: 500;
|
||||
color: #fff;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 12px;
|
||||
// font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inflow_echarts {
|
||||
width: 100%;
|
||||
height: 25vh;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
:deep(.progress_text) {
|
||||
.roboto {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
279
src/views/tender/bidd/index.vue
Normal 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>
|
@ -15,13 +15,6 @@
|
||||
<el-form-item label="企业名称" prop="supplierName">
|
||||
<el-input v-model="queryParams.supplierName" placeholder="请输入企业名称" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审核状态" prop="state">
|
||||
<el-select v-model="queryParams.state" placeholder="请选择状态" clearable>
|
||||
<el-option label="待审核" value="0"></el-option>
|
||||
<el-option label="已通过" value="1"></el-option>
|
||||
<el-option label="未通过" value="2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
@ -38,32 +31,16 @@
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['supplierInput:supplierInput:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['supplierInput:supplierInput:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['supplierInput:supplierInput:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<!-- <el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['supplierInput:supplierInput:export']">导出</el-button>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="loading" :data="supplierInputList" @selection-change="handleSelectionChange" border>
|
||||
<el-table-column type="selection" width="100%" align="center" />
|
||||
<el-table-column type="index" label="序号" align="center" width="60" />
|
||||
<el-table-column label="企业登记注册类型" align="center" prop="supplierType" width="140" />
|
||||
<el-table-column label="企业名称" align="center" prop="supplierName" width="180" />
|
||||
<el-table-column label="法定代表人" align="center" prop="supplierPerson" width="120" />
|
||||
@ -72,11 +49,9 @@
|
||||
<el-table-column label="负责人电话" align="center" prop="personPhone" width="120" />
|
||||
<el-table-column label="纳税规模" align="center" prop="taxScale" width="120" />
|
||||
<el-table-column label="资质等级" align="center" prop="supplierLivel" width="120" />
|
||||
<el-table-column label="审核状态" align="center" prop="state" width="120">
|
||||
<el-table-column label="流程状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.state === '1' ? 'success' : scope.row.state === '2' ? 'danger' : 'warning'">
|
||||
{{ scope.row.state === '1' ? '已通过' : scope.row.state === '2' ? '未通过' : '待审核' }}
|
||||
</el-tag>
|
||||
<dict-tag :options="wf_business_status" :value="scope.row.state" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入库资料" align="center" prop="inputFile" width="120">
|
||||
@ -86,34 +61,30 @@
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120">
|
||||
<el-table-column label="操作" align="center" fixed="right" width="240">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['supplierInput:supplierInput:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['supplierInput:supplierInput:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['supplierInput:supplierInput:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
<el-button link type="primary" icon="edit" @click="handleAudit(scope.row)" v-if="scope.row.state == 'draft' || scope.row.state == 'back'"
|
||||
>审核</el-button
|
||||
>
|
||||
<el-button link type="primary" icon="View" v-if="scope.row.state != '2' && scope.row.state != 'draft'" @click="handleAuditView(scope.row)"
|
||||
>查看流程</el-button
|
||||
>
|
||||
<!-- <el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['supplierInput:supplierInput: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-card>
|
||||
|
||||
<!-- 新增/修改对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="950px" append-to-body>
|
||||
<el-form ref="supplierInputFormRef" :model="form" :rules="rules" label-width="200px">
|
||||
@ -249,7 +220,6 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第十行:安全生产许可证有效期(仅劳务类型显示) -->
|
||||
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
@ -261,7 +231,6 @@
|
||||
<!-- 空列占位 -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第十一行:注册人员数量(仅劳务类型显示) -->
|
||||
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
@ -282,7 +251,6 @@
|
||||
<!-- 空列占位 -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第十二行:职称人员数量(仅劳务类型显示) -->
|
||||
<el-row :gutter="20" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
@ -303,7 +271,6 @@
|
||||
<!-- 空列占位 -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第十四行:入库资料上传 -->
|
||||
<el-row class="mb-4">
|
||||
<el-col :span="24">
|
||||
@ -342,7 +309,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 对话框底部按钮 -->
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@ -362,10 +328,8 @@ import { SupplierInputVO, SupplierInputQuery, SupplierInputForm, PageData, Dialo
|
||||
import Pagination from '@/components/Pagination/index.vue';
|
||||
import RightToolbar from '@/components/RightToolbar/index.vue';
|
||||
import FileUpload from '@/components/FileUpload/index.vue';
|
||||
|
||||
// 实例代理
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
|
||||
// 组件引用
|
||||
const fileUploadRef = ref<InstanceType<typeof FileUpload> | null>(null);
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
@ -414,7 +378,7 @@ const initFormData: any = {
|
||||
personnelNumber: undefined, // 后端返回的拼接字符串(如“5,6,7,8”)
|
||||
fileId: undefined,
|
||||
inputFile: undefined,
|
||||
state: '0', // 新增默认待审核
|
||||
// state: '0', // 新增默认待审核
|
||||
// 新增:用于表单输入的单独字段
|
||||
build1: undefined, // 一建建造师
|
||||
build2: undefined, // 二建建造师
|
||||
@ -577,6 +541,28 @@ const splitBackEndStrToForm = (resData: any) => {
|
||||
form.value.personnelNumber4 = personnelArr[3] || undefined; // 其他职称人员
|
||||
}
|
||||
};
|
||||
/** 审核过程按钮操作 */
|
||||
const handleAudit = async (row) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/approval/supplierInput/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'update'
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 查看按钮操作 */
|
||||
const handleAuditView = async (row) => {
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.push({
|
||||
path: `/approval/supplierInput/indexEdit`,
|
||||
query: {
|
||||
id: row.id,
|
||||
type: 'view'
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 文件选择变更 */
|
||||
const change = () => {
|
||||
fileUrl.value = '';
|
||||
|
521
src/views/tender/supplierInput/indexEdit.vue
Normal file
@ -0,0 +1,521 @@
|
||||
<template>
|
||||
<div class="p-4 bg-gray-50 min-h-screen">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<!-- 顶部按钮区域 -->
|
||||
<el-card class="mb-4 rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md">
|
||||
<approvalButton
|
||||
@submitForm="submitForm"
|
||||
@approvalVerifyOpen="approvalVerifyOpen"
|
||||
@handleApprovalRecord="handleApprovalRecord"
|
||||
:buttonLoading="buttonLoading"
|
||||
:id="form.id"
|
||||
:status="form.state"
|
||||
:pageType="routeParams.type"
|
||||
/>
|
||||
</el-card>
|
||||
<!-- 表单区域 -->
|
||||
<el-card class="rounded-lg shadow-sm bg-white border border-gray-100 transition-all hover:shadow-md overflow-hidden">
|
||||
<div class="p-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-100">
|
||||
<h3 class="text-lg font-semibold text-gray-800">专项方案信息</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<el-form ref="leaveFormRef" v-loading="loading" disabled :model="form" :rules="rules" label-width="150px" class="space-y-4">
|
||||
<div class="">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业登记注册类型" prop="supplierType">
|
||||
<el-select v-model="form.supplierType" placeholder="请选择供应商类型" @change="handleTypeChange">
|
||||
<el-option label="劳务" value="劳务"></el-option>
|
||||
<el-option label="技术服务" value="技术服务"></el-option>
|
||||
<el-option label="物资设备" value="物资设备"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业名称" prop="supplierName">
|
||||
<el-input v-model="form.supplierName" placeholder="请输入企业名称" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业法定代表人" prop="supplierPerson">
|
||||
<el-input v-model="form.supplierPerson" placeholder="请输入法定代表人" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="统一社会信用代码" prop="supplierCode">
|
||||
<el-input v-model="form.supplierCode" placeholder="请输入统一社会信用代码" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业注册地址" prop="supplierAddres">
|
||||
<el-input v-model="form.supplierAddres" placeholder="请输入注册地址" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人姓名" prop="personName">
|
||||
<el-input v-model="form.personName" placeholder="请输入负责人姓名" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="负责人联系电话" prop="personPhone">
|
||||
<el-input v-model="form.personPhone" placeholder="请输入联系电话" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="纳税规模" prop="taxScale">
|
||||
<el-select v-model="form.taxScale" placeholder="请选择纳税规模">
|
||||
<el-option label="一般纳税人" value="一般纳税人"></el-option>
|
||||
<el-option label="小规模纳税人" value="小规模纳税人"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行户名" prop="bankPersonName">
|
||||
<el-input v-model="form.bankPersonName" placeholder="请输入开户行户名" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户银行" prop="bankName">
|
||||
<el-input v-model="form.bankName" placeholder="请输入开户银行" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="开户行账号" prop="bankAccount">
|
||||
<el-input v-model="form.bankAccount" placeholder="请输入开户行账号" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="经营范围" prop="scope">
|
||||
<el-input v-model="form.scope" placeholder="请输入经营范围" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="企业资质等级" prop="supplierLivel">
|
||||
<el-input v-model="form.supplierLivel" placeholder="请输入资质等级" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发证日期" prop="issueDate">
|
||||
<el-date-picker v-model="form.issueDate" type="date" placeholder="请选择发证日期" value-format="YYYY-MM-DD" />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="证书有效期" prop="certificateValidity">
|
||||
<el-date-picker v-model="form.certificateValidity" type="date" placeholder="请选择证书有效期" value-format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="近三年营业额" prop="pastThreeYears">
|
||||
<el-input v-model="form.pastThreeYears" placeholder="请输入近三年营业额" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="生产许可证编号" prop="safeCode">
|
||||
<el-input v-model="form.safeCode" placeholder="请输入许可证编号" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="生产许可证发证日期" prop="safeCodeData">
|
||||
<el-date-picker v-model="form.safeCodeData" type="date" placeholder="请选择发证日期" />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="生产许可证发证日期" prop="safeCertificateValidity">
|
||||
<el-date-picker v-model="form.safeCertificateValidity" type="date" placeholder="请选择发证日期" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="一建建造师" prop="build1">
|
||||
<el-input v-model="form.build1" placeholder="请输入一建建造师数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="二建建造师" prop="build2">
|
||||
<el-input v-model="form.build2" placeholder="请输入二建建造师数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="其他(分别写)" prop="build4">
|
||||
<el-input v-model="form.build3" placeholder="请输入其他人员数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="注册造价工程师" prop="build3">
|
||||
<el-input v-model="form.build4" placeholder="请输入注册造价工程师数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" class="mb-4" v-if="form.supplierType === '劳务'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="高级工程师人数" prop="personnelNumber1">
|
||||
<el-input v-model="form.personnelNumber1" placeholder="请输高级工程师数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="工程师数量" prop="personnelNumber2">
|
||||
<el-input v-model="form.personnelNumber2" placeholder="请输入工程师数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="助理工程师数量" prop="personnelNumber3">
|
||||
<el-input v-model="form.personnelNumber3" placeholder="请输入助理工程师数量" clearable />
|
||||
</el-form-item> </el-col
|
||||
><el-col :span="12">
|
||||
<el-form-item label="其他人员数量" prop="personnelNumber4">
|
||||
<el-input v-model="form.personnelNumber4" placeholder="请输入其他人员数量" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- 提交组件 -->
|
||||
<approvalRecord ref="approvalRecordRef"></approvalRecord>
|
||||
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
||||
<!-- 流程选择对话框 -->
|
||||
<el-dialog
|
||||
draggable
|
||||
v-model="dialogVisible.visible"
|
||||
:title="dialogVisible.title"
|
||||
:before-close="handleClose"
|
||||
width="500"
|
||||
class="rounded-lg shadow-lg"
|
||||
>
|
||||
<div class="p-4">
|
||||
<p class="text-gray-600 mb-4">请选择要启动的流程:</p>
|
||||
<el-select v-model="flowCode" placeholder="请选择流程" style="width: 100%">
|
||||
<el-option v-for="item in flowCodeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer p-4 border-t border-gray-100 flex justify-end space-x-3">
|
||||
<el-button @click="handleClose" class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>取消</el-button
|
||||
>
|
||||
<el-button type="primary" @click="submitFlow()" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors"
|
||||
>确认</el-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Leave" lang="ts">
|
||||
import { LeaveForm, LeaveQuery } from '@/api/workflow/leave/types';
|
||||
import { startWorkFlow } from '@/api/workflow/task';
|
||||
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
||||
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
||||
import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
import { useUserStoreHook } from '@/store/modules/user';
|
||||
import { getSupplierInput } from '@/api/supplierInput/supplierInput';
|
||||
|
||||
// 获取用户 store
|
||||
const userStore = useUserStoreHook();
|
||||
// 从 store 中获取项目列表和当前选中的项目
|
||||
const currentProject = computed(() => userStore.selectedProject);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
//路由参数
|
||||
const routeParams = ref<Record<string, any>>({});
|
||||
const flowCode = ref<string>('');
|
||||
const status = ref<string>('');
|
||||
const dialogVisible = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: '流程定义'
|
||||
});
|
||||
//提交组件
|
||||
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
||||
//审批记录组件
|
||||
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
||||
const leaveFormRef = ref<ElFormInstance>();
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
isEdit: false
|
||||
});
|
||||
const submitFormData = ref<StartProcessBo>({
|
||||
businessId: '',
|
||||
flowCode: '',
|
||||
variables: {}
|
||||
});
|
||||
const taskVariables = ref<Record<string, any>>({});
|
||||
const flowCodeOptions = [
|
||||
{
|
||||
value: currentProject.value?.id + '_supplierInput',
|
||||
label: '供应商入库审核'
|
||||
}
|
||||
];
|
||||
const initFormData = {
|
||||
id: undefined,
|
||||
supplierType: undefined,
|
||||
supplierName: undefined,
|
||||
supplierPerson: undefined,
|
||||
supplierCode: undefined,
|
||||
supplierAddres: undefined,
|
||||
personName: undefined,
|
||||
personPhone: undefined,
|
||||
bankPersonName: undefined,
|
||||
bankName: undefined,
|
||||
bankAccount: undefined,
|
||||
taxScale: undefined,
|
||||
scope: undefined,
|
||||
supplierLivel: undefined,
|
||||
issueDate: undefined,
|
||||
certificateValidity: undefined,
|
||||
pastThreeYears: undefined,
|
||||
safeCode: undefined,
|
||||
safeCodeData: undefined,
|
||||
safeCertificateValidity: undefined,
|
||||
registeredNumber: undefined, // 后端返回的拼接字符串(如“1,2,3,4”)
|
||||
personnelNumber: undefined, // 后端返回的拼接字符串(如“5,6,7,8”)
|
||||
fileId: undefined,
|
||||
inputFile: undefined,
|
||||
state: '0', // 新增默认待审核
|
||||
// 新增:用于表单输入的单独字段
|
||||
build1: undefined, // 一建建造师
|
||||
build2: undefined, // 二建建造师
|
||||
build3: undefined, // 注册造价工程师
|
||||
build4: undefined, // 其他注册人员
|
||||
personnelNumber1: undefined, // 高级工程师
|
||||
personnelNumber2: undefined, // 工程师
|
||||
personnelNumber3: undefined, // 助理工程师
|
||||
personnelNumber4: undefined // 其他职称人员
|
||||
};
|
||||
const data = reactive<PageData<LeaveForm, LeaveQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: currentProject.value?.id,
|
||||
fileName: undefined,
|
||||
fileType: undefined,
|
||||
fileSuffix: undefined,
|
||||
fileStatus: undefined,
|
||||
originalName: undefined,
|
||||
newest: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
versionNumber: [{ required: true, message: '版本号不能为空', trigger: 'blur' }],
|
||||
fileName: [{ required: true, message: '文件名称不能为空', trigger: 'blur' }],
|
||||
fileType: [{ required: true, message: '文件类型不能为空', trigger: 'change' }],
|
||||
fileUrl: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// 新增时必须上传文件
|
||||
if (!form.value.fileUrl) {
|
||||
callback(new Error('请上传图纸文件'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.visible = false;
|
||||
flowCode.value = '';
|
||||
buttonLoading.value = false;
|
||||
};
|
||||
const { form, rules } = toRefs(data);
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
leaveFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 获取详情 */
|
||||
const getInfo = () => {
|
||||
loading.value = true;
|
||||
buttonLoading.value = false;
|
||||
nextTick(async () => {
|
||||
const res = await getSupplierInput(routeParams.value.id);
|
||||
Object.assign(form.value, res.data);
|
||||
loading.value = false;
|
||||
buttonLoading.value = false;
|
||||
});
|
||||
};
|
||||
/** 提交按钮 */
|
||||
const submitForm = (status1: string) => {
|
||||
status.value = status1;
|
||||
buttonLoading.value = true;
|
||||
dialog.visible = false;
|
||||
submit(status.value, form.value);
|
||||
};
|
||||
const submitFlow = async () => {
|
||||
handleStartWorkFlow(form.value);
|
||||
dialogVisible.visible = false;
|
||||
};
|
||||
//提交申请
|
||||
const handleStartWorkFlow = async (data: LeaveForm) => {
|
||||
try {
|
||||
submitFormData.value.flowCode = flowCode.value;
|
||||
submitFormData.value.businessId = data.id;
|
||||
//流程变量
|
||||
taskVariables.value = {
|
||||
// leave4/5 使用的流程变量
|
||||
userList: ['1', '3', '4']
|
||||
};
|
||||
submitFormData.value.variables = taskVariables.value;
|
||||
const resp = await startWorkFlow(submitFormData.value);
|
||||
if (submitVerifyRef.value) {
|
||||
buttonLoading.value = false;
|
||||
submitVerifyRef.value.openDialog(resp.data.taskId);
|
||||
}
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
};
|
||||
//审批记录
|
||||
const handleApprovalRecord = () => {
|
||||
approvalRecordRef.value.init(form.value.id);
|
||||
};
|
||||
//提交回调
|
||||
const submitCallback = async () => {
|
||||
await proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
};
|
||||
//审批
|
||||
const approvalVerifyOpen = async () => {
|
||||
submitVerifyRef.value.openDialog(routeParams.value.taskId);
|
||||
};
|
||||
// 图纸上传成功之后 开始提交
|
||||
const submit = async (status, data) => {
|
||||
form.value = data;
|
||||
if (status === 'draft') {
|
||||
buttonLoading.value = false;
|
||||
proxy?.$modal.msgSuccess('暂存成功');
|
||||
proxy.$tab.closePage(proxy.$route);
|
||||
proxy.$router.go(-1);
|
||||
} else {
|
||||
if ((form.value.state === 'draft' && (flowCode.value === '' || flowCode.value === null)) || routeParams.value.type === 'add') {
|
||||
flowCode.value = flowCodeOptions[0].value;
|
||||
dialogVisible.visible = true;
|
||||
return;
|
||||
}
|
||||
//说明启动过先随意穿个参数
|
||||
if (flowCode.value === '' || flowCode.value === null) {
|
||||
flowCode.value = 'xx';
|
||||
}
|
||||
console.log(data);
|
||||
await handleStartWorkFlow(data);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(async () => {
|
||||
routeParams.value = proxy.$route.query;
|
||||
reset();
|
||||
loading.value = false;
|
||||
if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
|
||||
getInfo();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
/* 全局样式 */
|
||||
:root {
|
||||
--primary: #409eff;
|
||||
--primary-light: #66b1ff;
|
||||
--primary-dark: #3a8ee6;
|
||||
--success: #67c23a;
|
||||
--warning: #e6a23c;
|
||||
--danger: #f56c6c;
|
||||
--info: #909399;
|
||||
}
|
||||
|
||||
/* 表单样式优化 */
|
||||
.el-form-item {
|
||||
.el-form-item__label {
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.el-input__inner,
|
||||
.el-select .el-input__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: 4px;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
box-shadow 0.2s;
|
||||
|
||||
&:focus {
|
||||
border-color: var(--primary-light);
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮样式优化 */
|
||||
.el-button {
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.is-primary {
|
||||
background-color: var(--primary);
|
||||
border-color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-light);
|
||||
border-color: var(--primary-light);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--primary-dark);
|
||||
border-color: var(--primary-dark);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-text {
|
||||
color: var(--primary);
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-light);
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片样式优化 */
|
||||
.el-card {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
/* transform: translateY(-2px); */
|
||||
}
|
||||
}
|
||||
|
||||
/* 对话框样式优化 */
|
||||
.el-dialog {
|
||||
.el-dialog__header {
|
||||
background-color: #f5f7fa;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.el-dialog__title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 15px 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
</style>
|