明树Git Lab
Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jt_front
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
jt_front
Commits
eef91c57
Commit
eef91c57
authored
Dec 02, 2025
by
chenron
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
部门管理添加
parent
502ba659
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
491 additions
and
794 deletions
+491
-794
iconfont.css
public/iconFont/iconfont.css
+23
-4
iconfont.js
public/iconFont/iconfont.js
+1
-1
progress.png
src/assets/images/progress.png
+0
-0
CommonComplete.vue
src/components/CommonComplete.vue
+12
-12
CommonPlant.vue
src/components/CommonPlant.vue
+3
-6
CommonTotal.vue
src/components/CommonTotal.vue
+8
-13
manage.less
src/styles/manage.less
+1
-1
Construct.vue
src/views/homePage/components/Construct.vue
+22
-24
Operation.vue
src/views/homePage/components/Operation.vue
+24
-26
ProjectApproval.vue
src/views/homePage/components/ProjectApproval.vue
+32
-53
copy.vue
src/views/homePage/copy.vue
+97
-356
index.vue
src/views/homePage/index.vue
+46
-28
departManage.vue
src/views/systemManage/departManage.vue
+219
-267
userManage.vue
src/views/systemManage/userManage.vue
+3
-3
No files found.
public/iconFont/iconfont.css
View file @
eef91c57
@font-face
{
font-family
:
"iconfont"
;
/* Project id 5072634 */
src
:
url('
iconfont.woff2?t=1764314450961
')
format
(
'woff2'
),
url('
iconfont.woff?t=1764314450961
')
format
(
'woff'
),
url('
iconfont.ttf?t=1764314450961
')
format
(
'truetype'
);
src
:
url('
//at.alicdn.com/t/c/font_5072634_8c3g9zvwtne.woff2?t=1764660705764
')
format
(
'woff2'
),
url('
//at.alicdn.com/t/c/font_5072634_8c3g9zvwtne.woff?t=1764660705764
')
format
(
'woff'
),
url('
//at.alicdn.com/t/c/font_5072634_8c3g9zvwtne.ttf?t=1764660705764
')
format
(
'truetype'
);
}
.iconfont
{
...
...
@@ -13,6 +13,26 @@
-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
{
content
:
"\e605"
;
}
...
...
@@ -20,4 +40,3 @@
.icon-quanping
:before
{
content
:
"\e62f"
;
}
public/iconFont/iconfont.js
View file @
eef91c57
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
);
\ No newline at end of file
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
src/assets/images/
Frame 10872
.png
→
src/assets/images/
progress
.png
View file @
eef91c57
File moved
src/components/CommonComplete.vue
View file @
eef91c57
...
...
@@ -57,39 +57,39 @@ const props = defineProps({
justify-content: center;
align-items: center;
.stat-card {
.vw(width, 1
7
0);
.vw(width, 1
8
0);
.vw(border-radius, 5);
.vw(margin-right,10);
.vw(padding, 10);
.vw(margin,10);
background-image: url("@/assets/images/complete.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
.stat-card-title {
.font(12);
text-align: center;
}
.stat-card-value {
.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-bottom {
display: flex;
justify-content: space-between;
p {
.vh(height,10);
}
.pro-title {
.font(10);
margin-bottom: -12px;
}
.pro-value {
.font(1
8
);
.font(1
6
);
font-family: "DIN";
color: @Color;
font-weight: 100;
span {
.font(10);
font-weight: 400;
//
font-weight: 400;
}
}
}
...
...
@@ -110,7 +110,7 @@ const props = defineProps({
}
.trend-icon {
font-size: 12px
;
.font(12)
;
color: #32d25a;
&.down-trend {
...
...
src/components/CommonPlant.vue
View file @
eef91c57
...
...
@@ -30,20 +30,18 @@ const props = defineProps({
.stat-cards-container {
display: flex;
.stat-card {
.vw(width, 1
7
0);
.vw(width, 1
8
0);
.vw(border-radius, 5);
.vw(padding, 10);
.vw(margin,10);
.vw(margin
-right
,10);
background-image: url("@/assets/images/total2.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
.stat-card-title {
.font(12);
font-weight: 700;
}
.stat-card-value {
.font(
20
);
.font(
18
);
color: @Color;
font-family: "DIN";
}
...
...
@@ -52,7 +50,6 @@ const props = defineProps({
justify-content: space-between;
.amount-title {
.font(10);
.vh(height, 5);
}
.amount-value {
.font(16);
...
...
src/components/CommonTotal.vue
View file @
eef91c57
...
...
@@ -133,33 +133,28 @@ onMounted(() => {});
<
style
scoped
lang=
"less"
>
.common-container {
display: flex;
justify-content: center;
.stat-cards-container {
display: flex;
.stat-card {
.vw(width, 1
7
0);
.vw(width, 1
8
0);
.vw(border-radius, 5);
.vw(padding,
2
0);
// .vw(margin
,10);
.vw(padding,
1
0);
.vw(margin-right
,10);
background-image: url("@/assets/images/total2.png");
background-repeat: no-repeat;
background-size: 90% 80%;
background-position: center;
background-size: contain;
.stat-card-title {
.font(12);
}
.stat-card-value {
.font(
24
);
.font(
18
);
cursor: pointer;
font-family: "DIN";
}
}
:last-child {
margin-right: 0;
}
}
}
:deep(.el-dialog__title) {
font-weight: 900;
}
:deep(.el-dialog) {
}
</
style
>
src/styles/manage.less
View file @
eef91c57
.tab-content{
th{
text-align: center;
//
text-align: center;
}
}
\ No newline at end of file
src/views/homePage/components/Construct.vue
View file @
eef91c57
...
...
@@ -11,16 +11,12 @@
<div
class=
"construct-right"
>
<div>
<div
class=
"info-title"
>
项目概况
</div>
<div>
<CommonTotal
:numberList=
"projectList"
/>
</div>
</div>
<div>
<div
class=
"info-title"
>
投资完成情况
</div>
<div>
<CommonTotal
:numberList=
"investmentList"
/>
</div>
</div>
<div>
<div
class=
"info-title"
>
投资完成情况
</div>
<div
class=
"progress"
>
...
...
@@ -39,31 +35,35 @@
</div>
<div>
<div
class=
"info-title"
>
投资回收完成情况
</div>
<div>
<CommonTotal
:numberList=
"recycleList"
/>
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
>
import
Map
from
"@/components/CommonMap.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"
;
const
projectList
=
reactive
([
// 定义组件名称,用于keep-alive缓存
defineOptions
({
name
:
'Construct'
});
// 使用shallowReactive减少深层响应式开销
const
projectList
=
shallowReactive
([
{
title
:
"总个数(个)"
,
value
:
"59"
},
{
title
:
"总投资(亿元)"
,
value
:
"2733.35"
},
{
title
:
"总规模(公里)"
,
value
:
"5116.72"
},
]);
const
investmentList
=
r
eactive
([
const
investmentList
=
shallowR
eactive
([
{
title
:
"10月完成投资(亿元)"
,
value
:
"0"
},
{
title
:
"2025年完成投资(亿元)"
,
value
:
"19.725"
},
{
title
:
"累计完成投资(亿元)"
,
value
:
"807.56"
},
]);
// recycleList
const
recycleList
=
r
eactive
([
const
recycleList
=
shallowR
eactive
([
{
title
:
"10月完成投资(亿元)"
,
value
:
"0"
},
{
title
:
"2025年完成投资(亿元)"
,
value
:
"2.98"
},
{
title
:
"完成率"
,
value
:
"50.2%"
},
...
...
@@ -74,7 +74,7 @@ const recycleList = reactive([
.construct-container {
display: flex;
.construct-left {
flex: 1
;
width: 100%
;
height: 100%;
.tag-image {
img {
...
...
@@ -94,11 +94,12 @@ const recycleList = reactive([
.vw(width, 180);
.vh(height, 30);
.vh(line-height, 30);
.
vw(margin-left,20
);
.
font(
20);
.v
w(padding-left,23
);
.
font(16
);
.
vw(padding-left,
20);
.v
h(margin-bottom,15
);
text-align: left;
color: #fff;
white-space: nowrap;
font-family: YouSheBiaoTiYuan;
background-image: url("@/assets/images/tag.png");
background-repeat: no-repeat;
...
...
@@ -132,24 +133,21 @@ const recycleList = reactive([
.vw(left,65);
width: 85%;
height: 30%;
background-image: url(
/src/assets/images/baseRate.png
);
background-image: url(
"@/assets/images/baseRate.png"
);
background-size: 100% 100%;
background-repeat: no-repeat;
}
.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;
.vh(top,26);
.vw(left,65);
height: 30%;
max-width: 83%;
height: 30%;
background-image: url(/src/assets/images/progress.png);
background-size: 100% 100%;
background-repeat: no-repeat;
-webkit-transform: skew(-30deg);
transform: skew(-30deg);
}
}
}
...
...
src/views/homePage/components/Operation.vue
View file @
eef91c57
...
...
@@ -11,36 +11,26 @@
<div
class=
"construct-right"
>
<div>
<div
class=
"info-title"
>
项目概况
</div>
<div>
<CommonTotal
:numberList=
"projectList"
/>
</div>
</div>
<div>
<div
class=
"info-title"
>
投资完成情况
</div>
<div>
<CommonTotal
:numberList=
"investmentList"
/>
</div>
</div>
<div>
<div
class=
"info-title"
>
经营计划完成情况
</div>
<div>
<CommonComplete
:numberList=
"completeList"
/>
</div>
</div>
<div>
<div
class=
"info-title"
>
投资回收完成情况
</div>
<div>
<CommonTotal
:numberList=
"recycleList"
/>
</div>
</div>
<div>
<div
class=
"info-title"
>
2025年经营计划完成进度
</div>
<div>
<CommonPlant
:numberList=
"operationList"
/>
</div>
</div>
</div>
</div>
</
template
>
<
script
setup
>
...
...
@@ -48,28 +38,34 @@ import Map from "@/components/CommonMap.vue";
import
CommonTotal
from
"@/components/CommonTotal.vue"
;
import
CommonComplete
from
"@/components/CommonComplete.vue"
;
import
CommonPlant
from
"@/components/CommonPlant.vue"
;
import
{
reactive
,
ref
,
onMounted
,
onUnmounted
}
from
"vue"
;
const
projectList
=
reactive
([
import
{
shallowReactive
,
ref
,
onMounted
,
onUnmounted
,
defineOptions
}
from
"vue"
;
// 定义组件名称,用于keep-alive缓存
defineOptions
({
name
:
'Operation'
});
// 使用shallowReactive减少深层响应式开销
const
projectList
=
shallowReactive
([
{
title
:
"总个数(个)"
,
value
:
"59"
},
{
title
:
"总投资(亿元)"
,
value
:
"2733.35"
},
{
title
:
"总规模(公里)"
,
value
:
"5116.72"
},
]);
const
investmentList
=
r
eactive
([
const
investmentList
=
shallowR
eactive
([
{
title
:
"10月完成投资(亿元)"
,
value
:
"0"
},
{
title
:
"2025年完成投资(亿元)"
,
value
:
"19.725"
},
{
title
:
"累计完成投资(亿元)"
,
value
:
"807.56"
},
]);
const
recycleList
=
r
eactive
([
const
recycleList
=
shallowR
eactive
([
{
title
:
"10月完成投资(亿元)"
,
value
:
"0"
},
{
title
:
"2025年完成投资(亿元)"
,
value
:
"2.98"
},
{
title
:
"完成率"
,
value
:
"50.2%"
},
]);
const
operationList
=
r
eactive
([
const
operationList
=
shallowR
eactive
([
{
title
:
"营业收入完成进度"
,
value
:
"68.1%"
},
{
title
:
"运营成本完成进度"
,
value
:
"68.1%"
},
{
title
:
"利润总额完成进度"
,
value
:
"68.1%"
},
]);
const
completeList
=
r
eactive
([
const
completeList
=
shallowR
eactive
([
{
title
:
"营业收入"
,
protitle
:
"11月完成"
,
...
...
@@ -101,7 +97,7 @@ const completeList = reactive([
.construct-container {
display: flex;
.construct-left {
flex: 1
;
width: 100%
;
height: 100%;
.tag-image {
img {
...
...
@@ -118,14 +114,16 @@ const completeList = reactive([
flex-direction: column;
justify-content: space-between;
.info-title {
.vw(width,
23
0);
.vw(width,
18
0);
.vh(height, 30);
.vh(line-height, 30);
.vw(margin-left,10);
.font(20);
.vw(padding-left,28);
.font(16);
.vw(padding-left,20);
.vh(margin-bottom,15);
.vh(margin-top,15);
text-align: left;
color: #fff;
white-space: nowrap;
font-family: YouSheBiaoTiYuan;
background-image: url("@/assets/images/tag.png");
background-repeat: no-repeat;
...
...
src/views/homePage/components/ProjectApproval.vue
View file @
eef91c57
<
template
>
<div
class=
"project-container"
>
<CommonTotal
:numberList=
"initiationList"
/>
</div>
<Map
/>
</
template
>
<
script
setup
>
import
CommonTotal
from
"@/components/CommonTotal.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
({
currentName
:
{
...
...
@@ -14,64 +21,36 @@ const props = defineProps({
default
:
"立项"
,
},
});
const
initiationList
=
ref
([]);
// 根据currentName设置不同的数据
const
setNumberList
=
(
name
)
=>
{
if
(
name
===
"立项"
)
{
initiationList
.
value
=
[
// 使用静态数据避免重复创建
const
STATIC_DATA
=
{
立项
:
[
{
title
:
"总个数(个)"
,
value
:
"59"
},
{
title
:
"总投资(亿元)"
,
value
:
"2733.35"
},
{
title
:
"总规模(公里)"
,
value
:
"5116.72"
},
],
转让
:
[
{
title
:
"总个数(个)"
,
value
:
"59"
},
{
title
:
"总投资(亿元)"
,
value
:
"2733.35"
},
{
title
:
"总规模(公里)"
,
value
:
"5116.72"
},
];
}
else
{
initiationList
.
value
=
[
{
title
:
"累计完成投资(亿元)"
,
value
:
"3116.44"
},
],
退出
:
[
{
title
:
"总个数(个)"
,
value
:
"59"
},
{
title
:
"总投资(亿元)"
,
value
:
"2733.35"
},
{
title
:
"总规模(公里)"
,
value
:
"5116.72"
},
{
title
:
"累计完成投资(亿元)"
,
value
:
"3116.44"
},
];
}
],
};
watch
(
()
=>
props
.
currentName
,
(
newVal
)
=>
{
setNumberList
(
newVal
);
},
{
immediate
:
true
,
deep
:
true
,
}
);
onMounted
(()
=>
{
setNumberList
(
props
.
currentName
);
// 使用computed进行响应式计算,避免不必要的重新渲染
const
initiationList
=
computed
(()
=>
{
return
STATIC_DATA
[
props
.
currentName
]
||
STATIC_DATA
.
立项
;
});
</
script
>
<
style
scoped
lang=
"less"
>
.
stat-cards
-container {
.
project
-container {
display: flex;
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
>
src/views/homePage/copy.vue
View file @
eef91c57
<
template
>
<div
class=
"home-container"
>
<div
class=
"header"
>
<div
class=
"header-left"
>
<div
class=
"nav-btn"
:class=
"
{ active: selectedLeftBtn === 'equity' }"
@click="selectLeftBtn('equity')"
>
股权投资
<div
class=
"construct-container"
>
<div
class=
"construct-left"
>
<div
class=
"tag-image"
>
<img
src=
"@/assets/images/建设.png"
/>
</div>
<div
class=
"nav-btn"
:class=
"
{ active: selectedLeftBtn === 'risk' }"
@click="selectLeftBtn('risk')"
>
固定风险投资
<div
class=
"construct-left-map"
>
<Map
/>
</div>
</div>
<div
class=
"header-middile"
>
葛洲坝集团交通投资有限公司投资决策分析系统
<div
class=
"construct-right"
>
<div>
<div
class=
"info-title"
>
项目概况
</div>
<CommonTotal
:numberList=
"projectList"
/>
</div>
<div
class=
"header-right"
>
<div
class=
"nav-btn"
:class=
"
{ active: selectedRightBtn === 'realestate' }"
@click="selectRightBtn('realestate')"
>
房地产投资
<div>
<div
class=
"info-title"
>
投资完成情况
</div>
<CommonTotal
:numberList=
"investmentList"
/>
</div>
<div
class=
"nav-btn"
:class=
"
{ active: selectedRightBtn === 'finance' }"
@click="selectRightBtn('finance')"
>
融资建设投资
<div>
<div
class=
"info-title"
>
经营计划完成情况
</div>
<CommonComplete
:numberList=
"completeList"
/>
</div>
<div>
<div
class=
"info-title"
>
投资回收完成情况
</div>
<CommonTotal
:numberList=
"recycleList"
/>
</div>
<div>
<div
class=
"info-title"
>
2025年经营计划完成进度
</div>
<CommonPlant
:numberList=
"operationList"
/>
</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
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>
</
template
>
<
script
setup
>
import
Construct
from
"../components/Construct.vue"
;
import
ProjectApproval
from
"../components/ProjectApproval.vue"
;
import
Operation
from
"../components/Operation.vue"
;
import
{
reactive
,
ref
}
from
"vue"
;
const
selectedLeftBtn
=
ref
(
"equity"
);
const
selectedRightBtn
=
ref
(
""
);
const
selectedContentBtn
=
ref
(
1
);
const
selectedContentName
=
ref
(
"立项"
);
const
isFullscreen
=
ref
(
false
);
const
showPopup
=
ref
(
false
);
const
currentProject
=
ref
({});
const
popupStyle
=
ref
({});
const
navList
=
reactive
([
{
index
:
1
,
name
:
"立项"
,
},
{
index
:
2
,
name
:
"建设"
,
},
import
Map
from
"@/components/CommonMap.vue"
;
import
CommonTotal
from
"@/components/CommonTotal.vue"
;
import
CommonComplete
from
"@/components/CommonComplete.vue"
;
import
CommonPlant
from
"@/components/CommonPlant.vue"
;
import
{
reactive
,
ref
,
onMounted
,
onUnmounted
}
from
"vue"
;
const
projectList
=
reactive
([
{
title
:
"总个数(个)"
,
value
:
"59"
},
{
title
:
"总投资(亿元)"
,
value
:
"2733.35"
},
{
title
:
"总规模(公里)"
,
value
:
"5116.72"
},
]);
const
investmentList
=
reactive
([
{
title
:
"10月完成投资(亿元)"
,
value
:
"0"
},
{
title
:
"2025年完成投资(亿元)"
,
value
:
"19.725"
},
{
title
:
"累计完成投资(亿元)"
,
value
:
"807.56"
},
]);
const
recycleList
=
reactive
([
{
title
:
"10月完成投资(亿元)"
,
value
:
"0"
},
{
title
:
"2025年完成投资(亿元)"
,
value
:
"2.98"
},
{
title
:
"完成率"
,
value
:
"50.2%"
},
]);
const
operationList
=
reactive
([
{
title
:
"营业收入完成进度"
,
value
:
"68.1%"
},
{
title
:
"运营成本完成进度"
,
value
:
"68.1%"
},
{
title
:
"利润总额完成进度"
,
value
:
"68.1%"
},
]);
const
completeList
=
reactive
([
{
index
:
3
,
name
:
"运营"
,
title
:
"营业收入"
,
protitle
:
"11月完成"
,
proValue
:
"1.1"
,
thirdTtile
:
"第三季度完成"
,
thirdValue
:
"4.21"
,
compareValue
:
"+1.2%"
,
},
{
index
:
4
,
name
:
"转让"
,
title
:
"运营成本"
,
protitle
:
"11月完成"
,
proValue
:
"47.98"
,
thirdTtile
:
"第三季度完成"
,
thirdValue
:
"5.75"
,
compareValue
:
"-0.5%"
,
},
{
index
:
5
,
name
:
"退出"
,
title
:
"利润总额"
,
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
>
<
style
scoped
lang=
"less"
>
.home-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;
.construct-container {
display: flex;
flex-direction: column;
justify-content: space-between;
.header {
.construct-left {
width: 100%;
.vh(height, 80);
display: flex;
justify-content: center;
align-items: flex-end;
.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;
height: 100%;
.tag-image {
img {
.vw(width, 85);
}
.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;
justify-content: center;
align-items: center;
.nav-btn {
flex-direction: column;
justify-content: space-between;
.info-title {
.vw(width, 180);
.vh(height, 30);
.vh(line-height, 30);
.font(16);
cursor: pointer;
.vw(padding-left,20);
.vh(margin-bottom,15);
.vh(margin-top,15);
text-align: left;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
font-family: YouSheBiaoTiYuan;
&:not(.active) {
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-image: url("@/assets/images/tag.png");
background-repeat: no-repeat;
background-size: cover;
background-position: center;
width: 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;
background-size: 100% 100%;
}
}
}
...
...
src/views/homePage/index.vue
View file @
eef91c57
...
...
@@ -55,6 +55,7 @@
>
{{
item
.
name
}}
</span>
</div>
<keep-alive
include=
"ProjectApproval,Construct,Operation"
>
<ProjectApproval
:currentName=
"selectedContentName"
v-if=
"
...
...
@@ -65,6 +66,7 @@
/>
<Construct
v-else-if=
"selectedContentBtn === 2"
/>
<Operation
v-else
/>
</keep-alive>
</div>
<div
class=
"bottom"
></div>
</div>
...
...
@@ -74,7 +76,7 @@
import
Construct
from
"./components/Construct.vue"
;
import
ProjectApproval
from
"./components/ProjectApproval.vue"
;
import
Operation
from
"./components/Operation.vue"
;
import
{
reactive
,
ref
}
from
"vue"
;
import
{
reactive
,
ref
,
nextTick
}
from
"vue"
;
const
selectedLeftBtn
=
ref
(
"equity"
);
const
selectedRightBtn
=
ref
(
""
);
...
...
@@ -118,27 +120,43 @@ const selectRightBtn = (btn) => {
selectedLeftBtn
.
value
=
""
;
};
// 添加防抖功能,避免频繁切换造成的性能问题
let
debounceTimer
=
null
;
const
selectContentBtn
=
(
item
)
=>
{
// 清除之前的定时器
if
(
debounceTimer
)
{
clearTimeout
(
debounceTimer
);
}
// 使用防抖延迟切换
debounceTimer
=
setTimeout
(()
=>
{
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
;
// 使用nextTick确保DOM更新完成
nextTick
(()
=>
{
// 可以在这里添加其他需要延迟执行的逻辑
});
},
100
);
// 100ms防抖延迟
};
// 暂时注释未使用的函数,避免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
homeContainer
=
document
.
querySelector
(
".home-container"
);
...
...
src/views/systemManage/departManage.vue
View file @
eef91c57
...
...
@@ -10,19 +10,47 @@
<el-icon><search
/></el-icon>
</
template
>
</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
class=
"table-container"
>
<div
class=
"origin-title"
>
<h3>
部门管理
</h3>
<el-button
type=
"primary"
@
click=
"handleAdd"
>
新增
</el-button>
</div>
<commonForm
v-model=
"searchForm"
:config=
"searchConfig"
:items=
"searchItems"
@
submit=
"handleSearch"
@
reset=
"handleReset"
/>
<common-table
:tableHeight=
"tableHeight"
:data=
"tableData"
...
...
@@ -35,29 +63,10 @@
@
size-change=
"handleSizeChange"
@
current-page-change=
"handleCurrentPageChange"
>
<!-- <template #header-actions>
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增
</el-button>
</template> -->
<
template
#
enable=
"{ row }"
>
<el-switch
:model-value=
"row.enable === 0 ? true : false"
@
change=
"handleStatusChange($event, row)"
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>
<el-tag
:type=
"row.enable === 0 ? 'success' : 'danger'"
>
{{
row
.
enable
===
"0"
?
"启用"
:
"停用"
}}
</el-tag>
</
template
>
</common-table>
</div>
...
...
@@ -65,11 +74,11 @@
<el-dialog
v-model=
"dialogVisible"
:title=
"dialogTitle"
width=
"
6
00px"
width=
"
4
00px"
@
close=
"handleDialogClose"
>
<commonForm
v-model=
"
user
Form"
v-model=
"
origin
Form"
:config=
"formConfig"
:items=
"formItems"
:rules=
"formRules"
...
...
@@ -88,6 +97,11 @@ import CommonTable from "@/components/common/commonTable.vue";
const
{
proxy
}
=
getCurrentInstance
();
const
loading
=
ref
(
false
);
const
userLoading
=
ref
(
false
);
const
treeData
=
ref
([]);
const
originTreeData
=
ref
([]);
const
selectedNode
=
ref
(
null
);
const
userName
=
ref
(
""
);
// 计算表格高度
const
tableHeight
=
computed
(()
=>
{
...
...
@@ -95,20 +109,28 @@ const tableHeight = computed(() => {
const
paginationHeight
=
50
;
const
rowHeight
=
40
;
const
baseHeight
=
headerHeight
+
paginationHeight
;
// 1.如果数据超过10条,固定显示10行的高度 + 滚动条;2.如果数据不超过10条,按实际行数计算高度
const
maxRows
=
Math
.
min
(
tableData
.
value
.
length
,
10
);
const
contentHeight
=
maxRows
*
rowHeight
;
return
`
${
baseHeight
+
contentHeight
}
px`
;
});
// 数据转换函数
const
convertToTreeData
=
(
apiData
)
=>
{
const
convertToTreeData
=
(
apiData
,
type
)
=>
{
if
(
type
===
"id"
)
{
return
apiData
.
map
((
item
)
=>
({
value
:
item
.
id
.
toString
()
,
id
:
item
.
id
,
label
:
item
.
name
,
parentId
:
item
.
parentId
,
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 = [
slot
:
"enable"
,
align
:
"center"
,
},
{
prop
:
"operations"
,
label
:
"操作"
,
width
:
160
,
slot
:
"operations"
,
fixed
:
"right"
,
align
:
"center"
,
},
];
// 对话框相关
const
dialogVisible
=
ref
(
false
);
const
dialogTitle
=
ref
(
"新增用户"
);
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
user
Form
=
ref
({
const
origin
Form
=
ref
({
name
:
""
,
departs
:
[],
positions
:
[],
roles
:
[],
enable
:
"0"
,
parentId
:
""
,
});
// 用户表单配置
...
...
@@ -213,121 +202,33 @@ const formConfig = {
submitText
:
"保存"
,
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
(()
=>
[
{
type
:
"input"
,
prop
:
"name"
,
label
:
"用户姓名"
,
placeholder
:
"请输入用户姓名"
,
span
:
12
,
},
{
type
:
"tree"
,
prop
:
"
departs
"
,
label
:
"
所属部门
"
,
placeholder
:
"请选择
部门
"
,
data
:
department
Data
.
value
,
prop
:
"
parentId
"
,
label
:
"
上级组织
"
,
placeholder
:
"请选择
上级组织
"
,
data
:
originTree
Data
.
value
,
clearable
:
true
,
filterable
:
true
,
checkStrictly
:
true
,
renderAfterExpand
:
false
,
showCheckbox
:
true
,
multiple
:
true
,
collapseTags
:
true
,
maxCollapseTags
:
2
,
span
:
12
,
},
{
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
,
showCheckbox
:
false
,
multiple
:
false
,
span
:
24
,
// required: true,
// rules: [{ required: true, message: "请选择上级组织", trigger: "blur" }],
},
{
type
:
"input"
,
prop
:
"mobile"
,
label
:
"手机号码"
,
placeholder
:
"请输入手机号码"
,
span
:
12
,
},
{
type
:
"radio"
,
prop
:
"enable"
,
label
:
"状态"
,
span
:
12
,
options
:
[
{
label
:
"启用"
,
value
:
"0"
},
{
label
:
"停用"
,
value
:
"1"
},
],
prop
:
"name"
,
label
:
"组织名称:"
,
placeholder
:
"请输入组织名称"
,
required
:
true
,
rules
:
[{
required
:
true
,
message
:
"请输入组织名称"
,
trigger
:
"blur"
}],
span
:
24
,
},
]);
...
...
@@ -348,103 +249,106 @@ const handleCurrentPageChange = (page) => {
// 新增用户
const
handleAdd
=
()
=>
{
isEdit
.
value
=
false
;
dialogTitle
.
value
=
"新增用户"
;
userForm
.
value
=
{
dialogTitle
.
value
=
"新增组织"
;
originForm
.
value
=
{
parentId
:
""
,
name
:
""
,
departs
:
[],
positions
:
[],
roles
:
[],
enable
:
"0"
,
};
loadDepartmentData
();
loadPositionsData
();
loadRolesData
();
dialogVisible
.
value
=
true
;
};
let
currentID
=
ref
();
// 编辑
const
handleEdit
=
(
row
,
index
)
=>
{
let
currentID
=
ref
(
""
);
// 处理树节点点击事件
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
;
dialogTitle
.
value
=
"编辑用户"
;
editIndex
.
value
=
index
;
dialogVisible
.
value
=
true
;
dialogTitle
.
value
=
"编辑组织"
;
const
selectID
=
{
id
:
selectedNode
.
value
.
id
,
};
proxy
.
$post
({
url
:
"/api/user/manage/getUserInfo
"
,
data
:
{
id
:
row
.
id
}
,
url
:
"/api/user/depart/getDepart
"
,
data
:
selectID
,
callback
:
(
data
)
=>
{
user
Form
.
value
=
{
...
data
};
origin
Form
.
value
=
{
...
data
};
currentID
.
value
=
data
.
id
;
},
error
:
(
err
)
=>
{
ElMessage
.
error
(
"编辑失败:"
,
err
);
},
});
dialogVisible
.
value
=
true
;
}
};
// 删除
const
handleDelete
=
async
(
row
,
index
)
=>
{
try
{
await
ElMessageBox
.
confirm
(
`确定要删除用户"
${
row
.
name
}
"吗?`
,
"提示"
,
{
confirmButtonText
:
"确定"
,
cancelButtonText
:
"取消"
,
type
:
"warning"
,
});
// 组织删除
const
handleDeleteOrigin
=
()
=>
{
if
(
selectedNode
.
value
===
null
)
{
ElMessage
.
warning
(
"请选择要删除的组织"
);
}
else
{
const
deleteItem
=
{
id
:
selectedNode
.
value
.
value
,
name
:
selectedNode
.
value
.
label
,
parentId
:
selectedNode
.
value
.
parentId
?
selectedNode
.
value
.
parentId
:
null
,
};
proxy
.
$post
({
url
:
"/api/user/
manage/deleteUser
"
,
data
:
{
id
:
row
.
id
}
,
url
:
"/api/user/
depart/deleteDepart
"
,
data
:
deleteItem
,
callback
:
(
data
)
=>
{
dialogVisible
.
value
=
false
;
loadTableData
();
handleTreeData
();
ElMessage
.
success
(
"删除成功"
);
},
error
:
(
err
)
=>
{
ElMessage
.
error
(
"删除失败
:
"
,
err
);
ElMessage
.
error
(
"删除失败"
,
err
);
},
});
loadTableData
();
}
catch
{}
}
};
const
handleFormSubmit
=
(
formData
)
=>
{
if
(
isEdit
.
value
)
{
// 编辑
用户
const
update
User
=
{
// 编辑
组织
const
update
OriForm
=
{
...
formData
,
departs
:
Array
.
isArray
(
formData
.
departs
)
?
formData
.
departs
:
[],
positions
:
Array
.
isArray
(
formData
.
positions
)
?
formData
.
positions
:
[],
roles
:
Array
.
isArray
(
formData
.
roles
)
?
formData
.
roles
:
[],
parentId
:
formData
.
parentId
?
formData
.
parentId
:
null
,
id
:
currentID
.
value
,
};
proxy
.
$post
({
url
:
"/api/user/
manage/updateUser
"
,
data
:
update
User
,
url
:
"/api/user/
depart/updateDepart
"
,
data
:
update
OriForm
,
callback
:
(
data
)
=>
{
dialogVisible
.
value
=
false
;
loadTabl
eData
();
ElMessage
.
success
(
"
用户信息
更新成功"
);
handleTre
eData
();
ElMessage
.
success
(
"
组织
更新成功"
);
},
error
:
(
err
)
=>
{
ElMessage
.
error
(
"
用户信息更新失败:
"
,
err
);
ElMessage
.
error
(
"
组织更新失败
"
,
err
);
},
});
}
else
{
// 新增
用户
const
newUser
=
{
// 新增
组织
const
addOroginForm
=
{
...
formData
,
departs
:
Array
.
isArray
(
formData
.
departs
)
?
formData
.
departs
:
[],
positions
:
Array
.
isArray
(
formData
.
positions
)
?
formData
.
positions
:
[],
roles
:
Array
.
isArray
(
formData
.
roles
)
?
formData
.
roles
:
[],
parentId
:
formData
.
parentId
?
formData
.
parentId
:
null
,
};
proxy
.
$post
({
url
:
"/api/user/
manage/createUser
"
,
data
:
newUser
,
url
:
"/api/user/
depart/createDepart
"
,
data
:
addOroginForm
,
callback
:
(
data
)
=>
{
dialogVisible
.
value
=
false
;
loadTabl
eData
();
ElMessage
.
success
(
"
用户
添加成功"
);
handleTre
eData
();
ElMessage
.
success
(
"
组织
添加成功"
);
},
error
:
(
err
)
=>
{
ElMessage
.
error
(
"
用户
添加失败:"
,
err
);
ElMessage
.
error
(
"
组织
添加失败:"
,
err
);
},
});
}
...
...
@@ -457,38 +361,43 @@ const handleFormReset = () => {
const
handleDialogClose
=
()
=>
{
dialogVisible
.
value
=
false
;
};
const
handleReset
=
()
=>
{
userName
.
value
=
""
;
currentPage
.
value
=
1
;
handleUserData
();
};
// 处理状态切换
const
handleStatusChange
=
(
newValue
,
row
)
=>
{
const
newEnableValue
=
newValue
?
"0"
:
"1"
;
// 树形数据
const
handleTreeData
=
()
=>
{
loading
.
value
=
true
;
proxy
.
$post
({
url
:
"/api/user/
manage/updateUser
"
,
url
:
"/api/user/
depart/treeDepart
"
,
data
:
{
id
:
row
.
id
,
enable
:
newEnableV
alue
,
page
:
currentPage
.
value
,
pageSize
:
pageSize
.
v
alue
,
},
callback
:
(
data
)
=>
{
row
.
enable
=
newEnableValue
;
loadTableData
();
ElMessage
.
success
(
`用户状态已
${
newEnableValue
===
"0"
?
"启用"
:
"停用"
}
`
);
treeData
.
value
=
convertToTreeData
(
data
,
"id"
);
originTreeData
.
value
=
convertToTreeData
(
data
,
"value"
);
selectedNode
.
value
=
data
[
0
].
id
;
loading
.
value
=
false
;
},
error
:
(
err
)
=>
{
ElMessage
.
error
(
"状态更新失败"
);
loading
.
value
=
false
;
ElMessage
.
error
(
"加载数据失败"
);
},
});
};
// 表格数据
const
loadTableData
=
()
=>
{
loading
.
value
=
true
;
// 人员信息
const
handleUserData
=
()
=>
{
userLoading
.
value
=
true
;
proxy
.
$post
({
url
:
"/api/user/
manage/listUser
"
,
url
:
"/api/user/
depart/getDepartUsers
"
,
data
:
{
page
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
departId
:
currentID
.
value
,
name
:
userName
.
value
,
},
callback
:
(
data
)
=>
{
tableData
.
value
=
data
.
rows
.
map
((
item
)
=>
{
...
...
@@ -498,20 +407,17 @@ const loadTableData = () => {
return
item
;
});
total
.
value
=
data
.
count
;
l
oading
.
value
=
false
;
userL
oading
.
value
=
false
;
},
error
:
(
err
)
=>
{
l
oading
.
value
=
false
;
userL
oading
.
value
=
false
;
ElMessage
.
error
(
"加载数据失败"
);
},
});
};
onMounted
(()
=>
{
// loadTableData();
// loadDepartmentData();
// loadPositionsData();
// loadRolesData();
handleTreeData
();
});
</
script
>
...
...
@@ -540,6 +446,27 @@ onMounted(() => {
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 {
flex: 1;
...
...
@@ -552,4 +479,29 @@ onMounted(() => {
.el-input__wrapper {
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
>
src/views/systemManage/userManage.vue
View file @
eef91c57
...
...
@@ -279,7 +279,7 @@ const formItems = computed(() => [
filterable
:
true
,
checkStrictly
:
true
,
renderAfterExpand
:
false
,
showCheckbox
:
tru
e
,
showCheckbox
:
fals
e
,
multiple
:
true
,
collapseTags
:
true
,
maxCollapseTags
:
2
,
...
...
@@ -295,7 +295,7 @@ const formItems = computed(() => [
filterable
:
true
,
checkStrictly
:
true
,
renderAfterExpand
:
false
,
showCheckbox
:
tru
e
,
showCheckbox
:
fals
e
,
multiple
:
true
,
collapseTags
:
true
,
maxCollapseTags
:
2
,
...
...
@@ -311,7 +311,7 @@ const formItems = computed(() => [
filterable
:
true
,
checkStrictly
:
true
,
renderAfterExpand
:
false
,
showCheckbox
:
tru
e
,
showCheckbox
:
fals
e
,
multiple
:
true
,
collapseTags
:
true
,
maxCollapseTags
:
2
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment