明树Git Lab

Commit eef91c57 authored by chenron's avatar chenron

部门管理添加

parent 502ba659
@font-face { @font-face {
font-family: "iconfont"; /* Project id 5072634 */ font-family: "iconfont"; /* Project id 5072634 */
src: url('iconfont.woff2?t=1764314450961') format('woff2'), src: url('//at.alicdn.com/t/c/font_5072634_8c3g9zvwtne.woff2?t=1764660705764') format('woff2'),
url('iconfont.woff?t=1764314450961') format('woff'), url('//at.alicdn.com/t/c/font_5072634_8c3g9zvwtne.woff?t=1764660705764') format('woff'),
url('iconfont.ttf?t=1764314450961') format('truetype'); url('//at.alicdn.com/t/c/font_5072634_8c3g9zvwtne.ttf?t=1764660705764') format('truetype');
} }
.iconfont { .iconfont {
...@@ -13,6 +13,26 @@ ...@@ -13,6 +13,26 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-gengduo:before {
content: "\e73a";
}
.icon-shanchu:before {
content: "\e8c1";
}
.icon-bianji:before {
content: "\e607";
}
.icon-tianjia:before {
content: "\e6ea";
}
.icon-gedian:before {
content: "\e603";
}
.icon-gongzuotai:before { .icon-gongzuotai:before {
content: "\e605"; content: "\e605";
} }
...@@ -20,4 +40,3 @@ ...@@ -20,4 +40,3 @@
.icon-quanping:before { .icon-quanping:before {
content: "\e62f"; content: "\e62f";
} }
window._iconfont_svg_string_5072634='<svg><symbol id="icon-gongzuotai" viewBox="0 0 1024 1024"><path d="M837.12 112.64H161.28c-43.52 0-76.8 35.84-76.8 76.8v468.48c0 43.52 35.84 76.8 76.8 76.8h276.48v156.16H215.04c-15.36 0-25.6 12.8-25.6 25.6 0 15.36 12.8 25.6 25.6 25.6h570.88c15.36 0 25.6-12.8 25.6-25.6 0-15.36-12.8-25.6-25.6-25.6h-243.2v-156.16h294.4c43.52 0 76.8-35.84 76.8-76.8V189.44c2.56-43.52-33.28-76.8-76.8-76.8z m25.6 545.28c0 15.36-12.8 25.6-25.6 25.6H161.28c-15.36 0-25.6-12.8-25.6-25.6V189.44c0-15.36 12.8-25.6 25.6-25.6h675.84c15.36 0 25.6 12.8 25.6 25.6v468.48z m0 0" ></path></symbol><symbol id="icon-quanping" viewBox="0 0 1024 1024"><path d="M181 357.5V181.2h176.4c14.3 0 25.9-11.6 25.9-25.9v-31.1c0-14.3-11.6-25.9-25.9-25.9H118c-11 0-20 9-20 20v239.4c0 14.3 11.6 25.9 25.9 25.9H155c14.4-0.1 26-11.7 26-26.1zM668.6 181.2H845v176.4c0 14.3 11.6 25.9 25.9 25.9H902c14.3 0 25.9-11.6 25.9-25.9V118.2c0-11-9-20-20-20H668.6c-14.3 0-25.9 11.6-25.9 25.9v31.1c0 14.3 11.6 26 25.9 26zM357.4 845.2H181V668.8c0-14.3-11.6-25.9-25.9-25.9H124c-14.3 0-25.9 11.6-25.9 25.9v239.4c0 11 9 20 20 20h239.4c14.3 0 25.9-11.6 25.9-25.9v-31.1c-0.1-14.4-11.7-26-26-26zM845 668.8v176.4H668.6c-14.3 0-25.9 11.6-25.9 25.9v31.1c0 14.3 11.6 25.9 25.9 25.9H908c11 0 20-9 20-20V668.8c0-14.3-11.6-25.9-25.9-25.9H871c-14.4 0-26 11.6-26 25.9z" ></path></symbol></svg>',(n=>{var t=(e=(e=document.getElementsByTagName("script"))[e.length-1]).getAttribute("data-injectcss"),e=e.getAttribute("data-disable-injectsvg");if(!e){var c,o,i,d,s,a=function(t,e){e.parentNode.insertBefore(t,e)};if(t&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}c=function(){var t,e=document.createElement("div");e.innerHTML=n._iconfont_svg_string_5072634,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?a(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),c()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=c,d=n.document,s=!1,r(),d.onreadystatechange=function(){"complete"==d.readyState&&(d.onreadystatechange=null,l())})}function l(){s||(s=!0,i())}function r(){try{d.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}l()}})(window); window._iconfont_svg_string_5072634='<svg><symbol id="icon-gengduo" viewBox="0 0 1024 1024"><path d="M223.962372 607.897867c-52.980346 0-95.983874-43.003528-95.983874-95.983874s43.003528-95.983874 95.983874-95.983874 95.983874 43.003528 95.983874 95.983874S276.942718 607.897867 223.962372 607.897867z" fill="#575B66" ></path><path d="M511.913993 607.897867c-52.980346 0-95.983874-43.003528-95.983874-95.983874s43.003528-95.983874 95.983874-95.983874 95.983874 43.003528 95.983874 95.983874S564.894339 607.897867 511.913993 607.897867z" fill="#575B66" ></path><path d="M800.037628 607.897867c-52.980346 0-95.983874-43.003528-95.983874-95.983874s43.003528-95.983874 95.983874-95.983874 95.983874 43.003528 95.983874 95.983874S852.84596 607.897867 800.037628 607.897867z" fill="#575B66" ></path></symbol><symbol id="icon-shanchu" viewBox="0 0 1024 1024"><path d="M106.666667 213.333333h810.666666v42.666667H106.666667z" fill="#3D3D3D" ></path><path d="M640 128v42.666667h42.666667V128c0-23.573333-19.093333-42.666667-42.538667-42.666667H383.872A42.496 42.496 0 0 0 341.333333 128v42.666667h42.666667V128h256z" fill="#3D3D3D" ></path><path d="M213.333333 896V256H170.666667v639.957333C170.666667 919.552 189.653333 938.666667 213.376 938.666667h597.248C834.218667 938.666667 853.333333 919.68 853.333333 895.957333V256h-42.666666v640H213.333333z" fill="#3D3D3D" ></path><path d="M320 341.333333h42.666667v384h-42.666667zM490.666667 341.333333h42.666666v384h-42.666666zM661.333333 341.333333h42.666667v384h-42.666667z" fill="#3D3D3D" ></path></symbol><symbol id="icon-bianji" viewBox="0 0 1024 1024"><path d="M153.6 902.656a32.256 32.256 0 0 1 0-64h716.8a32.256 32.256 0 0 1 0 64zM743.936 151.04l72.192 72.192a51.2 51.2 0 0 1 0 72.192L358.4 751.616a51.2 51.2 0 0 1-36.352 14.848H226.816a25.6 25.6 0 0 1-25.6-25.6v-97.792a51.2 51.2 0 0 1 14.848-36.352l455.68-455.68a51.2 51.2 0 0 1 72.192 0z m-478.72 497.152v54.272h54.272l442.88-442.88L708.096 204.8z" fill="#5A5A68" ></path></symbol><symbol id="icon-tianjia" viewBox="0 0 1024 1024"><path d="M924.9 337.6c-22.6-53.4-54.9-101.3-96-142.4-41.1-41.1-89.1-73.5-142.4-96-55.3-23.5-114-35.3-174.5-35.3S392.8 75.8 337.6 99.1c-53.4 22.6-101.3 54.9-142.4 96-41.1 41.1-73.5 89.1-96 142.4C75.8 392.8 64 451.5 64 512s11.9 119.2 35.2 174.4c22.6 53.4 54.9 101.3 96 142.4 41.1 41.1 89.1 73.5 142.4 96 55.3 23.4 114 35.2 174.5 35.2s119.2-11.9 174.4-35.2c53.4-22.6 101.3-54.9 142.4-96 41.1-41.1 73.5-89.1 96-142.4 23.4-55.3 35.2-114 35.2-174.4 0-60.5-11.8-119.2-35.2-174.4zM512 902.5c-215.3 0-390.5-175.2-390.5-390.5S296.7 121.5 512 121.5 902.5 296.7 902.5 512 727.3 902.5 512 902.5z" ></path><path d="M681.7 483.2H540.8v-141c0-15.9-12.9-28.8-28.8-28.8s-28.8 12.9-28.8 28.8v140.9h-141c-15.9 0-28.8 12.9-28.8 28.8s12.9 28.8 28.8 28.8h140.9v140.9c0 15.9 12.9 28.8 28.8 28.8s28.8-12.9 28.8-28.8V540.8h140.9c15.9 0 28.8-12.9 28.8-28.8 0.2-15.9-12.8-28.8-28.7-28.8z" ></path></symbol><symbol id="icon-gedian" viewBox="0 0 1024 1024"><path d="M371.29728 181.76A87.46752 87.46752 0 1 1 284.16 269.22752 87.296 87.296 0 0 1 371.29728 181.76z m278.84544 0a87.46752 87.46752 0 1 1-87.13984 87.46752A87.296 87.296 0 0 1 650.14272 181.76zM371.29728 461.65248A87.46752 87.46752 0 1 1 284.16 549.12a87.296 87.296 0 0 1 87.13728-87.46752z m278.84544 0a87.46752 87.46752 0 1 1-87.13984 87.46752 87.296 87.296 0 0 1 87.13984-87.46752zM371.29728 741.54752A87.46752 87.46752 0 1 1 284.16 829.01248a87.296 87.296 0 0 1 87.13728-87.46496z m278.84544 0a87.46752 87.46752 0 1 1-87.13984 87.46496 87.296 87.296 0 0 1 87.13984-87.46496z" ></path></symbol><symbol id="icon-gongzuotai" viewBox="0 0 1024 1024"><path d="M837.12 112.64H161.28c-43.52 0-76.8 35.84-76.8 76.8v468.48c0 43.52 35.84 76.8 76.8 76.8h276.48v156.16H215.04c-15.36 0-25.6 12.8-25.6 25.6 0 15.36 12.8 25.6 25.6 25.6h570.88c15.36 0 25.6-12.8 25.6-25.6 0-15.36-12.8-25.6-25.6-25.6h-243.2v-156.16h294.4c43.52 0 76.8-35.84 76.8-76.8V189.44c2.56-43.52-33.28-76.8-76.8-76.8z m25.6 545.28c0 15.36-12.8 25.6-25.6 25.6H161.28c-15.36 0-25.6-12.8-25.6-25.6V189.44c0-15.36 12.8-25.6 25.6-25.6h675.84c15.36 0 25.6 12.8 25.6 25.6v468.48z m0 0" ></path></symbol><symbol id="icon-quanping" viewBox="0 0 1024 1024"><path d="M181 357.5V181.2h176.4c14.3 0 25.9-11.6 25.9-25.9v-31.1c0-14.3-11.6-25.9-25.9-25.9H118c-11 0-20 9-20 20v239.4c0 14.3 11.6 25.9 25.9 25.9H155c14.4-0.1 26-11.7 26-26.1zM668.6 181.2H845v176.4c0 14.3 11.6 25.9 25.9 25.9H902c14.3 0 25.9-11.6 25.9-25.9V118.2c0-11-9-20-20-20H668.6c-14.3 0-25.9 11.6-25.9 25.9v31.1c0 14.3 11.6 26 25.9 26zM357.4 845.2H181V668.8c0-14.3-11.6-25.9-25.9-25.9H124c-14.3 0-25.9 11.6-25.9 25.9v239.4c0 11 9 20 20 20h239.4c14.3 0 25.9-11.6 25.9-25.9v-31.1c-0.1-14.4-11.7-26-26-26zM845 668.8v176.4H668.6c-14.3 0-25.9 11.6-25.9 25.9v31.1c0 14.3 11.6 25.9 25.9 25.9H908c11 0 20-9 20-20V668.8c0-14.3-11.6-25.9-25.9-25.9H871c-14.4 0-26 11.6-26 25.9z" ></path></symbol></svg>',(c=>{var t=(e=(e=document.getElementsByTagName("script"))[e.length-1]).getAttribute("data-injectcss"),e=e.getAttribute("data-disable-injectsvg");if(!e){var i,n,o,a,d,h=function(t,e){e.parentNode.insertBefore(t,e)};if(t&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}i=function(){var t,e=document.createElement("div");e.innerHTML=c._iconfont_svg_string_5072634,(e=e.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",e=e,(t=document.body).firstChild?h(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(i,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),i()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(o=i,a=c.document,d=!1,s(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,l())})}function l(){d||(d=!0,o())}function s(){try{a.documentElement.doScroll("left")}catch(t){return void setTimeout(s,50)}l()}})(window);
\ No newline at end of file \ No newline at end of file
...@@ -57,39 +57,39 @@ const props = defineProps({ ...@@ -57,39 +57,39 @@ const props = defineProps({
justify-content: center; justify-content: center;
align-items: center; align-items: center;
.stat-card { .stat-card {
.vw(width, 170); .vw(width, 180);
.vw(border-radius, 5); .vw(border-radius, 5);
.vw(margin-right,10);
.vw(padding, 10); .vw(padding, 10);
.vw(margin,10);
background-image: url("@/assets/images/complete.png"); background-image: url("@/assets/images/complete.png");
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
.stat-card-title { .stat-card-title {
.font(12); .font(12);
text-align: center; text-align: center;
} }
.stat-card-value { .stat-card-value {
.font(12); .font(12);
display: flex;
flex-direction: column;
justify-content: space-between;
.vh(margin-top,20);
.card-value-top {
.vh(margin-bottom,10);
}
.card-value-top, .card-value-top,
.card-value-bottom { .card-value-bottom {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
p {
.vh(height,10);
}
.pro-title { .pro-title {
.font(10); .font(10);
margin-bottom: -12px;
} }
.pro-value { .pro-value {
.font(18); .font(16);
font-family: "DIN"; font-family: "DIN";
color: @Color; color: @Color;
font-weight: 100;
span { span {
.font(10); .font(10);
font-weight: 400; // font-weight: 400;
} }
} }
} }
...@@ -110,7 +110,7 @@ const props = defineProps({ ...@@ -110,7 +110,7 @@ const props = defineProps({
} }
.trend-icon { .trend-icon {
font-size: 12px; .font(12);
color: #32d25a; color: #32d25a;
&.down-trend { &.down-trend {
......
...@@ -30,20 +30,18 @@ const props = defineProps({ ...@@ -30,20 +30,18 @@ const props = defineProps({
.stat-cards-container { .stat-cards-container {
display: flex; display: flex;
.stat-card { .stat-card {
.vw(width, 170); .vw(width, 180);
.vw(border-radius, 5); .vw(border-radius, 5);
.vw(padding, 10); .vw(padding, 10);
.vw(margin,10); .vw(margin-right,10);
background-image: url("@/assets/images/total2.png"); background-image: url("@/assets/images/total2.png");
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
background-position: center;
.stat-card-title { .stat-card-title {
.font(12); .font(12);
font-weight: 700;
} }
.stat-card-value { .stat-card-value {
.font(20); .font(18);
color: @Color; color: @Color;
font-family: "DIN"; font-family: "DIN";
} }
...@@ -52,7 +50,6 @@ const props = defineProps({ ...@@ -52,7 +50,6 @@ const props = defineProps({
justify-content: space-between; justify-content: space-between;
.amount-title { .amount-title {
.font(10); .font(10);
.vh(height, 5);
} }
.amount-value { .amount-value {
.font(16); .font(16);
......
...@@ -133,33 +133,28 @@ onMounted(() => {}); ...@@ -133,33 +133,28 @@ onMounted(() => {});
<style scoped lang="less"> <style scoped lang="less">
.common-container { .common-container {
display: flex;
justify-content: center;
.stat-cards-container { .stat-cards-container {
display: flex; display: flex;
.stat-card { .stat-card {
.vw(width, 170); .vw(width, 180);
.vw(border-radius, 5); .vw(border-radius, 5);
.vw(padding, 20); .vw(padding, 10);
// .vw(margin,10); .vw(margin-right,10);
background-image: url("@/assets/images/total2.png"); background-image: url("@/assets/images/total2.png");
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 90% 80%; background-size: contain;
background-position: center;
.stat-card-title { .stat-card-title {
.font(12); .font(12);
} }
.stat-card-value { .stat-card-value {
.font(24); .font(18);
cursor: pointer; cursor: pointer;
font-family: "DIN"; font-family: "DIN";
} }
} }
:last-child {
margin-right: 0;
}
} }
}
:deep(.el-dialog__title) {
font-weight: 900;
}
:deep(.el-dialog) {
} }
</style> </style>
.tab-content{ .tab-content{
th{ th{
text-align: center; // text-align: center;
} }
} }
\ No newline at end of file
...@@ -11,16 +11,12 @@ ...@@ -11,16 +11,12 @@
<div class="construct-right"> <div class="construct-right">
<div> <div>
<div class="info-title">项目概况</div> <div class="info-title">项目概况</div>
<div>
<CommonTotal :numberList="projectList" /> <CommonTotal :numberList="projectList" />
</div> </div>
</div>
<div> <div>
<div class="info-title">投资完成情况</div> <div class="info-title">投资完成情况</div>
<div>
<CommonTotal :numberList="investmentList" /> <CommonTotal :numberList="investmentList" />
</div> </div>
</div>
<div> <div>
<div class="info-title">投资完成情况</div> <div class="info-title">投资完成情况</div>
<div class="progress"> <div class="progress">
...@@ -39,31 +35,35 @@ ...@@ -39,31 +35,35 @@
</div> </div>
<div> <div>
<div class="info-title">投资回收完成情况</div> <div class="info-title">投资回收完成情况</div>
<div>
<CommonTotal :numberList="recycleList" /> <CommonTotal :numberList="recycleList" />
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script setup> <script setup>
import Map from "@/components/CommonMap.vue"; import Map from "@/components/CommonMap.vue";
import CommonTotal from "@/components/CommonTotal.vue"; import CommonTotal from "@/components/CommonTotal.vue";
import { reactive, ref, onMounted, onUnmounted } from "vue"; import { reactive, ref, onMounted, onUnmounted, shallowReactive, defineOptions } from "vue";
import CircleProgress from "./CircleProgress.vue"; import CircleProgress from "./CircleProgress.vue";
const projectList = reactive([
// 定义组件名称,用于keep-alive缓存
defineOptions({
name: 'Construct'
});
// 使用shallowReactive减少深层响应式开销
const projectList = shallowReactive([
{ title: "总个数(个)", value: "59" }, { title: "总个数(个)", value: "59" },
{ title: "总投资(亿元)", value: "2733.35" }, { title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" }, { title: "总规模(公里)", value: "5116.72" },
]); ]);
const investmentList = reactive([ const investmentList = shallowReactive([
{ title: "10月完成投资(亿元)", value: "0" }, { title: "10月完成投资(亿元)", value: "0" },
{ title: "2025年完成投资(亿元)", value: "19.725" }, { title: "2025年完成投资(亿元)", value: "19.725" },
{ title: "累计完成投资(亿元)", value: "807.56" }, { title: "累计完成投资(亿元)", value: "807.56" },
]); ]);
// recycleList // recycleList
const recycleList = reactive([ const recycleList = shallowReactive([
{ title: "10月完成投资(亿元)", value: "0" }, { title: "10月完成投资(亿元)", value: "0" },
{ title: "2025年完成投资(亿元)", value: "2.98" }, { title: "2025年完成投资(亿元)", value: "2.98" },
{ title: "完成率", value: "50.2%" }, { title: "完成率", value: "50.2%" },
...@@ -74,7 +74,7 @@ const recycleList = reactive([ ...@@ -74,7 +74,7 @@ const recycleList = reactive([
.construct-container { .construct-container {
display: flex; display: flex;
.construct-left { .construct-left {
flex: 1; width: 100%;
height: 100%; height: 100%;
.tag-image { .tag-image {
img { img {
...@@ -94,11 +94,12 @@ const recycleList = reactive([ ...@@ -94,11 +94,12 @@ const recycleList = reactive([
.vw(width, 180); .vw(width, 180);
.vh(height, 30); .vh(height, 30);
.vh(line-height, 30); .vh(line-height, 30);
.vw(margin-left,20); .font(16);
.font(20); .vw(padding-left,20);
.vw(padding-left,23); .vh(margin-bottom,15);
text-align: left; text-align: left;
color: #fff; color: #fff;
white-space: nowrap;
font-family: YouSheBiaoTiYuan; font-family: YouSheBiaoTiYuan;
background-image: url("@/assets/images/tag.png"); background-image: url("@/assets/images/tag.png");
background-repeat: no-repeat; background-repeat: no-repeat;
...@@ -132,24 +133,21 @@ const recycleList = reactive([ ...@@ -132,24 +133,21 @@ const recycleList = reactive([
.vw(left,65); .vw(left,65);
width: 85%; width: 85%;
height: 30%; height: 30%;
background-image: url(/src/assets/images/baseRate.png); background-image: url("@/assets/images/baseRate.png");
background-size: 100% 100%; background-size: 100% 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
.progress-show-value-rel { .progress-show-value-rel {
height: 100%;
background-image: url("@/assets/images/baseRateRel.png");
background-size: auto 100%;
-webkit-transform: skew(-30deg);
transform: skew(-30deg);
position: absolute; position: absolute;
.vh(top,26); .vh(top,26);
.vw(left,65); .vw(left,65);
height: 30%;
max-width: 83%; max-width: 83%;
height: 30%;
background-image: url(/src/assets/images/progress.png); background-image: url(/src/assets/images/progress.png);
background-size: 100% 100%; background-size: 100% 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
-webkit-transform: skew(-30deg);
transform: skew(-30deg);
} }
} }
} }
......
...@@ -11,36 +11,26 @@ ...@@ -11,36 +11,26 @@
<div class="construct-right"> <div class="construct-right">
<div> <div>
<div class="info-title">项目概况</div> <div class="info-title">项目概况</div>
<div>
<CommonTotal :numberList="projectList" /> <CommonTotal :numberList="projectList" />
</div> </div>
</div>
<div> <div>
<div class="info-title">投资完成情况</div> <div class="info-title">投资完成情况</div>
<div>
<CommonTotal :numberList="investmentList" /> <CommonTotal :numberList="investmentList" />
</div> </div>
</div>
<div> <div>
<div class="info-title">经营计划完成情况</div> <div class="info-title">经营计划完成情况</div>
<div>
<CommonComplete :numberList="completeList" /> <CommonComplete :numberList="completeList" />
</div> </div>
</div>
<div> <div>
<div class="info-title">投资回收完成情况</div> <div class="info-title">投资回收完成情况</div>
<div>
<CommonTotal :numberList="recycleList" /> <CommonTotal :numberList="recycleList" />
</div> </div>
</div>
<div> <div>
<div class="info-title">2025年经营计划完成进度</div> <div class="info-title">2025年经营计划完成进度</div>
<div>
<CommonPlant :numberList="operationList" /> <CommonPlant :numberList="operationList" />
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script setup> <script setup>
...@@ -48,28 +38,34 @@ import Map from "@/components/CommonMap.vue"; ...@@ -48,28 +38,34 @@ import Map from "@/components/CommonMap.vue";
import CommonTotal from "@/components/CommonTotal.vue"; import CommonTotal from "@/components/CommonTotal.vue";
import CommonComplete from "@/components/CommonComplete.vue"; import CommonComplete from "@/components/CommonComplete.vue";
import CommonPlant from "@/components/CommonPlant.vue"; import CommonPlant from "@/components/CommonPlant.vue";
import { reactive, ref, onMounted, onUnmounted } from "vue"; import { shallowReactive, ref, onMounted, onUnmounted, defineOptions } from "vue";
const projectList = reactive([
// 定义组件名称,用于keep-alive缓存
defineOptions({
name: 'Operation'
});
// 使用shallowReactive减少深层响应式开销
const projectList = shallowReactive([
{ title: "总个数(个)", value: "59" }, { title: "总个数(个)", value: "59" },
{ title: "总投资(亿元)", value: "2733.35" }, { title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" }, { title: "总规模(公里)", value: "5116.72" },
]); ]);
const investmentList = reactive([ const investmentList = shallowReactive([
{ title: "10月完成投资(亿元)", value: "0" }, { title: "10月完成投资(亿元)", value: "0" },
{ title: "2025年完成投资(亿元)", value: "19.725" }, { title: "2025年完成投资(亿元)", value: "19.725" },
{ title: "累计完成投资(亿元)", value: "807.56" }, { title: "累计完成投资(亿元)", value: "807.56" },
]); ]);
const recycleList = reactive([ const recycleList = shallowReactive([
{ title: "10月完成投资(亿元)", value: "0" }, { title: "10月完成投资(亿元)", value: "0" },
{ title: "2025年完成投资(亿元)", value: "2.98" }, { title: "2025年完成投资(亿元)", value: "2.98" },
{ title: "完成率", value: "50.2%" }, { title: "完成率", value: "50.2%" },
]); ]);
const operationList = reactive([ const operationList = shallowReactive([
{ title: "营业收入完成进度", value: "68.1%" }, { title: "营业收入完成进度", value: "68.1%" },
{ title: "运营成本完成进度", value: "68.1%" }, { title: "运营成本完成进度", value: "68.1%" },
{ title: "利润总额完成进度", value: "68.1%" }, { title: "利润总额完成进度", value: "68.1%" },
]); ]);
const completeList = reactive([ const completeList = shallowReactive([
{ {
title: "营业收入", title: "营业收入",
protitle: "11月完成", protitle: "11月完成",
...@@ -101,7 +97,7 @@ const completeList = reactive([ ...@@ -101,7 +97,7 @@ const completeList = reactive([
.construct-container { .construct-container {
display: flex; display: flex;
.construct-left { .construct-left {
flex: 1; width: 100%;
height: 100%; height: 100%;
.tag-image { .tag-image {
img { img {
...@@ -118,14 +114,16 @@ const completeList = reactive([ ...@@ -118,14 +114,16 @@ const completeList = reactive([
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
.info-title { .info-title {
.vw(width, 230); .vw(width, 180);
.vh(height, 30); .vh(height, 30);
.vh(line-height, 30); .vh(line-height, 30);
.vw(margin-left,10); .font(16);
.font(20); .vw(padding-left,20);
.vw(padding-left,28); .vh(margin-bottom,15);
.vh(margin-top,15);
text-align: left; text-align: left;
color: #fff; color: #fff;
white-space: nowrap;
font-family: YouSheBiaoTiYuan; font-family: YouSheBiaoTiYuan;
background-image: url("@/assets/images/tag.png"); background-image: url("@/assets/images/tag.png");
background-repeat: no-repeat; background-repeat: no-repeat;
......
<template> <template>
<div class="project-container">
<CommonTotal :numberList="initiationList" /> <CommonTotal :numberList="initiationList" />
</div>
<Map /> <Map />
</template> </template>
<script setup> <script setup>
import CommonTotal from "@/components/CommonTotal.vue"; import CommonTotal from "@/components/CommonTotal.vue";
import Map from "@/components/CommonMap.vue"; import Map from "@/components/CommonMap.vue";
import { onMounted, reactive, watch, ref } from "vue"; import { onMounted, reactive, watch, ref, computed, shallowRef, defineOptions } from "vue";
// 定义组件名称,用于keep-alive缓存
defineOptions({
name: 'ProjectApproval'
});
const props = defineProps({ const props = defineProps({
currentName: { currentName: {
...@@ -14,64 +21,36 @@ const props = defineProps({ ...@@ -14,64 +21,36 @@ const props = defineProps({
default: "立项", default: "立项",
}, },
}); });
const initiationList = ref([]); // 使用静态数据避免重复创建
// 根据currentName设置不同的数据 const STATIC_DATA = {
const setNumberList = (name) => { 立项: [
if (name === "立项") { { title: "总个数(个)", value: "59" },
initiationList.value = [ { title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" },
],
转让: [
{ title: "总个数(个)", value: "59" }, { title: "总个数(个)", value: "59" },
{ title: "总投资(亿元)", value: "2733.35" }, { title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" }, { title: "总规模(公里)", value: "5116.72" },
]; { title: "累计完成投资(亿元)", value: "3116.44" },
} else { ],
initiationList.value = [ 退出: [
{ title: "总个数(个)", value: "59" }, { title: "总个数(个)", value: "59" },
{ title: "总投资(亿元)", value: "2733.35" }, { title: "总投资(亿元)", value: "2733.35" },
{ title: "总规模(公里)", value: "5116.72" }, { title: "总规模(公里)", value: "5116.72" },
{ title: "累计完成投资(亿元)", value: "3116.44" }, { title: "累计完成投资(亿元)", value: "3116.44" },
]; ],
}
}; };
watch( // 使用computed进行响应式计算,避免不必要的重新渲染
() => props.currentName, const initiationList = computed(() => {
(newVal) => { return STATIC_DATA[props.currentName] || STATIC_DATA.立项;
setNumberList(newVal);
},
{
immediate: true,
deep: true,
}
);
onMounted(() => {
setNumberList(props.currentName);
}); });
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.stat-cards-container { .project-container {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center;
.stat-card {
.vw(width, 180);
.vh(height, 80);
.vw(border-radius, 5);
.vw(padding, 10);
.vw(padding, 10);
.vw(margin,10);
background-image: url("@/assets/images/total.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
.stat-card-title {
.font(14);
}
.stat-card-value {
.vh(margin-top, 20);
.font(20);
}
}
} }
</style> </style>
<template> <template>
<div class="home-container"> <div class="construct-container">
<div class="header"> <div class="construct-left">
<div class="header-left"> <div class="tag-image">
<div <img src="@/assets/images/建设.png" />
class="nav-btn"
:class="{ active: selectedLeftBtn === 'equity' }"
@click="selectLeftBtn('equity')"
>
股权投资
</div> </div>
<div <div class="construct-left-map">
class="nav-btn" <Map />
:class="{ active: selectedLeftBtn === 'risk' }"
@click="selectLeftBtn('risk')"
>
固定风险投资
</div> </div>
</div> </div>
<div class="header-middile"> <div class="construct-right">
葛洲坝集团交通投资有限公司投资决策分析系统 <div>
<div class="info-title">项目概况</div>
<CommonTotal :numberList="projectList" />
</div> </div>
<div class="header-right"> <div>
<div <div class="info-title">投资完成情况</div>
class="nav-btn" <CommonTotal :numberList="investmentList" />
:class="{ active: selectedRightBtn === 'realestate' }"
@click="selectRightBtn('realestate')"
>
房地产投资
</div> </div>
<div <div>
class="nav-btn" <div class="info-title">经营计划完成情况</div>
:class="{ active: selectedRightBtn === 'finance' }" <CommonComplete :numberList="completeList" />
@click="selectRightBtn('finance')"
>
融资建设投资
</div> </div>
<div>
<div class="info-title">投资回收完成情况</div>
<CommonTotal :numberList="recycleList" />
</div> </div>
<div>
<div class="info-title">2025年经营计划完成进度</div>
<CommonPlant :numberList="operationList" />
</div> </div>
<div class="content">
<div class="fullscreen-btn">
<span @click="toggleFullscreen" class="fullscreen-text">
<el-icon :size="18">
<full-screen />
</el-icon>
{{ isFullscreen ? "退出全屏" : "全屏" }}
</span>
</div> </div>
<div class="content-btn">
<span
v-for="item in navList"
:key="item.index"
:class="{ active: selectedContentBtn === item.index }"
@click="selectContentBtn(item)"
>{{ item.name }}
</span>
</div>
<ProjectApproval
:currentName="selectedContentName"
v-if="
selectedContentBtn === 1 ||
selectedContentBtn === 4 ||
selectedContentBtn === 5
"
/>
<Construct v-else-if="selectedContentBtn === 2" />
<Operation v-else />
</div>
<div class="bottom"></div>
</div> </div>
</template> </template>
<script setup> <script setup>
import Construct from "../components/Construct.vue"; import Map from "@/components/CommonMap.vue";
import ProjectApproval from "../components/ProjectApproval.vue"; import CommonTotal from "@/components/CommonTotal.vue";
import Operation from "../components/Operation.vue"; import CommonComplete from "@/components/CommonComplete.vue";
import { reactive, ref } from "vue"; import CommonPlant from "@/components/CommonPlant.vue";
import { reactive, ref, onMounted, onUnmounted } from "vue";
const selectedLeftBtn = ref("equity"); const projectList = reactive([
const selectedRightBtn = ref(""); { title: "总个数(个)", value: "59" },
const selectedContentBtn = ref(1); { title: "总投资(亿元)", value: "2733.35" },
const selectedContentName = ref("立项"); { title: "总规模(公里)", value: "5116.72" },
const isFullscreen = ref(false); ]);
const showPopup = ref(false); const investmentList = reactive([
const currentProject = ref({}); { title: "10月完成投资(亿元)", value: "0" },
const popupStyle = ref({}); { title: "2025年完成投资(亿元)", value: "19.725" },
{ title: "累计完成投资(亿元)", value: "807.56" },
const navList = reactive([ ]);
{ const recycleList = reactive([
index: 1, { title: "10月完成投资(亿元)", value: "0" },
name: "立项", { title: "2025年完成投资(亿元)", value: "2.98" },
}, { title: "完成率", value: "50.2%" },
{ ]);
index: 2, const operationList = reactive([
name: "建设", { title: "营业收入完成进度", value: "68.1%" },
}, { title: "运营成本完成进度", value: "68.1%" },
{ title: "利润总额完成进度", value: "68.1%" },
]);
const completeList = reactive([
{ {
index: 3, title: "营业收入",
name: "运营", protitle: "11月完成",
proValue: "1.1",
thirdTtile: "第三季度完成",
thirdValue: "4.21",
compareValue: "+1.2%",
}, },
{ {
index: 4, title: "运营成本",
name: "转让", protitle: "11月完成",
proValue: "47.98",
thirdTtile: "第三季度完成",
thirdValue: "5.75",
compareValue: "-0.5%",
}, },
{ {
index: 5, title: "利润总额",
name: "退出", protitle: "11月完成",
proValue: "9.90",
thirdTtile: "第三季度完成",
thirdValue: "23.52",
compareValue: "+1.2%",
}, },
]); ]);
const selectLeftBtn = (btn) => {
selectedLeftBtn.value = btn;
selectedRightBtn.value = "";
};
const selectRightBtn = (btn) => {
selectedRightBtn.value = btn;
selectedLeftBtn.value = "";
};
const selectContentBtn = (item) => {
selectedContentBtn.value = item.index;
selectedContentName.value = item.name;
};
const showProjectPopup = (event) => {
currentProject.value = {
name: "项目001",
data: [
{ label: "数据01", value: "3654.89", unit: "单位" },
{ label: "数据02", value: "3654.89", unit: "单位" },
{ label: "数据03", value: "3654.89", unit: "单位" },
],
};
popupStyle.value = {
left: `${event.clientX}px`,
top: `${event.clientY - 100}px`,
};
showPopup.value = true;
};
// 全屏切换功能
const toggleFullscreen = () => {
const homeContainer = document.querySelector(".home-container");
if (isFullscreen.value) {
// 退出全屏
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
homeContainer.style.height = "100%";
} else {
// 进入全屏
if (homeContainer.requestFullscreen) {
homeContainer.requestFullscreen();
} else if (homeContainer.webkitRequestFullscreen) {
homeContainer.webkitRequestFullscreen();
} else if (homeContainer.msRequestFullscreen) {
homeContainer.msRequestFullscreen();
}
homeContainer.style.height = "100vh";
}
isFullscreen.value = !isFullscreen.value;
};
// 监听全屏变化事件
window.addEventListener("fullscreenchange", () => {
if (!document.fullscreenElement) {
isFullscreen.value = false;
const homeContainer = document.querySelector(".home-container");
if (homeContainer) homeContainer.style.height = "100%";
} else {
isFullscreen.value = true;
const homeContainer = document.querySelector(".home-container");
if (homeContainer) homeContainer.style.height = "100vh";
}
});
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.home-container { .construct-container {
// background-image: url("@/assets/images/bg.png");
// background-repeat: no-repeat;
// background-size: cover;
// background-position: center;
width: 100%;
height: 100%;
background-color: #04427e;
overflow: hidden;
color: #fff;
display: flex; display: flex;
flex-direction: column; .construct-left {
justify-content: space-between;
.header {
width: 100%; width: 100%;
.vh(height, 80); height: 100%;
display: flex; .tag-image {
justify-content: center; img {
align-items: flex-end; .vw(width, 85);
.header-middile {
background-image: url("@/assets/images/header.png");
background-repeat: no-repeat;
background-position: center;
background-size: auto;
.font(28);
letter-spacing: 1px;
font-weight: 400;
font-family: "YouSheBiaoTiHei";
.vw(width,950);
height: 74px;
display: flex;
justify-content: center;
align-items: center;
} }
.header-left {
display: flex;
align-items: center;
justify-content: center;
.nav-btn {
.font(16);
cursor: pointer;
color: #fff;
font-family: YouSheBiaoTiYuan;
font-weight: 100;
display: flex;
align-items: center;
justify-content: center;
&:not(.active) {
background-image: url("@/assets/images/left-btn.png");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
width: 155px;
height: 35px;
background-size: 100% 100%;
}
&.active {
background-image: url("@/assets/images/left-hlightBtn.png");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
width: 155px;
height: 35px;
background-size: 100% 100%;
} }
.construct-left-map {
.vh(height, 700);
.vh(padding-top, 4);
} }
} }
.header-right { .construct-right {
display: flex; display: flex;
justify-content: center; flex-direction: column;
align-items: center; justify-content: space-between;
.nav-btn { .info-title {
.vw(width, 180);
.vh(height, 30);
.vh(line-height, 30);
.font(16); .font(16);
cursor: pointer; .vw(padding-left,20);
.vh(margin-bottom,15);
.vh(margin-top,15);
text-align: left;
color: #fff; color: #fff;
display: flex; white-space: nowrap;
align-items: center;
justify-content: center;
font-family: YouSheBiaoTiYuan; font-family: YouSheBiaoTiYuan;
&:not(.active) { background-image: url("@/assets/images/tag.png");
background-image: url("@/assets/images/right-btn.png");
background-repeat: no-repeat;
background-position: center;
width: 155px;
height: 35px;
background-size: 100% 100%;
}
&.active {
background-image: url("@/assets/images/right-hight-btn.png");
background-repeat: no-repeat;
background-position: center;
background-size: contain;
width: 155px;
height: 35px;
background-size: 100% 100%;
}
}
}
}
.content {
flex: 1;
.vw(padding-left, 20);
.vw(padding-right, 20);
.content-btn {
.vh(margin-top,-10);
.vh(margin-bottom,20);
display: flex;
justify-content: center;
align-items: center;
span {
.font(14);
width: 110px;
height: 35px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
flex-shrink: 0;
color: #fff; // 补充文字颜色(匹配蓝色按钮的白色文字)
}
// 立项按钮
:nth-child(1) {
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: center;
width: 115px;
height: 35px;
line-height: 35px;
text-align: center;
&.active {
background-image: url("@/assets/images/立项.png");
}
&:not(.active) {
background-image: url("@/assets/images/initiation-default.png");
}
}
// 退出按钮
:nth-child(5) {
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: center;
width: 111px;
height: 35px;
line-height: 35px;
text-align: center;
&.active {
background-image: url("@/assets/images/退出-active.png");
}
&:not(.active) {
background-image: url("@/assets/images/退出.png");
}
}
// 中间按钮(建设、运营、转止)
:nth-child(2),
:nth-child(3),
:nth-child(4) {
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: center;
width: 115px;
height: 35px;
line-height: 35px;
text-align: center;
margin-left: -32px;
background-image: url("@/assets/images/default-btn.png");
&.active {
background-image: url("@/assets/images/nav-hight-btn.png");
}
}
:nth-child(5) {
margin-left: -31px;
}
}
}
.bottom {
background-image: url("@/assets/images/bottom.png");
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
background-position: center; background-position: center;
width: 100%; background-size: 100% 100%;
.vh(height, 80);
}
.fullscreen-btn {
display: flex;
cursor: pointer;
span {
margin-left: auto;
color: white;
font-weight: 700;
.font(16);
display: flex;
align-items: center;
gap: 6px;
} }
} }
} }
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
>{{ item.name }} >{{ item.name }}
</span> </span>
</div> </div>
<keep-alive include="ProjectApproval,Construct,Operation">
<ProjectApproval <ProjectApproval
:currentName="selectedContentName" :currentName="selectedContentName"
v-if=" v-if="
...@@ -65,6 +66,7 @@ ...@@ -65,6 +66,7 @@
/> />
<Construct v-else-if="selectedContentBtn === 2" /> <Construct v-else-if="selectedContentBtn === 2" />
<Operation v-else /> <Operation v-else />
</keep-alive>
</div> </div>
<div class="bottom"></div> <div class="bottom"></div>
</div> </div>
...@@ -74,7 +76,7 @@ ...@@ -74,7 +76,7 @@
import Construct from "./components/Construct.vue"; import Construct from "./components/Construct.vue";
import ProjectApproval from "./components/ProjectApproval.vue"; import ProjectApproval from "./components/ProjectApproval.vue";
import Operation from "./components/Operation.vue"; import Operation from "./components/Operation.vue";
import { reactive, ref } from "vue"; import { reactive, ref, nextTick } from "vue";
const selectedLeftBtn = ref("equity"); const selectedLeftBtn = ref("equity");
const selectedRightBtn = ref(""); const selectedRightBtn = ref("");
...@@ -118,27 +120,43 @@ const selectRightBtn = (btn) => { ...@@ -118,27 +120,43 @@ const selectRightBtn = (btn) => {
selectedLeftBtn.value = ""; selectedLeftBtn.value = "";
}; };
// 添加防抖功能,避免频繁切换造成的性能问题
let debounceTimer = null;
const selectContentBtn = (item) => { const selectContentBtn = (item) => {
// 清除之前的定时器
if (debounceTimer) {
clearTimeout(debounceTimer);
}
// 使用防抖延迟切换
debounceTimer = setTimeout(() => {
selectedContentBtn.value = item.index; selectedContentBtn.value = item.index;
selectedContentName.value = item.name; selectedContentName.value = item.name;
};
const showProjectPopup = (event) => { // 使用nextTick确保DOM更新完成
currentProject.value = { nextTick(() => {
name: "项目001", // 可以在这里添加其他需要延迟执行的逻辑
data: [ });
{ label: "数据01", value: "3654.89", unit: "单位" }, }, 100); // 100ms防抖延迟
{ label: "数据02", value: "3654.89", unit: "单位" },
{ label: "数据03", value: "3654.89", unit: "单位" },
],
};
popupStyle.value = {
left: `${event.clientX}px`,
top: `${event.clientY - 100}px`,
};
showPopup.value = true;
}; };
// 暂时注释未使用的函数,避免lint警告
// const showProjectPopup = (event) => {
// currentProject.value = {
// name: "项目001",
// data: [
// { label: "数据01", value: "3654.89", unit: "单位" },
// { label: "数据02", value: "3654.89", unit: "单位" },
// { label: "数据03", value: "3654.89", unit: "单位" },
// ],
// };
// popupStyle.value = {
// left: `${event.clientX}px`,
// top: `${event.clientY - 100}px`,
// };
// showPopup.value = true;
// };
// 全屏切换功能 // 全屏切换功能
const toggleFullscreen = () => { const toggleFullscreen = () => {
const homeContainer = document.querySelector(".home-container"); const homeContainer = document.querySelector(".home-container");
......
...@@ -10,19 +10,47 @@ ...@@ -10,19 +10,47 @@
<el-icon><search /></el-icon> <el-icon><search /></el-icon>
</template> </template>
</el-input> </el-input>
<div class="tree-content">
<span class="filterRow">
<el-dropdown>
<span class="el-dropdown-link">
<i class="iconfont icon-gengduo"></i>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="handleOriginEdit"
><i class="iconfont icon-bianji"></i
>编辑组织</el-dropdown-item
>
<el-dropdown-item @click="handleDeleteOrigin"
><i class="iconfont icon-shanchu"></i
>删除组织</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</span>
<el-tree
:data="treeData"
node-key="id"
default-expand-all
draggable
@node-click="handleNodeClick"
>
</el-tree>
</div>
</div>
<div class="table-container" v-loading="userLoading">
<div class="search-contain">
<div>
人员信息:
<el-input placeholder="请输入人员信息" v-model="userName" />
</div>
<div>
<el-button type="primary" @click="handleUserData">查询</el-button>
<el-button type="" @click="handleReset">重置</el-button>
</div> </div>
<div class="table-container">
<div class="origin-title">
<h3>部门管理</h3>
<el-button type="primary" @click="handleAdd"> 新增 </el-button>
</div> </div>
<commonForm
v-model="searchForm"
:config="searchConfig"
:items="searchItems"
@submit="handleSearch"
@reset="handleReset"
/>
<common-table <common-table
:tableHeight="tableHeight" :tableHeight="tableHeight"
:data="tableData" :data="tableData"
...@@ -35,29 +63,10 @@ ...@@ -35,29 +63,10 @@
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-page-change="handleCurrentPageChange" @current-page-change="handleCurrentPageChange"
> >
<!-- <template #header-actions>
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增
</el-button>
</template> -->
<template #enable="{ row }"> <template #enable="{ row }">
<el-switch <el-tag :type="row.enable === 0 ? 'success' : 'danger'">
:model-value="row.enable === 0 ? true : false" {{ row.enable === "0" ? "启用" : "停用" }}
@change="handleStatusChange($event, row)" </el-tag>
active-color="#13ce66"
inactive-color="#ff4949"
></el-switch>
</template>
<template #operations="{ row, index }">
<el-button type="text" size="small" @click="handleEdit(row, index)">
编辑
</el-button>
<el-button type="text" size="small" @click="handleDelete(row, index)">
删除
</el-button>
</template> </template>
</common-table> </common-table>
</div> </div>
...@@ -65,11 +74,11 @@ ...@@ -65,11 +74,11 @@
<el-dialog <el-dialog
v-model="dialogVisible" v-model="dialogVisible"
:title="dialogTitle" :title="dialogTitle"
width="600px" width="400px"
@close="handleDialogClose" @close="handleDialogClose"
> >
<commonForm <commonForm
v-model="userForm" v-model="originForm"
:config="formConfig" :config="formConfig"
:items="formItems" :items="formItems"
:rules="formRules" :rules="formRules"
...@@ -88,6 +97,11 @@ import CommonTable from "@/components/common/commonTable.vue"; ...@@ -88,6 +97,11 @@ import CommonTable from "@/components/common/commonTable.vue";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const loading = ref(false); const loading = ref(false);
const userLoading = ref(false);
const treeData = ref([]);
const originTreeData = ref([]);
const selectedNode = ref(null);
const userName = ref("");
// 计算表格高度 // 计算表格高度
const tableHeight = computed(() => { const tableHeight = computed(() => {
...@@ -95,20 +109,28 @@ const tableHeight = computed(() => { ...@@ -95,20 +109,28 @@ const tableHeight = computed(() => {
const paginationHeight = 50; const paginationHeight = 50;
const rowHeight = 40; const rowHeight = 40;
const baseHeight = headerHeight + paginationHeight; const baseHeight = headerHeight + paginationHeight;
// 1.如果数据超过10条,固定显示10行的高度 + 滚动条;2.如果数据不超过10条,按实际行数计算高度
const maxRows = Math.min(tableData.value.length, 10); const maxRows = Math.min(tableData.value.length, 10);
const contentHeight = maxRows * rowHeight; const contentHeight = maxRows * rowHeight;
return `${baseHeight + contentHeight}px`; return `${baseHeight + contentHeight}px`;
}); });
// 数据转换函数 // 数据转换函数
const convertToTreeData = (apiData) => { const convertToTreeData = (apiData, type) => {
if (type === "id") {
return apiData.map((item) => ({ return apiData.map((item) => ({
value: item.id.toString(), id: item.id,
label: item.name, label: item.name,
parentId: item.parentId,
children: item.children ? convertToTreeData(item.children) : [], children: item.children ? convertToTreeData(item.children) : [],
})); }));
} else {
return apiData.map((item) => ({
parentId: item.parentId,
value: item.id,
label: item.name,
children: item.children ? convertToTreeData(item.children) : [],
}));
}
}; };
// 表格数据 // 表格数据
...@@ -160,50 +182,17 @@ const tableColumns = [ ...@@ -160,50 +182,17 @@ const tableColumns = [
slot: "enable", slot: "enable",
align: "center", align: "center",
}, },
{
prop: "operations",
label: "操作",
width: 160,
slot: "operations",
fixed: "right",
align: "center",
},
]; ];
// 对话框相关 // 对话框相关
const dialogVisible = ref(false); const dialogVisible = ref(false);
const dialogTitle = ref("新增用户"); const dialogTitle = ref("新增用户");
const isEdit = ref(false); const isEdit = ref(false);
const editIndex = ref(-1);
const searchForm = ref({
name: "",
});
const searchConfig = {
inline: false,
labelWidth: "80px",
showButtons: true,
submitText: "查询",
resetText: "重置",
};
const searchItems = [
{
type: "input",
prop: "name",
label: "部门名称",
placeholder: "请输入部门名称",
clearable: true,
span: 8,
},
];
// 用户表单数据 // 用户表单数据
const userForm = ref({ const originForm = ref({
name: "", name: "",
departs: [], parentId: "",
positions: [],
roles: [],
enable: "0",
}); });
// 用户表单配置 // 用户表单配置
...@@ -213,121 +202,33 @@ const formConfig = { ...@@ -213,121 +202,33 @@ const formConfig = {
submitText: "保存", submitText: "保存",
resetText: "取消", resetText: "取消",
}; };
const departmentData = ref([]);
const positionsData = ref([]);
const rolesData = ref([]);
const loadDepartmentData = () => {
proxy.$post({
url: "/api/user/depart/treeDepart",
data: {},
callback: (data) => {
departmentData.value = convertToTreeData(data);
},
error: (err) => {},
});
};
// 岗位下拉数据
const loadPositionsData = () => {
proxy.$post({
url: "/api/user/position/listPosition",
data: {},
callback: (data) => {
positionsData.value = convertToTreeData(data.rows);
},
error: (err) => {},
});
};
// 角色下拉数据
const loadRolesData = () => {
proxy.$post({
url: "/api/user/role/listRole",
data: {
page: 1,
pageSize: 10,
},
callback: (data) => {
rolesData.value = convertToTreeData(data.rows);
},
error: (err) => {},
});
};
// 用户表单项配置 // 用户表单项配置
const formItems = computed(() => [ const formItems = computed(() => [
{
type: "input",
prop: "name",
label: "用户姓名",
placeholder: "请输入用户姓名",
span: 12,
},
{ {
type: "tree", type: "tree",
prop: "departs", prop: "parentId",
label: "所属部门", label: "上级组织",
placeholder: "请选择部门", placeholder: "请选择上级组织",
data: departmentData.value, data: originTreeData.value,
clearable: true, clearable: true,
filterable: true, filterable: true,
checkStrictly: true, checkStrictly: true,
renderAfterExpand: false, renderAfterExpand: false,
showCheckbox: true, showCheckbox: false,
multiple: true, multiple: false,
collapseTags: true, span: 24,
maxCollapseTags: 2, // required: true,
span: 12, // rules: [{ required: true, message: "请选择上级组织", trigger: "blur" }],
},
{
type: "tree",
prop: "positions",
label: "岗位",
placeholder: "请选择岗位",
data: positionsData.value,
clearable: true,
filterable: true,
checkStrictly: true,
renderAfterExpand: false,
showCheckbox: true,
multiple: true,
collapseTags: true,
maxCollapseTags: 2,
span: 12,
},
{
type: "tree",
prop: "roles",
label: "角色",
placeholder: "请选择角色",
data: rolesData.value,
clearable: true,
filterable: true,
checkStrictly: true,
renderAfterExpand: false,
showCheckbox: true,
multiple: true,
collapseTags: true,
maxCollapseTags: 2,
span: 12,
}, },
{ {
type: "input", type: "input",
prop: "mobile", prop: "name",
label: "手机号码", label: "组织名称:",
placeholder: "请输入手机号码", placeholder: "请输入组织名称",
span: 12, required: true,
}, rules: [{ required: true, message: "请输入组织名称", trigger: "blur" }],
{ span: 24,
type: "radio",
prop: "enable",
label: "状态",
span: 12,
options: [
{ label: "启用", value: "0" },
{ label: "停用", value: "1" },
],
}, },
]); ]);
...@@ -348,103 +249,106 @@ const handleCurrentPageChange = (page) => { ...@@ -348,103 +249,106 @@ const handleCurrentPageChange = (page) => {
// 新增用户 // 新增用户
const handleAdd = () => { const handleAdd = () => {
isEdit.value = false; isEdit.value = false;
dialogTitle.value = "新增用户"; dialogTitle.value = "新增组织";
userForm.value = { originForm.value = {
parentId: "",
name: "", name: "",
departs: [],
positions: [],
roles: [],
enable: "0",
}; };
loadDepartmentData();
loadPositionsData();
loadRolesData();
dialogVisible.value = true; dialogVisible.value = true;
}; };
let currentID = ref(); let currentID = ref("");
// 编辑 // 处理树节点点击事件
const handleEdit = (row, index) => { const handleNodeClick = (data, node, element) => {
selectedNode.value = data;
currentID.value = selectedNode.value.id;
handleUserData();
};
// 组织详情
const handleOriginEdit = () => {
if (selectedNode.value === null) {
ElMessage.warning("请选择要编辑的数据");
} else {
isEdit.value = true; isEdit.value = true;
dialogTitle.value = "编辑用户"; dialogVisible.value = true;
editIndex.value = index; dialogTitle.value = "编辑组织";
const selectID = {
id: selectedNode.value.id,
};
proxy.$post({ proxy.$post({
url: "/api/user/manage/getUserInfo", url: "/api/user/depart/getDepart",
data: { id: row.id }, data: selectID,
callback: (data) => { callback: (data) => {
userForm.value = { ...data }; originForm.value = { ...data };
currentID.value = data.id; currentID.value = data.id;
}, },
error: (err) => { error: (err) => {
ElMessage.error("编辑失败:", err); ElMessage.error("编辑失败:", err);
}, },
}); });
dialogVisible.value = true; }
}; };
// 组织删除
// 删除 const handleDeleteOrigin = () => {
const handleDelete = async (row, index) => { if (selectedNode.value === null) {
try { ElMessage.warning("请选择要删除的组织");
await ElMessageBox.confirm(`确定要删除用户"${row.name}"吗?`, "提示", { } else {
confirmButtonText: "确定", const deleteItem = {
cancelButtonText: "取消", id: selectedNode.value.value,
type: "warning", name: selectedNode.value.label,
}); parentId: selectedNode.value.parentId
? selectedNode.value.parentId
: null,
};
proxy.$post({ proxy.$post({
url: "/api/user/manage/deleteUser", url: "/api/user/depart/deleteDepart",
data: { id: row.id }, data: deleteItem,
callback: (data) => { callback: (data) => {
dialogVisible.value = false; handleTreeData();
loadTableData();
ElMessage.success("删除成功"); ElMessage.success("删除成功");
}, },
error: (err) => { error: (err) => {
ElMessage.error("删除失败:", err); ElMessage.error("删除失败", err);
}, },
}); });
loadTableData(); }
} catch {}
}; };
const handleFormSubmit = (formData) => { const handleFormSubmit = (formData) => {
if (isEdit.value) { if (isEdit.value) {
// 编辑用户 // 编辑组织
const updateUser = { const updateOriForm = {
...formData, ...formData,
departs: Array.isArray(formData.departs) ? formData.departs : [], parentId: formData.parentId ? formData.parentId : null,
positions: Array.isArray(formData.positions) ? formData.positions : [],
roles: Array.isArray(formData.roles) ? formData.roles : [],
id: currentID.value, id: currentID.value,
}; };
proxy.$post({ proxy.$post({
url: "/api/user/manage/updateUser", url: "/api/user/depart/updateDepart",
data: updateUser, data: updateOriForm,
callback: (data) => { callback: (data) => {
dialogVisible.value = false; dialogVisible.value = false;
loadTableData(); handleTreeData();
ElMessage.success("用户信息更新成功"); ElMessage.success("组织更新成功");
}, },
error: (err) => { error: (err) => {
ElMessage.error("用户信息更新失败:", err); ElMessage.error("组织更新失败", err);
}, },
}); });
} else { } else {
// 新增用户 // 新增组织
const newUser = { const addOroginForm = {
...formData, ...formData,
departs: Array.isArray(formData.departs) ? formData.departs : [], parentId: formData.parentId ? formData.parentId : null,
positions: Array.isArray(formData.positions) ? formData.positions : [],
roles: Array.isArray(formData.roles) ? formData.roles : [],
}; };
proxy.$post({ proxy.$post({
url: "/api/user/manage/createUser", url: "/api/user/depart/createDepart",
data: newUser, data: addOroginForm,
callback: (data) => { callback: (data) => {
dialogVisible.value = false; dialogVisible.value = false;
loadTableData(); handleTreeData();
ElMessage.success("用户添加成功"); ElMessage.success("组织添加成功");
}, },
error: (err) => { error: (err) => {
ElMessage.error("用户添加失败:", err); ElMessage.error("组织添加失败:", err);
}, },
}); });
} }
...@@ -457,38 +361,43 @@ const handleFormReset = () => { ...@@ -457,38 +361,43 @@ const handleFormReset = () => {
const handleDialogClose = () => { const handleDialogClose = () => {
dialogVisible.value = false; dialogVisible.value = false;
}; };
const handleReset = () => {
userName.value = "";
currentPage.value = 1;
handleUserData();
};
// 处理状态切换 // 树形数据
const handleStatusChange = (newValue, row) => { const handleTreeData = () => {
const newEnableValue = newValue ? "0" : "1"; loading.value = true;
proxy.$post({ proxy.$post({
url: "/api/user/manage/updateUser", url: "/api/user/depart/treeDepart",
data: { data: {
id: row.id, page: currentPage.value,
enable: newEnableValue, pageSize: pageSize.value,
}, },
callback: (data) => { callback: (data) => {
row.enable = newEnableValue; treeData.value = convertToTreeData(data, "id");
loadTableData(); originTreeData.value = convertToTreeData(data, "value");
ElMessage.success( selectedNode.value = data[0].id;
`用户状态已${newEnableValue === "0" ? "启用" : "停用"}` loading.value = false;
);
}, },
error: (err) => { error: (err) => {
ElMessage.error("状态更新失败"); loading.value = false;
ElMessage.error("加载数据失败");
}, },
}); });
}; };
// 人员信息
// 表格数据 const handleUserData = () => {
const loadTableData = () => { userLoading.value = true;
loading.value = true;
proxy.$post({ proxy.$post({
url: "/api/user/manage/listUser", url: "/api/user/depart/getDepartUsers",
data: { data: {
page: currentPage.value, page: currentPage.value,
pageSize: pageSize.value, pageSize: pageSize.value,
departId: currentID.value,
name: userName.value,
}, },
callback: (data) => { callback: (data) => {
tableData.value = data.rows.map((item) => { tableData.value = data.rows.map((item) => {
...@@ -498,20 +407,17 @@ const loadTableData = () => { ...@@ -498,20 +407,17 @@ const loadTableData = () => {
return item; return item;
}); });
total.value = data.count; total.value = data.count;
loading.value = false; userLoading.value = false;
}, },
error: (err) => { error: (err) => {
loading.value = false; userLoading.value = false;
ElMessage.error("加载数据失败"); ElMessage.error("加载数据失败");
}, },
}); });
}; };
onMounted(() => { onMounted(() => {
// loadTableData(); handleTreeData();
// loadDepartmentData();
// loadPositionsData();
// loadRolesData();
}); });
</script> </script>
...@@ -540,6 +446,27 @@ onMounted(() => { ...@@ -540,6 +446,27 @@ onMounted(() => {
cursor: pointer; cursor: pointer;
} }
} }
.search-contain {
display: flex;
justify-content: space-between;
.el-input {
width: 255px;
}
}
.filterRow {
display: inline-block;
cursor: pointer;
display: flex;
justify-content: end;
.icon-gedian {
color: rgba(37, 97, 239, 1);
font-size: 20px;
}
}
.tree-content {
margin-top: 20px;
position: relative;
}
.table-container { .table-container {
flex: 1; flex: 1;
...@@ -552,4 +479,29 @@ onMounted(() => { ...@@ -552,4 +479,29 @@ onMounted(() => {
.el-input__wrapper { .el-input__wrapper {
height: 35px; height: 35px;
} }
// 树节点选中样式
:deep(.el-tree-node__content) {
&:hover {
background-color: #f5f7fa;
}
}
:deep(.el-tree-node.is-current > .el-tree-node__content) {
background-color: rgba(37, 97, 239, 1);
color: #fff;
height: 40px;
border-radius: 3px;
.el-tree-node__label {
color: white !important;
}
.el-icon {
color: white !important;
}
}
// :deep(.el-tree-node > .el-tree-node__children) {
// background-color: rgba(37, 97, 239, 1) !important;
// }
</style> </style>
...@@ -279,7 +279,7 @@ const formItems = computed(() => [ ...@@ -279,7 +279,7 @@ const formItems = computed(() => [
filterable: true, filterable: true,
checkStrictly: true, checkStrictly: true,
renderAfterExpand: false, renderAfterExpand: false,
showCheckbox: true, showCheckbox: false,
multiple: true, multiple: true,
collapseTags: true, collapseTags: true,
maxCollapseTags: 2, maxCollapseTags: 2,
...@@ -295,7 +295,7 @@ const formItems = computed(() => [ ...@@ -295,7 +295,7 @@ const formItems = computed(() => [
filterable: true, filterable: true,
checkStrictly: true, checkStrictly: true,
renderAfterExpand: false, renderAfterExpand: false,
showCheckbox: true, showCheckbox: false,
multiple: true, multiple: true,
collapseTags: true, collapseTags: true,
maxCollapseTags: 2, maxCollapseTags: 2,
...@@ -311,7 +311,7 @@ const formItems = computed(() => [ ...@@ -311,7 +311,7 @@ const formItems = computed(() => [
filterable: true, filterable: true,
checkStrictly: true, checkStrictly: true,
renderAfterExpand: false, renderAfterExpand: false,
showCheckbox: true, showCheckbox: false,
multiple: true, multiple: true,
collapseTags: true, collapseTags: true,
maxCollapseTags: 2, maxCollapseTags: 2,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment