Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
BI管理后台
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
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
刘春
BI管理后台
Commits
dd2396a7
Commit
dd2396a7
authored
Jun 26, 2025
by
孙浩然
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
web后端维护
parent
fc866840
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
502 additions
and
21 deletions
+502
-21
index.ts
src/api/menuApi/index.ts
+12
-4
main.ts
src/main.ts
+1
-0
common.scss
src/styles/common.scss
+151
-0
AddMenu.vue
src/views/web/components/AddMenu.vue
+202
-0
index.vue
src/views/web/index.vue
+136
-17
No files found.
src/api/menuApi/index.ts
View file @
dd2396a7
...
@@ -7,7 +7,15 @@ import type { addMenuDto, delMenuDto, getMenuListDto } from '@/types/BiMenu';
...
@@ -7,7 +7,15 @@ import type { addMenuDto, delMenuDto, getMenuListDto } from '@/types/BiMenu';
* @returns
* @returns
*/
*/
const
addMenu
=
(
data
:
addMenuDto
)
=>
const
addMenu
=
(
data
:
addMenuDto
)
=>
request
(
'
/bi-manager/menu/addMenu
'
,
'
get
'
,
data
);
request
(
'
/bi-manager/menu/addMenu
'
,
'
post
'
,
data
);
/**
* 新增菜单
* @param addMenuDto
* @returns
*/
const
editMenu
=
(
data
:
addMenuDto
)
=>
request
(
'
/bi-manager/menu/editMenu
'
,
'
post
'
,
data
);
/**
/**
* 删除菜单
* 删除菜单
...
@@ -15,7 +23,7 @@ const addMenu = (data: addMenuDto) =>
...
@@ -15,7 +23,7 @@ const addMenu = (data: addMenuDto) =>
* @returns
* @returns
*/
*/
const
delMenu
=
(
data
:
delMenuDto
)
=>
const
delMenu
=
(
data
:
delMenuDto
)
=>
request
(
'
/bi-manager/menu/delMenu
'
,
'
ge
t
'
,
data
);
request
(
'
/bi-manager/menu/delMenu
'
,
'
pos
t
'
,
data
);
/**
/**
* 查询
* 查询
...
@@ -23,6 +31,6 @@ const delMenu = (data: delMenuDto) =>
...
@@ -23,6 +31,6 @@ const delMenu = (data: delMenuDto) =>
* @returns
* @returns
*/
*/
const
getMenuList
=
(
data
:
getMenuListDto
)
=>
const
getMenuList
=
(
data
:
getMenuListDto
)
=>
request
(
'
/bi-manager/menu/getMenuList
'
,
'
ge
t
'
,
data
);
request
(
'
/bi-manager/menu/getMenuList
'
,
'
pos
t
'
,
data
);
export
default
{
addMenu
,
delMenu
,
getMenuList
};
export
default
{
addMenu
,
editMenu
,
delMenu
,
getMenuList
};
src/main.ts
View file @
dd2396a7
...
@@ -15,6 +15,7 @@ import './styles/themes/dark.scss';
...
@@ -15,6 +15,7 @@ import './styles/themes/dark.scss';
import
'
./styles/index.scss
'
;
import
'
./styles/index.scss
'
;
import
'
uno.css
'
;
import
'
uno.css
'
;
import
'
./styles/vxetheme.scss
'
;
import
'
./styles/vxetheme.scss
'
;
import
'
./styles/common.scss
'
;
const
app
=
createApp
(
App
);
const
app
=
createApp
(
App
);
...
...
src/styles/common.scss
0 → 100644
View file @
dd2396a7
.page-container
{
overflow
:
hidden
;
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
box-sizing
:
border-box
;
}
.w100
{
width
:
100%
;
}
.flex-content-x
{
flex
:
1
;
overflow-x
:
auto
;
position
:
relative
;
}
.flex-content-y
{
flex
:
1
;
overflow-y
:
auto
;
position
:
relative
;
}
.card
{
padding
:
8px
;
background-color
:
#fff
;
border-radius
:
5px
;
overflow
:
hidden
;
}
/* 横向flex布局 */
.flex-start-start
{
display
:
flex
;
justify-content
:
flex-start
;
align-items
:
flex-start
;
}
.flex-start-center
{
display
:
flex
;
justify-content
:
flex-start
;
align-items
:
center
;
}
.flex-start-end
{
display
:
flex
;
justify-content
:
flex-start
;
align-items
:
flex-end
;
}
.flex-center-center
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
}
.flex-center-start
{
display
:
flex
;
justify-content
:
center
;
align-items
:
flex-start
;
}
.flex-center-end
{
display
:
flex
;
justify-content
:
center
;
align-items
:
flex-end
;
}
.flex-end-start
{
display
:
flex
;
justify-content
:
flex-end
;
align-items
:
flex-start
;
}
.flex-end-center
{
display
:
flex
;
justify-content
:
flex-end
;
align-items
:
center
;
}
.flex-end-end
{
display
:
flex
;
justify-content
:
flex-end
;
align-items
:
flex-end
;
}
.flex-spacebetween-center
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.flex-spacebetween-start
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
flex-start
;
}
.flex-spacebetween-end
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
flex-end
;
}
.flex-spacearound-start
{
display
:
flex
;
justify-content
:
space-around
;
align-items
:
flex-start
;
}
.flex-spacearound-center
{
display
:
flex
;
justify-content
:
space-around
;
align-items
:
center
;
}
.flex-spacearound-end
{
display
:
flex
;
justify-content
:
space-around
;
align-items
:
flex-end
;
}
.flex-column
{
display
:
flex
;
flex-direction
:
column
;
}
.w100
{
width
:
100%
;
}
.h100
{
height
:
100%
;
}
.flex-1
{
flex
:
1
;
}
// 间距
.margin10
{
margin
:
10px
;
}
.margin_bottom10
{
margin-bottom
:
10px
;
}
.margin_top10
{
margin-top
:
10px
;
}
.margin_left10
{
margin-left
:
10px
;
}
.margin_right10
{
margin-right
:
10px
;
}
.padding10
{
padding
:
10px
;
}
.margin_bottom8
{
margin-bottom
:
8px
;
}
src/views/web/components/AddMenu.vue
0 → 100644
View file @
dd2396a7
<
template
>
<ele-modal
v-model=
"modalVisible"
:title=
"modalTitle"
width=
"600px"
position=
"center"
@
close=
"handleCancel"
>
<ele-loading
:loading=
"pageLoading"
>
<ele-card>
<pro-form
ref=
"ProFormRef"
label-width=
"auto"
:model=
"form"
:items=
"items"
:footer=
"false"
:grid=
"
{ span: 24 }"
@updateValue="setFieldValue"
/>
</ele-card>
</ele-loading>
<template
#footer
>
<el-button
type=
"primary"
plain
@
click=
"handleCancel"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"onSubmit"
>
确定
</el-button>
</
template
>
</ele-modal>
</template>
<
script
lang=
"ts"
setup
>
// tools
import
{
useFormData
}
from
'
@/utils/use-form-data
'
;
import
{
addMenuDto
}
from
'
@/types/BiMenu
'
;
import
{
tryit
}
from
'
radash
'
;
import
{
EleMessage
}
from
'
ele-admin-plus
'
;
import
menuApi
from
'
@/api/menuApi
'
;
const
modalVisible
=
ref
(
false
);
const
useType
=
ref
<
'
add
'
|
'
edit
'
>
(
'
add
'
);
const
pageLoading
=
ref
(
false
);
const
modalTitle
=
computed
(()
=>
{
return
useType
.
value
===
'
add
'
?
'
添加菜单
'
:
'
编辑菜单
'
;
});
/** 表单数据 */
const
[
form
,
resetFields
,
,
setFieldValue
]
=
useFormData
<
addMenuDto
>
({
menuType
:
'
0
'
,
// 类型:0web端,1移动端
pid
:
''
,
// 父级ID
menuName
:
''
,
// 菜单名称
menuUrl
:
''
,
// 菜单链接
menuId
:
''
,
// 菜单ID
isJoint
:
0
,
// 是否需要拼接参数 1拼,0不拼
status
:
0
,
// 状态:0开启,1关闭
sort
:
0
,
// 排序
});
// 打开
const
curInfo
=
ref
<
addMenuDto
>
();
const
open
=
(
type
:
'
add
'
|
'
edit
'
=
'
add
'
,
info
?:
addMenuDto
)
=>
{
useType
.
value
=
type
;
modalVisible
.
value
=
true
;
if
(
info
){
curInfo
.
value
=
info
;
Object
.
assign
(
form
,
info
);
}
// if (type === 'edit') {
// curInfo.value = info;
// Object.assign(form, info);
// }
};
defineExpose
({
open
:
open
as
(
type
:
'
add
'
|
'
edit
'
,
info
?:
addThemeDTO
)
=>
void
});
onMounted
(()
=>
{
getMenuList
();
// 获取菜单列表
});
const
menuList
=
ref
([]);
const
getMenuList
=
async
()
=>
{
pageLoading
.
value
=
true
;
let
params
=
{
menuType
:
'
0
'
};
const
[
err
,
ret
]
=
await
tryit
(
menuApi
.
getMenuList
)(
params
);
if
(
err
)
{
console
.
error
(
err
);
return
;
}
if
(
ret
.
code
===
200
)
{
menuList
.
value
=
ret
.
data
.
map
((
item
)
=>
{
return
{
label
:
item
.
menuName
,
value
:
item
.
menuId
};
});
}
pageLoading
.
value
=
false
;
};
/** 表单项 */
const
items
=
computed
<
Array
<
ProFormItemProps
>>
(()
=>
[
{
label
:
'
上级菜单
'
,
prop
:
'
pid
'
,
type
:
'
select
'
,
options
:
menuList
.
value
},
{
label
:
'
菜单名称
'
,
prop
:
'
menuName
'
,
type
:
'
input
'
,
required
:
true
},
{
label
:
'
菜单链接
'
,
prop
:
'
menuUrl
'
,
type
:
'
input
'
,
required
:
true
},
{
label
:
'
排序
'
,
prop
:
'
sort
'
,
type
:
'
inputNumber
'
,
required
:
true
},
// {
// label: '关联角色',
// prop: 'roleIds',
// type: 'multipleSelect',
// options: roleList.value,
// required: true
// },
{
label
:
'
是否需要拼参数
'
,
prop
:
'
isJoint
'
,
type
:
'
radio
'
,
options
:
[
{
label
:
'
不拼
'
,
value
:
0
},
{
label
:
'
拼
'
,
value
:
1
}
],
required
:
true
},
{
label
:
'
是否开启
'
,
prop
:
'
status
'
,
type
:
'
radio
'
,
options
:
[
{
label
:
'
开启
'
,
value
:
0
},
{
label
:
'
关闭
'
,
value
:
1
}
],
required
:
true
}
]);
/** 提交 */
const
ProFormRef
=
ref
<
FormInstance
>
();
const
emits
=
defineEmits
([
'
reload
'
]);
const
onSubmit
=
()
=>
{
ProFormRef
.
value
?.
validate
(
async
(
valid
)
=>
{
if
(
!
valid
)
{
EleMessage
.
warning
(
'
请填写必填项
'
);
return
;
}
ElMessageBox
.
confirm
(
`确定要
${
useType
.
value
===
'
add
'
?
'
保存
'
:
'
修改
'
}
?`
,
'
提示
'
,
{
type
:
'
warning
'
}
).
then
(
async
()
=>
{
pageLoading
.
value
=
true
;
const
apiObj
=
{
add
:
menuApi
.
addMenu
,
edit
:
menuApi
.
editMenu
};
let
params
=
{
...
form
,
pid
:
form
.
pid
===
''
?
0
:
form
.
pid
};
const
[,
ret
]
=
await
tryit
(
apiObj
[
useType
.
value
!
])(
params
);
if
(
ret
?.
code
===
200
)
{
EleMessage
.
success
(
useType
.
value
===
'
add
'
?
'
添加成功
'
:
'
修改成功
'
);
emits
(
'
reload
'
);
handleClose
();
}
pageLoading
.
value
=
false
;
});
});
};
// 关闭
const
handleClose
=
()
=>
{
modalVisible
.
value
=
false
;
resetFields
();
};
// 点击取消
const
handleCancel
=
()
=>
{
handleClose
();
};
</
script
>
src/views/web/index.vue
View file @
dd2396a7
...
@@ -6,35 +6,56 @@
...
@@ -6,35 +6,56 @@
<template
#toolbar_slot
>
<template
#toolbar_slot
>
<div
class=
"p-l-4px flex flex-content-start"
>
<div
class=
"p-l-4px flex flex-content-start"
>
<FcCardTip
title=
"菜单维护"
/>
<FcCardTip
title=
"菜单维护"
/>
<el-button
class=
"m-l-4px"
type=
"primary"
plain
size=
"small"
>
<el-button
class=
"m-l-4px"
type=
"primary"
plain
size=
"small"
@
click=
"toggleExpand"
>
收起/展开
收起/展开
</el-button>
</el-button>
<el-button
type=
"success"
size=
"small"
>
添加菜单
</el-button>
<el-button
type=
"success"
size=
"small"
@
click=
"handleAdd"
>
添加菜单
</el-button
>
</div>
</div>
</
template
>
</
template
>
<!-- 菜单名称 -->
<
template
#menuName_slot
="{
row
}"
>
<
template
#menuName_slot
="{
row
}"
>
<span>
{{
row
.
menuName
}}
</span>
<div
class=
"menuName_text flex-start-center"
>
<el-icon
color=
"#114dfc"
size=
"16px"
class=
"m-l-4px"
>
<span>
{{
row
.
menuName
}}
</span>
<CirclePlus
/>
<el-icon
</el-icon>
color=
"#114dfc"
size=
"18px"
class=
"m-l-4px"
@
click=
"handleAddByNext(row)"
>
<CirclePlus
/>
</el-icon>
</div>
</
template
>
</
template
>
<!-- 操作 -->
<!-- 操作 -->
<
template
#option_slot
>
<
template
#option_slot
="{
row
}"
>
<el-link
type=
"primary"
>
编辑
</el-link>
<el-link
type=
"primary"
@
click=
"handleEdit(row)"
>
编辑
</el-link>
<el-divider
direction=
"vertical"
/>
<el-divider
direction=
"vertical"
/>
<el-link
type=
"warning"
>
页面预览
</el-link>
<el-link
type=
"warning"
>
页面预览
</el-link>
<el-divider
direction=
"vertical"
/>
<el-divider
direction=
"vertical"
/>
<el-link
type=
"danger"
>
删除
</el-link>
<el-link
type=
"danger"
@
click=
"handleDelete(row)"
>
删除
</el-link>
</
template
>
</
template
>
<!-- 展开插槽 -->
<!-- 展开插槽 -->
<
template
#expand_slot
>
<
template
#expand_slot
="{
row
}"
>
<vxe-grid
v-bind=
"expandGridOptions"
/>
<vxe-grid
v-bind=
"expandGridOptions"
:data=
"row.menuChildren"
>
<!-- 操作 -->
<template
#option_slot
="
{ row }">
<el-link
type=
"primary"
@
click=
"handleEdit(row)"
>
编辑
</el-link>
<el-divider
direction=
"vertical"
/>
<el-link
type=
"warning"
>
页面预览
</el-link>
<el-divider
direction=
"vertical"
/>
<el-link
type=
"danger"
@
click=
"handleDelete(row)"
>
删除
</el-link>
</
template
>
</vxe-grid>
</template>
</template>
</vxe-grid>
</vxe-grid>
</div>
</div>
<!-- 新增/编辑弹窗 -->
<addMenu
ref=
"AddMenuRef"
@
reload=
"getMenuList"
></addMenu>
</ele-loading>
</ele-loading>
</template>
</template>
...
@@ -43,6 +64,16 @@
...
@@ -43,6 +64,16 @@
import
{
FcCardTip
}
from
'
@fancy-design/coms
'
;
import
{
FcCardTip
}
from
'
@fancy-design/coms
'
;
import
{
CirclePlus
}
from
'
@element-plus/icons-vue
'
;
import
{
CirclePlus
}
from
'
@element-plus/icons-vue
'
;
import
{
VxeGridProps
}
from
'
vxe-table
'
;
import
{
VxeGridProps
}
from
'
vxe-table
'
;
import
{
tryit
}
from
'
radash
'
;
import
{
EleMessage
}
from
'
ele-admin-plus
'
;
import
menuApi
from
'
@/api/menuApi
'
;
import
addMenu
from
'
./components/AddMenu.vue
'
;
onMounted
(()
=>
{
getMenuList
();
// 获取菜单列表
});
const
xGrid
=
ref
()
const
pageLoading
=
ref
(
false
);
const
pageLoading
=
ref
(
false
);
const
gridOptions
=
reactive
<
VxeGridProps
>
({
const
gridOptions
=
reactive
<
VxeGridProps
>
({
...
@@ -81,18 +112,19 @@
...
@@ -81,18 +112,19 @@
slots
:
{
content
:
'
expand_slot
'
,
default
:
'
menuName_slot
'
}
slots
:
{
content
:
'
expand_slot
'
,
default
:
'
menuName_slot
'
}
},
},
{
{
field
:
'
u
rl
'
,
field
:
'
menuU
rl
'
,
title
:
'
URL地址
'
title
:
'
URL地址
'
},
},
{
{
field
:
'
操作
'
,
field
:
'
操作
'
,
fixed
:
'
right
'
,
fixed
:
'
right
'
,
title
:
'
操作
'
,
title
:
'
操作
'
,
width
:
200
,
slots
:
{
default
:
'
option_slot
'
}
slots
:
{
default
:
'
option_slot
'
}
}
}
],
],
pagerConfig
:
{
pagerConfig
:
{
enabled
:
tru
e
,
enabled
:
fals
e
,
pageSize
:
10
,
pageSize
:
10
,
pageSizes
:
[
5
,
10
,
15
,
20
,
50
,
100
,
200
,
500
,
1000
]
pageSizes
:
[
5
,
10
,
15
,
20
,
50
,
100
,
200
,
500
,
1000
]
},
},
...
@@ -123,7 +155,7 @@
...
@@ -123,7 +155,7 @@
title
:
'
菜单名称
'
title
:
'
菜单名称
'
},
},
{
{
field
:
'
u
rl
'
,
field
:
'
menuU
rl
'
,
title
:
'
URL地址
'
title
:
'
URL地址
'
},
},
{
{
...
@@ -131,9 +163,96 @@
...
@@ -131,9 +163,96 @@
fixed
:
'
right
'
,
fixed
:
'
right
'
,
title
:
'
Role
'
,
title
:
'
Role
'
,
sortable
:
true
,
sortable
:
true
,
editRender
:
{
name
:
'
input
'
,
attrs
:
{
placeholder
:
'
请输入角色
'
}
}
width
:
200
,
slots
:
{
default
:
'
option_slot
'
}
}
}
],
],
data
:
[
{
menuName
:
'
首页
'
,
url
:
'
www.ce.com
'
}
]
data
:
[]
});
});
// 获取菜单列表
const
getMenuList
=
async
()
=>
{
pageLoading
.
value
=
true
;
let
parmas
=
{
menuType
:
'
0
'
};
const
[
err
,
ret
]
=
await
tryit
(
menuApi
.
getMenuList
)(
parmas
);
if
(
err
)
{
console
.
error
(
err
);
return
;
}
if
(
ret
?.
code
===
200
)
{
gridOptions
.
data
=
ret
.
data
;
}
pageLoading
.
value
=
false
;
};
// 添加菜单
const
AddMenuRef
=
ref
();
const
handleAdd
=
()
=>
{
AddMenuRef
.
value
.
open
();
};
// 编辑菜单
const
handleEdit
=
(
row
)
=>
{
row
.
pid
=
row
.
pid
==
0
?
''
:
row
.
pid
;
// pid特殊处理
AddMenuRef
.
value
.
open
(
'
edit
'
,
row
);
// 传递菜单信息
};
// 添加子集
const
handleAddByNext
=
(
row
)
=>
{
let
obj
=
{
pid
:
row
.
menuId
};
AddMenuRef
.
value
.
open
(
'
add
'
,
obj
);
// 传递父菜单信息
};
// 删除菜单
const
handleDelete
=
async
(
row
)
=>
{
ElMessageBox
.
confirm
(
`确定要删除此菜单吗?`
,
'
提示
'
,
{
type
:
'
warning
'
}).
then
(
async
(
res
)
=>
{
let
params
=
{
menuId
:
row
.
menuId
,
menuType
:
'
0
'
,
pid
:
row
.
pid
};
const
[
err
,
ret
]
=
await
tryit
(
menuApi
.
delMenu
)(
params
);
if
(
err
)
{
console
.
error
(
err
);
return
;
}
if
(
ret
?.
code
===
200
)
{
EleMessage
.
success
(
'
删除成功
'
);
getMenuList
();
// 重新获取菜单列表
}
});
};
// 展开/收起
const
toggleExpand
=
()
=>
{
console
.
log
(
"
🚀 ~ handleUnfold ~ xGrid.value:
"
,
xGrid
.
value
)
// xGrid.value.setRowExpand();
}
</
script
>
</
script
>
<
style
lang=
"scss"
scoped
>
.menuName_text
{
display
:
inline-block
;
:deep
()
{
.el-icon
{
cursor
:
pointer
;
}
}
}
</
style
>
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