提交 9a269f8d 作者: 郁骅焌

菜单管理

上级 d7cb6d09
......@@ -5,5 +5,5 @@
NODE_ENV=development
# api接口地址
# VITE_APP_BASE_URL='http://139.196.169.103:9003'
VITE_APP_BASE_URL=''
VITE_APP_BASE_URL='http://139.196.169.103:9003'
# VITE_APP_BASE_URL=''
......@@ -15,6 +15,8 @@ declare global {
const $sub: typeof import('../../../src/hooks/index')['$sub']
const $unsub: typeof import('../../../src/hooks/index')['$unsub']
const EffectScope: typeof import('vue')['EffectScope']
const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
......
......@@ -35,6 +35,7 @@ declare module 'vue' {
DepartmentManagementEdit: typeof import('./../../../src/views/setting/departmentManagement/vabAutoComponents/DepartmentManagementEdit.vue')['default']
Develop: typeof import('./../../../src/views/index/vabAutoComponents/Develop.vue')['default']
DictionaryManagementEdit: typeof import('./../../../src/views/setting/dictionaryManagement/vabAutoComponents/DictionaryManagementEdit.vue')['default']
DictTag: typeof import('./../../components/DictTag/index.vue')['default']
DrawerBasicUsage: typeof import('./../../../src/views/vab/drawer/vabAutoComponents/DrawerBasicUsage.vue')['default']
DrawerCustomizationContent: typeof import('./../../../src/views/vab/drawer/vabAutoComponents/DrawerCustomizationContent.vue')['default']
DrawerCustomizationHeader: typeof import('./../../../src/views/vab/drawer/vabAutoComponents/DrawerCustomizationHeader.vue')['default']
......@@ -140,6 +141,7 @@ declare module 'vue' {
InfiniteScrollDisableLoading: typeof import('./../../../src/views/vab/infiniteScroll/vabAutoComponents/InfiniteScrollDisableLoading.vue')['default']
LoginContainer: typeof import('./../../../src/views/login/vabAutoComponents/LoginContainer.vue')['default']
MenuManagementEdit: typeof import('./../../../src/views/setting/menuManagement/vabAutoComponents/MenuManagementEdit.vue')['default']
MenuManagementEdit2: typeof import('./../../../src/views/system/menuManagement/vabAutoComponents/MenuManagementEdit2.vue')['default']
NodePanel: typeof import('./../../../src/views/other/workflow/vabAutoComponents/lFComponents/NodePanel.vue')['default']
PageHeader: typeof import('./../../../src/views/index/vabAutoComponents/PageHeader.vue')['default']
Pending: typeof import('./../../../src/views/index/vabAutoComponents/Pending.vue')['default']
......@@ -196,6 +198,7 @@ declare module 'vue' {
UploadPhotoWall: typeof import('./../../../src/views/vab/upload/vabAutoComponents/UploadPhotoWall.vue')['default']
User: typeof import('./../../../src/views/other/workflow/vabAutoComponents/propertySetting/User.vue')['default']
UserManagementEdit: typeof import('./../../../src/views/setting/userManagement/vabAutoComponents/UserManagementEdit.vue')['default']
UserManagementEdit2: typeof import('./../../../src/views/system/userManagement/vabAutoComponents/UserManagementEdit2.vue')['default']
VabAlert: typeof import('./../../components/VabAlert/index.vue')['default']
VabApp: typeof import('./../../components/VabApp/index.vue')['default']
VabAppMain: typeof import('./../../components/VabAppMain/index.vue')['default']
......
<template>
<div>
<template v-for="(item, index) in options">
<template v-if="values.includes(item.value)">
<span
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
:key="item.value"
:class="item.elTagClass"
:index="index"
>
{{ item.label + ' ' }}
</span>
<el-tag v-else :key="item.value + ''" :class="item.elTagClass" :disable-transitions="true" :index="index" :type="item.elTagType">
{{ item.label + ' ' }}
</el-tag>
</template>
</template>
<template v-if="unmatch && showValue">
{{ handledUnmatchArray }}
</template>
</div>
</template>
<script setup>
// 记录未匹配的项
const unmatchArray = ref([])
const props = defineProps({
// 数据
options: {
type: Array,
default: null,
},
// 当前的值
value: [Number, String, Array],
// 当未找到匹配的数据时,显示value
showValue: {
type: Boolean,
default: true,
},
separator: {
type: String,
default: ',',
},
})
const values = computed(() => {
if (props.value === null || props.value === undefined || props.value === '') return []
return Array.isArray(props.value) ? props.value.map(String) : String(props.value).split(props.separator)
})
const unmatch = computed(() => {
unmatchArray.value = []
// 没有value不显示
if (
props.value === null ||
props.value === undefined ||
props.value === '' ||
!Array.isArray(props.options) ||
props.options.length === 0
)
return false
// 传入值为数组
let unmatch = false // 添加一个标志来判断是否有未匹配项
values.value.forEach((item) => {
if (!props.options.some((v) => v.value === item)) {
unmatchArray.value.push(item)
unmatch = true // 如果有未匹配项,将标志设置为true
}
})
return unmatch // 返回标志的值
})
const handledUnmatchArray = computed(() => {
if (unmatchArray.value.length === 0) return ''
return unmatchArray.value.reduce((pre, cur) => {
return `${pre} ${cur}`
})
})
</script>
<style scoped>
.el-tag + .el-tag {
margin-left: 10px;
}
</style>
......@@ -100,7 +100,7 @@ export default [
},
},
{
url: '/system/dept/treeselect',
url: '/system/user/deptTree',
method: 'get',
response: () => {
return {
......
......@@ -1216,9 +1216,9 @@ const list: VabRouteRecord[] = [
{
path: 'userManagement',
name: 'UserManagement',
component: '/@/views/setting/userManagement/index.vue',
component: '/@views/setting/userManagement/index.vue',
meta: {
title: '用户管理1',
title: '用户管理',
icon: 'user-3-line',
},
},
......
......@@ -93,7 +93,7 @@ const List = [
export default [
{
url: '/userManagement/getList',
url: '/system/user/list',
method: 'get',
response({ query }: any) {
const { username, pageNo = 1, pageSize = 20 } = query
......@@ -129,8 +129,8 @@ export default [
},
},
{
url: '/userManagement/doDelete',
method: 'post',
url: '/system/user/:userId',
method: 'delete',
response() {
return {
code: 200,
......
......@@ -33,6 +33,7 @@
"@logicflow/extension": "^2.0.12",
"@lucky-canvas/vue": "^0.1.11",
"@opentiny/vue": "3.18.0",
"@types/file-saver": "^2.0.7",
"@vueuse/core": "^12.0.0",
"@vueuse/head": "^2.0.0",
"@wangeditor/editor": "^5.1.23",
......@@ -42,6 +43,7 @@
"disable-devtool": "^0.3.8",
"echarts": "^5.5.1",
"element-plus": "^2.8.8",
"file-saver": "2.0.5",
"jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21",
"mitt": "^3.0.1",
......
......@@ -27,7 +27,7 @@ export const doDelete = (data: any) => {
// 查询部门下拉树结构
export function deptTreeSelect() {
return request({
url: '/system/dept/treeselect',
url: '/system/user/deptTree',
method: 'get',
})
}
import request from '/@/utils/request'
// 查询菜单列表
export function listMenu(params?: any) {
return request({
url: '/system/menu/list',
method: 'get',
params,
})
}
export function getTree(params?: any) {
return request({
url: '/menuManagement/getTree',
......@@ -8,18 +17,36 @@ export function getTree(params?: any) {
})
}
export const doEdit = (data: any) => {
/** 新增菜单 */
export const doAdd = (data: any) => {
return request({
url: '/menuManagement/doEdit',
url: '/system/menu',
method: 'post',
data,
})
}
export const doDelete = (data: any) => {
/** 修改菜单 */
export const doEdit = (data: any) => {
return request({
url: '/menuManagement/doDelete',
method: 'post',
url: '/system/menu',
method: 'put',
data,
})
}
/** 删除菜单 */
export const doDelete = (menuId: any) => {
return request({
url: `/system/menu/${menuId}`,
method: 'delete',
})
}
// 查询菜单详细
export function getMenu(menuId: any) {
return request({
url: `/system/menu/${menuId}`,
method: 'get',
})
}
......@@ -3,7 +3,7 @@ import request from '/@/utils/request'
export function getList(params?: any) {
return request({
url: '/userManagement/getList',
url: '/system/user/list',
method: 'get',
params,
})
......@@ -25,11 +25,10 @@ export const doEdit = (data: any) => {
})
}
export const doDelete = (data: any) => {
export const doDelete = (userId: any) => {
return request({
url: '/userManagement/doDelete',
method: 'post',
data,
url: `/system/user/${userId}`,
method: 'delete',
})
}
......@@ -40,3 +39,16 @@ export function getUser(userId: any) {
method: 'get',
})
}
// 用户状态修改
export function changeUserStatus(userId: any, status: any) {
const data = {
userId,
status,
}
return request({
url: '/system/user/changeStatus',
method: 'put',
data,
})
}
export default [
'24-hours-fill',
'24-hours-line',
'4k-fill',
'4k-line',
'a-b',
'account-box-fill',
'account-box-line',
'account-circle-fill',
'account-circle-line',
'account-pin-box-fill',
'account-pin-box-line',
'account-pin-circle-fill',
'account-pin-circle-line',
'add-box-fill',
'add-box-line',
'add-circle-fill',
'add-circle-line',
'add-fill',
'add-line',
'admin-fill',
'admin-line',
'advertisement-fill',
'advertisement-line',
'airplay-fill',
'airplay-line',
'alarm-fill',
'alarm-line',
'alarm-warning-fill',
'alarm-warning-line',
'album-fill',
'album-line',
'alert-fill',
'alert-line',
'aliens-fill',
'aliens-line',
'align-bottom',
'align-center',
'align-justify',
'align-left',
'align-right',
'align-top',
'align-vertically',
'alipay-fill',
'alipay-line',
'amazon-fill',
'amazon-line',
'anchor-fill',
'anchor-line',
'ancient-gate-fill',
'ancient-gate-line',
'ancient-pavilion-fill',
'ancient-pavilion-line',
'android-fill',
'android-line',
'angularjs-fill',
'angularjs-line',
'anticlockwise-2-fill',
'anticlockwise-2-line',
'anticlockwise-fill',
'anticlockwise-line',
'app-store-fill',
'app-store-line',
'apple-fill',
'apple-line',
'apps-2-fill',
'apps-2-line',
'apps-fill',
'apps-line',
'archive-drawer-fill',
'archive-drawer-line',
'archive-fill',
'archive-line',
'arrow-down-circle-fill',
'arrow-down-circle-line',
'arrow-down-fill',
'arrow-down-line',
'arrow-down-s-fill',
'arrow-down-s-line',
'arrow-drop-down-fill',
'arrow-drop-down-line',
'arrow-drop-left-fill',
'arrow-drop-left-line',
'arrow-drop-right-fill',
'arrow-drop-right-line',
'arrow-drop-up-fill',
'arrow-drop-up-line',
'arrow-go-back-fill',
'arrow-go-back-line',
'arrow-go-forward-fill',
'arrow-go-forward-line',
'arrow-left-circle-fill',
'arrow-left-circle-line',
'arrow-left-down-fill',
'arrow-left-down-line',
'arrow-left-fill',
'arrow-left-line',
'arrow-left-right-fill',
'arrow-left-right-line',
'arrow-left-s-fill',
'arrow-left-s-line',
'arrow-left-up-fill',
'arrow-left-up-line',
'arrow-right-circle-fill',
'arrow-right-circle-line',
'arrow-right-down-fill',
'arrow-right-down-line',
'arrow-right-fill',
'arrow-right-line',
'arrow-right-s-fill',
'arrow-right-s-line',
'arrow-right-up-fill',
'arrow-right-up-line',
'arrow-up-circle-fill',
'arrow-up-circle-line',
'arrow-up-down-fill',
'arrow-up-down-line',
'arrow-up-fill',
'arrow-up-line',
'arrow-up-s-fill',
'arrow-up-s-line',
'artboard-2-fill',
'artboard-2-line',
'artboard-fill',
'artboard-line',
'article-fill',
'article-line',
'aspect-ratio-fill',
'aspect-ratio-line',
'asterisk',
'at-fill',
'at-line',
'attachment-2',
'attachment-fill',
'attachment-line',
'auction-fill',
'auction-line',
'award-fill',
'award-line',
'baidu-fill',
'baidu-line',
'ball-pen-fill',
'ball-pen-line',
'bank-card-2-fill',
'bank-card-2-line',
'bank-card-fill',
'bank-card-line',
'bank-fill',
'bank-line',
'bar-chart-2-fill',
'bar-chart-2-line',
'bar-chart-box-fill',
'bar-chart-box-line',
'bar-chart-fill',
'bar-chart-grouped-fill',
'bar-chart-grouped-line',
'bar-chart-horizontal-fill',
'bar-chart-horizontal-line',
'bar-chart-line',
'barcode-box-fill',
'barcode-box-line',
'barcode-fill',
'barcode-line',
'barricade-fill',
'barricade-line',
'base-station-fill',
'base-station-line',
'basketball-fill',
'basketball-line',
'battery-2-charge-fill',
'battery-2-charge-line',
'battery-2-fill',
'battery-2-line',
'battery-charge-fill',
'battery-charge-line',
'battery-fill',
'battery-line',
'battery-low-fill',
'battery-low-line',
'battery-saver-fill',
'battery-saver-line',
'battery-share-fill',
'battery-share-line',
'bear-smile-fill',
'bear-smile-line',
'behance-fill',
'behance-line',
'bell-fill',
'bell-line',
'bike-fill',
'bike-line',
'bilibili-fill',
'bilibili-line',
'bill-fill',
'bill-line',
'billiards-fill',
'billiards-line',
'bit-coin-fill',
'bit-coin-line',
'blaze-fill',
'blaze-line',
'bluetooth-connect-fill',
'bluetooth-connect-line',
'bluetooth-fill',
'bluetooth-line',
'blur-off-fill',
'blur-off-line',
'body-scan-fill',
'body-scan-line',
'bold',
'book-2-fill',
'book-2-line',
'book-3-fill',
'book-3-line',
'book-fill',
'book-line',
'book-marked-fill',
'book-marked-line',
'book-open-fill',
'book-open-line',
'book-read-fill',
'book-read-line',
'booklet-fill',
'booklet-line',
'bookmark-2-fill',
'bookmark-2-line',
'bookmark-3-fill',
'bookmark-3-line',
'bookmark-fill',
'bookmark-line',
'boxing-fill',
'boxing-line',
'braces-fill',
'braces-line',
'brackets-fill',
'brackets-line',
'briefcase-2-fill',
'briefcase-2-line',
'briefcase-3-fill',
'briefcase-3-line',
'briefcase-4-fill',
'briefcase-4-line',
'briefcase-5-fill',
'briefcase-5-line',
'briefcase-fill',
'briefcase-line',
'bring-forward',
'bring-to-front',
'broadcast-fill',
'broadcast-line',
'brush-2-fill',
'brush-2-line',
'brush-3-fill',
'brush-3-line',
'brush-4-fill',
'brush-4-line',
'brush-fill',
'brush-line',
'bubble-chart-fill',
'bubble-chart-line',
'bug-2-fill',
'bug-2-line',
'bug-fill',
'bug-line',
'building-2-fill',
'building-2-line',
'building-3-fill',
'building-3-line',
'building-4-fill',
'building-4-line',
'building-fill',
'building-line',
'bus-2-fill',
'bus-2-line',
'bus-fill',
'bus-line',
'bus-wifi-fill',
'bus-wifi-line',
'cactus-fill',
'cactus-line',
'cake-2-fill',
'cake-2-line',
'cake-3-fill',
'cake-3-line',
'cake-fill',
'cake-line',
'calculator-fill',
'calculator-line',
'calendar-2-fill',
'calendar-2-line',
'calendar-check-fill',
'calendar-check-line',
'calendar-event-fill',
'calendar-event-line',
'calendar-fill',
'calendar-line',
'calendar-todo-fill',
'calendar-todo-line',
'camera-2-fill',
'camera-2-line',
'camera-3-fill',
'camera-3-line',
'camera-fill',
'camera-lens-fill',
'camera-lens-line',
'camera-line',
'camera-off-fill',
'camera-off-line',
'camera-switch-fill',
'camera-switch-line',
'capsule-fill',
'capsule-line',
'car-fill',
'car-line',
'car-washing-fill',
'car-washing-line',
'caravan-fill',
'caravan-line',
'cast-fill',
'cast-line',
'cellphone-fill',
'cellphone-line',
'celsius-fill',
'celsius-line',
'centos-fill',
'centos-line',
'character-recognition-fill',
'character-recognition-line',
'charging-pile-2-fill',
'charging-pile-2-line',
'charging-pile-fill',
'charging-pile-line',
'chat-1-fill',
'chat-1-line',
'chat-2-fill',
'chat-2-line',
'chat-3-fill',
'chat-3-line',
'chat-4-fill',
'chat-4-line',
'chat-check-fill',
'chat-check-line',
'chat-delete-fill',
'chat-delete-line',
'chat-download-fill',
'chat-download-line',
'chat-follow-up-fill',
'chat-follow-up-line',
'chat-forward-fill',
'chat-forward-line',
'chat-heart-fill',
'chat-heart-line',
'chat-history-fill',
'chat-history-line',
'chat-new-fill',
'chat-new-line',
'chat-off-fill',
'chat-off-line',
'chat-poll-fill',
'chat-poll-line',
'chat-private-fill',
'chat-private-line',
'chat-quote-fill',
'chat-quote-line',
'chat-settings-fill',
'chat-settings-line',
'chat-smile-2-fill',
'chat-smile-2-line',
'chat-smile-3-fill',
'chat-smile-3-line',
'chat-smile-fill',
'chat-smile-line',
'chat-upload-fill',
'chat-upload-line',
'chat-voice-fill',
'chat-voice-line',
'check-double-fill',
'check-double-line',
'check-fill',
'check-line',
'checkbox-blank-circle-fill',
'checkbox-blank-circle-line',
'checkbox-blank-fill',
'checkbox-blank-line',
'checkbox-circle-fill',
'checkbox-circle-line',
'checkbox-fill',
'checkbox-indeterminate-fill',
'checkbox-indeterminate-line',
'checkbox-line',
'checkbox-multiple-blank-fill',
'checkbox-multiple-blank-line',
'checkbox-multiple-fill',
'checkbox-multiple-line',
'china-railway-fill',
'china-railway-line',
'chrome-fill',
'chrome-line',
'clapperboard-fill',
'clapperboard-line',
'clipboard-fill',
'clipboard-line',
'clockwise-2-fill',
'clockwise-2-line',
'clockwise-fill',
'clockwise-line',
'close-circle-fill',
'close-circle-line',
'close-fill',
'close-line',
'closed-captioning-fill',
'closed-captioning-line',
'cloud-fill',
'cloud-line',
'cloud-off-fill',
'cloud-off-line',
'cloud-windy-fill',
'cloud-windy-line',
'cloudy-2-fill',
'cloudy-2-line',
'cloudy-fill',
'cloudy-line',
'code-box-fill',
'code-box-line',
'code-fill',
'code-line',
'code-s-fill',
'code-s-line',
'code-s-slash-fill',
'code-s-slash-line',
'code-view',
'codepen-fill',
'codepen-line',
'coin-fill',
'coin-line',
'coins-fill',
'coins-line',
'collage-fill',
'collage-line',
'command-fill',
'command-line',
'community-fill',
'community-line',
'compass-2-fill',
'compass-2-line',
'compass-3-fill',
'compass-3-line',
'compass-4-fill',
'compass-4-line',
'compass-discover-fill',
'compass-discover-line',
'compass-fill',
'compass-line',
'compasses-2-fill',
'compasses-2-line',
'compasses-fill',
'compasses-line',
'computer-fill',
'computer-line',
'contacts-book-2-fill',
'contacts-book-2-line',
'contacts-book-fill',
'contacts-book-line',
'contacts-book-upload-fill',
'contacts-book-upload-line',
'contacts-fill',
'contacts-line',
'contrast-2-fill',
'contrast-2-line',
'contrast-drop-2-fill',
'contrast-drop-2-line',
'contrast-drop-fill',
'contrast-drop-line',
'contrast-fill',
'contrast-line',
'copper-coin-fill',
'copper-coin-line',
'copper-diamond-fill',
'copper-diamond-line',
'copyleft-fill',
'copyleft-line',
'copyright-fill',
'copyright-line',
'coreos-fill',
'coreos-line',
'coupon-2-fill',
'coupon-2-line',
'coupon-3-fill',
'coupon-3-line',
'coupon-4-fill',
'coupon-4-line',
'coupon-5-fill',
'coupon-5-line',
'coupon-fill',
'coupon-line',
'cpu-fill',
'cpu-line',
'creative-commons-by-fill',
'creative-commons-by-line',
'creative-commons-fill',
'creative-commons-line',
'creative-commons-nc-fill',
'creative-commons-nc-line',
'creative-commons-nd-fill',
'creative-commons-nd-line',
'creative-commons-sa-fill',
'creative-commons-sa-line',
'creative-commons-zero-fill',
'creative-commons-zero-line',
'criminal-fill',
'criminal-line',
'crop-2-fill',
'crop-2-line',
'crop-fill',
'crop-line',
'css3-fill',
'css3-line',
'cup-fill',
'cup-line',
'currency-fill',
'currency-line',
'cursor-fill',
'cursor-line',
'customer-service-2-fill',
'customer-service-2-line',
'customer-service-fill',
'customer-service-line',
'dashboard-2-fill',
'dashboard-2-line',
'dashboard-3-fill',
'dashboard-3-line',
'dashboard-fill',
'dashboard-line',
'database-2-fill',
'database-2-line',
'database-fill',
'database-line',
'delete-back-2-fill',
'delete-back-2-line',
'delete-back-fill',
'delete-back-line',
'delete-bin-2-fill',
'delete-bin-2-line',
'delete-bin-3-fill',
'delete-bin-3-line',
'delete-bin-4-fill',
'delete-bin-4-line',
'delete-bin-5-fill',
'delete-bin-5-line',
'delete-bin-6-fill',
'delete-bin-6-line',
'delete-bin-7-fill',
'delete-bin-7-line',
'delete-bin-fill',
'delete-bin-line',
'delete-column',
'delete-row',
'device-fill',
'device-line',
'device-recover-fill',
'device-recover-line',
'dingding-fill',
'dingding-line',
'direction-fill',
'direction-line',
'disc-fill',
'disc-line',
'discord-fill',
'discord-line',
'discuss-fill',
'discuss-line',
'dislike-fill',
'dislike-line',
'disqus-fill',
'disqus-line',
'divide-fill',
'divide-line',
'donut-chart-fill',
'donut-chart-line',
'door-closed-fill',
'door-closed-line',
'door-fill',
'door-line',
'door-lock-box-fill',
'door-lock-box-line',
'door-lock-fill',
'door-lock-line',
'door-open-fill',
'door-open-line',
'dossier-fill',
'dossier-line',
'douban-fill',
'douban-line',
'double-quotes-l',
'double-quotes-r',
'download-2-fill',
'download-2-line',
'download-cloud-2-fill',
'download-cloud-2-line',
'download-cloud-fill',
'download-cloud-line',
'download-fill',
'download-line',
'draft-fill',
'draft-line',
'drag-drop-fill',
'drag-drop-line',
'drag-move-2-fill',
'drag-move-2-line',
'drag-move-fill',
'drag-move-line',
'dribbble-fill',
'dribbble-line',
'drive-fill',
'drive-line',
'drizzle-fill',
'drizzle-line',
'drop-fill',
'drop-line',
'dropbox-fill',
'dropbox-line',
'dual-sim-1-fill',
'dual-sim-1-line',
'dual-sim-2-fill',
'dual-sim-2-line',
'dv-fill',
'dv-line',
'dvd-fill',
'dvd-line',
'e-bike-2-fill',
'e-bike-2-line',
'e-bike-fill',
'e-bike-line',
'earth-fill',
'earth-line',
'earthquake-fill',
'earthquake-line',
'edge-fill',
'edge-line',
'edit-2-fill',
'edit-2-line',
'edit-box-fill',
'edit-box-line',
'edit-circle-fill',
'edit-circle-line',
'edit-fill',
'edit-line',
'eject-fill',
'eject-line',
'emotion-2-fill',
'emotion-2-line',
'emotion-fill',
'emotion-happy-fill',
'emotion-happy-line',
'emotion-laugh-fill',
'emotion-laugh-line',
'emotion-line',
'emotion-normal-fill',
'emotion-normal-line',
'emotion-sad-fill',
'emotion-sad-line',
'emotion-unhappy-fill',
'emotion-unhappy-line',
'empathize-fill',
'empathize-line',
'emphasis-cn',
'emphasis',
'english-input',
'equalizer-fill',
'equalizer-line',
'eraser-fill',
'eraser-line',
'error-warning-fill',
'error-warning-line',
'evernote-fill',
'evernote-line',
'exchange-box-fill',
'exchange-box-line',
'exchange-cny-fill',
'exchange-cny-line',
'exchange-dollar-fill',
'exchange-dollar-line',
'exchange-fill',
'exchange-funds-fill',
'exchange-funds-line',
'exchange-line',
'external-link-fill',
'external-link-line',
'eye-2-fill',
'eye-2-line',
'eye-close-fill',
'eye-close-line',
'eye-fill',
'eye-line',
'eye-off-fill',
'eye-off-line',
'facebook-box-fill',
'facebook-box-line',
'facebook-circle-fill',
'facebook-circle-line',
'facebook-fill',
'facebook-line',
'fahrenheit-fill',
'fahrenheit-line',
'feedback-fill',
'feedback-line',
'file-2-fill',
'file-2-line',
'file-3-fill',
'file-3-line',
'file-4-fill',
'file-4-line',
'file-add-fill',
'file-add-line',
'file-chart-2-fill',
'file-chart-2-line',
'file-chart-fill',
'file-chart-line',
'file-cloud-fill',
'file-cloud-line',
'file-code-fill',
'file-code-line',
'file-copy-2-fill',
'file-copy-2-line',
'file-copy-fill',
'file-copy-line',
'file-damage-fill',
'file-damage-line',
'file-download-fill',
'file-download-line',
'file-edit-fill',
'file-edit-line',
'file-excel-2-fill',
'file-excel-2-line',
'file-excel-fill',
'file-excel-line',
'file-fill',
'file-forbid-fill',
'file-forbid-line',
'file-gif-fill',
'file-gif-line',
'file-history-fill',
'file-history-line',
'file-hwp-fill',
'file-hwp-line',
'file-info-fill',
'file-info-line',
'file-line',
'file-list-2-fill',
'file-list-2-line',
'file-list-3-fill',
'file-list-3-line',
'file-list-fill',
'file-list-line',
'file-lock-fill',
'file-lock-line',
'file-marked-fill',
'file-marked-line',
'file-music-fill',
'file-music-line',
'file-paper-2-fill',
'file-paper-2-line',
'file-paper-fill',
'file-paper-line',
'file-pdf-fill',
'file-pdf-line',
'file-ppt-2-fill',
'file-ppt-2-line',
'file-ppt-fill',
'file-ppt-line',
'file-reduce-fill',
'file-reduce-line',
'file-search-fill',
'file-search-line',
'file-settings-fill',
'file-settings-line',
'file-shield-2-fill',
'file-shield-2-line',
'file-shield-fill',
'file-shield-line',
'file-shred-fill',
'file-shred-line',
'file-text-fill',
'file-text-line',
'file-transfer-fill',
'file-transfer-line',
'file-unknow-fill',
'file-unknow-line',
'file-upload-fill',
'file-upload-line',
'file-user-fill',
'file-user-line',
'file-warning-fill',
'file-warning-line',
'file-word-2-fill',
'file-word-2-line',
'file-word-fill',
'file-word-line',
'file-zip-fill',
'file-zip-line',
'film-fill',
'film-line',
'filter-2-fill',
'filter-2-line',
'filter-3-fill',
'filter-3-line',
'filter-fill',
'filter-line',
'filter-off-fill',
'filter-off-line',
'find-replace-fill',
'find-replace-line',
'finder-fill',
'finder-line',
'fingerprint-2-fill',
'fingerprint-2-line',
'fingerprint-fill',
'fingerprint-line',
'fire-fill',
'fire-line',
'firefox-fill',
'firefox-line',
'first-aid-kit-fill',
'first-aid-kit-line',
'flag-2-fill',
'flag-2-line',
'flag-fill',
'flag-line',
'flashlight-fill',
'flashlight-line',
'flask-fill',
'flask-line',
'flight-land-fill',
'flight-land-line',
'flight-takeoff-fill',
'flight-takeoff-line',
'flood-fill',
'flood-line',
'flow-chart',
'flutter-fill',
'flutter-line',
'focus-2-fill',
'focus-2-line',
'focus-3-fill',
'focus-3-line',
'focus-fill',
'focus-line',
'foggy-fill',
'foggy-line',
'folder-2-fill',
'folder-2-line',
'folder-3-fill',
'folder-3-line',
'folder-4-fill',
'folder-4-line',
'folder-5-fill',
'folder-5-line',
'folder-add-fill',
'folder-add-line',
'folder-chart-2-fill',
'folder-chart-2-line',
'folder-chart-fill',
'folder-chart-line',
'folder-download-fill',
'folder-download-line',
'folder-fill',
'folder-forbid-fill',
'folder-forbid-line',
'folder-history-fill',
'folder-history-line',
'folder-info-fill',
'folder-info-line',
'folder-keyhole-fill',
'folder-keyhole-line',
'folder-line',
'folder-lock-fill',
'folder-lock-line',
'folder-music-fill',
'folder-music-line',
'folder-open-fill',
'folder-open-line',
'folder-received-fill',
'folder-received-line',
'folder-reduce-fill',
'folder-reduce-line',
'folder-settings-fill',
'folder-settings-line',
'folder-shared-fill',
'folder-shared-line',
'folder-shield-2-fill',
'folder-shield-2-line',
'folder-shield-fill',
'folder-shield-line',
'folder-transfer-fill',
'folder-transfer-line',
'folder-unknow-fill',
'folder-unknow-line',
'folder-upload-fill',
'folder-upload-line',
'folder-user-fill',
'folder-user-line',
'folder-warning-fill',
'folder-warning-line',
'folder-zip-fill',
'folder-zip-line',
'folders-fill',
'folders-line',
'font-color',
'font-size-2',
'font-size',
'football-fill',
'football-line',
'footprint-fill',
'footprint-line',
'forbid-2-fill',
'forbid-2-line',
'forbid-fill',
'forbid-line',
'format-clear',
'fridge-fill',
'fridge-line',
'fullscreen-exit-fill',
'fullscreen-exit-line',
'fullscreen-fill',
'fullscreen-line',
'function-fill',
'function-line',
'functions',
'funds-box-fill',
'funds-box-line',
'funds-fill',
'funds-line',
'gallery-fill',
'gallery-line',
'gallery-upload-fill',
'gallery-upload-line',
'game-fill',
'game-line',
'gamepad-fill',
'gamepad-line',
'gas-station-fill',
'gas-station-line',
'gatsby-fill',
'gatsby-line',
'genderless-fill',
'genderless-line',
'ghost-2-fill',
'ghost-2-line',
'ghost-fill',
'ghost-line',
'ghost-smile-fill',
'ghost-smile-line',
'gift-2-fill',
'gift-2-line',
'gift-fill',
'gift-line',
'git-branch-fill',
'git-branch-line',
'git-commit-fill',
'git-commit-line',
'git-merge-fill',
'git-merge-line',
'git-pull-request-fill',
'git-pull-request-line',
'git-repository-commits-fill',
'git-repository-commits-line',
'git-repository-fill',
'git-repository-line',
'git-repository-private-fill',
'git-repository-private-line',
'github-fill',
'github-line',
'gitlab-fill',
'gitlab-line',
'global-fill',
'global-line',
'globe-fill',
'globe-line',
'goblet-fill',
'goblet-line',
'google-fill',
'google-line',
'google-play-fill',
'google-play-line',
'government-fill',
'government-line',
'gps-fill',
'gps-line',
'gradienter-fill',
'gradienter-line',
'grid-fill',
'grid-line',
'group-2-fill',
'group-2-line',
'group-fill',
'group-line',
'guide-fill',
'guide-line',
'h-1',
'h-2',
'h-3',
'h-4',
'h-5',
'h-6',
'hail-fill',
'hail-line',
'hammer-fill',
'hammer-line',
'hand-coin-fill',
'hand-coin-line',
'hand-heart-fill',
'hand-heart-line',
'hand-sanitizer-fill',
'hand-sanitizer-line',
'handbag-fill',
'handbag-line',
'hard-drive-2-fill',
'hard-drive-2-line',
'hard-drive-fill',
'hard-drive-line',
'hashtag',
'haze-2-fill',
'haze-2-line',
'haze-fill',
'haze-line',
'hd-fill',
'hd-line',
'heading',
'headphone-fill',
'headphone-line',
'health-book-fill',
'health-book-line',
'heart-2-fill',
'heart-2-line',
'heart-3-fill',
'heart-3-line',
'heart-add-fill',
'heart-add-line',
'heart-fill',
'heart-line',
'heart-pulse-fill',
'heart-pulse-line',
'hearts-fill',
'hearts-line',
'heavy-showers-fill',
'heavy-showers-line',
'history-fill',
'history-line',
'home-2-fill',
'home-2-line',
'home-3-fill',
'home-3-line',
'home-4-fill',
'home-4-line',
'home-5-fill',
'home-5-line',
'home-6-fill',
'home-6-line',
'home-7-fill',
'home-7-line',
'home-8-fill',
'home-8-line',
'home-fill',
'home-gear-fill',
'home-gear-line',
'home-heart-fill',
'home-heart-line',
'home-line',
'home-smile-2-fill',
'home-smile-2-line',
'home-smile-fill',
'home-smile-line',
'home-wifi-fill',
'home-wifi-line',
'honor-of-kings-fill',
'honor-of-kings-line',
'honour-fill',
'honour-line',
'hospital-fill',
'hospital-line',
'hotel-bed-fill',
'hotel-bed-line',
'hotel-fill',
'hotel-line',
'hotspot-fill',
'hotspot-line',
'hq-fill',
'hq-line',
'html5-fill',
'html5-line',
'ie-fill',
'ie-line',
'image-2-fill',
'image-2-line',
'image-add-fill',
'image-add-line',
'image-edit-fill',
'image-edit-line',
'image-fill',
'image-line',
'inbox-archive-fill',
'inbox-archive-line',
'inbox-fill',
'inbox-line',
'inbox-unarchive-fill',
'inbox-unarchive-line',
'increase-decrease-fill',
'increase-decrease-line',
'indent-decrease',
'indent-increase',
'indeterminate-circle-fill',
'indeterminate-circle-line',
'information-fill',
'information-line',
'infrared-thermometer-fill',
'infrared-thermometer-line',
'ink-bottle-fill',
'ink-bottle-line',
'input-cursor-move',
'input-method-fill',
'input-method-line',
'insert-column-left',
'insert-column-right',
'insert-row-bottom',
'insert-row-top',
'instagram-fill',
'instagram-line',
'install-fill',
'install-line',
'invision-fill',
'invision-line',
'italic',
'kakao-talk-fill',
'kakao-talk-line',
'key-2-fill',
'key-2-line',
'key-fill',
'key-line',
'keyboard-box-fill',
'keyboard-box-line',
'keyboard-fill',
'keyboard-line',
'keynote-fill',
'keynote-line',
'knife-blood-fill',
'knife-blood-line',
'knife-fill',
'knife-line',
'landscape-fill',
'landscape-line',
'layout-2-fill',
'layout-2-line',
'layout-3-fill',
'layout-3-line',
'layout-4-fill',
'layout-4-line',
'layout-5-fill',
'layout-5-line',
'layout-6-fill',
'layout-6-line',
'layout-bottom-2-fill',
'layout-bottom-2-line',
'layout-bottom-fill',
'layout-bottom-line',
'layout-column-fill',
'layout-column-line',
'layout-fill',
'layout-grid-fill',
'layout-grid-line',
'layout-left-2-fill',
'layout-left-2-line',
'layout-left-fill',
'layout-left-line',
'layout-line',
'layout-masonry-fill',
'layout-masonry-line',
'layout-right-2-fill',
'layout-right-2-line',
'layout-right-fill',
'layout-right-line',
'layout-row-fill',
'layout-row-line',
'layout-top-2-fill',
'layout-top-2-line',
'layout-top-fill',
'layout-top-line',
'leaf-fill',
'leaf-line',
'lifebuoy-fill',
'lifebuoy-line',
'lightbulb-fill',
'lightbulb-flash-fill',
'lightbulb-flash-line',
'lightbulb-line',
'line-chart-fill',
'line-chart-line',
'line-fill',
'line-height',
'line-line',
'link-m',
'link-unlink-m',
'link-unlink',
'link',
'linkedin-box-fill',
'linkedin-box-line',
'linkedin-fill',
'linkedin-line',
'links-fill',
'links-line',
'list-check-2',
'list-check',
'list-ordered',
'list-settings-fill',
'list-settings-line',
'list-unordered',
'live-fill',
'live-line',
'loader-2-fill',
'loader-2-line',
'loader-3-fill',
'loader-3-line',
'loader-4-fill',
'loader-4-line',
'loader-5-fill',
'loader-5-line',
'loader-fill',
'loader-line',
'lock-2-fill',
'lock-2-line',
'lock-fill',
'lock-line',
'lock-password-fill',
'lock-password-line',
'lock-unlock-fill',
'lock-unlock-line',
'login-box-fill',
'login-box-line',
'login-circle-fill',
'login-circle-line',
'logout-box-fill',
'logout-box-line',
'logout-box-r-fill',
'logout-box-r-line',
'logout-circle-fill',
'logout-circle-line',
'logout-circle-r-fill',
'logout-circle-r-line',
'luggage-cart-fill',
'luggage-cart-line',
'luggage-deposit-fill',
'luggage-deposit-line',
'lungs-fill',
'lungs-line',
'mac-fill',
'mac-line',
'macbook-fill',
'macbook-line',
'magic-fill',
'magic-line',
'mail-add-fill',
'mail-add-line',
'mail-check-fill',
'mail-check-line',
'mail-close-fill',
'mail-close-line',
'mail-download-fill',
'mail-download-line',
'mail-fill',
'mail-forbid-fill',
'mail-forbid-line',
'mail-line',
'mail-lock-fill',
'mail-lock-line',
'mail-open-fill',
'mail-open-line',
'mail-send-fill',
'mail-send-line',
'mail-settings-fill',
'mail-settings-line',
'mail-star-fill',
'mail-star-line',
'mail-unread-fill',
'mail-unread-line',
'mail-volume-fill',
'mail-volume-line',
'map-2-fill',
'map-2-line',
'map-fill',
'map-line',
'map-pin-2-fill',
'map-pin-2-line',
'map-pin-3-fill',
'map-pin-3-line',
'map-pin-4-fill',
'map-pin-4-line',
'map-pin-5-fill',
'map-pin-5-line',
'map-pin-add-fill',
'map-pin-add-line',
'map-pin-fill',
'map-pin-line',
'map-pin-range-fill',
'map-pin-range-line',
'map-pin-time-fill',
'map-pin-time-line',
'map-pin-user-fill',
'map-pin-user-line',
'mark-pen-fill',
'mark-pen-line',
'markdown-fill',
'markdown-line',
'markup-fill',
'markup-line',
'mastercard-fill',
'mastercard-line',
'mastodon-fill',
'mastodon-line',
'medal-2-fill',
'medal-2-line',
'medal-fill',
'medal-line',
'medicine-bottle-fill',
'medicine-bottle-line',
'medium-fill',
'medium-line',
'men-fill',
'men-line',
'mental-health-fill',
'mental-health-line',
'menu-2-fill',
'menu-2-line',
'menu-3-fill',
'menu-3-line',
'menu-4-fill',
'menu-4-line',
'menu-5-fill',
'menu-5-line',
'menu-add-fill',
'menu-add-line',
'menu-fill',
'menu-fold-fill',
'menu-fold-line',
'menu-line',
'menu-unfold-fill',
'menu-unfold-line',
'merge-cells-horizontal',
'merge-cells-vertical',
'message-2-fill',
'message-2-line',
'message-3-fill',
'message-3-line',
'message-fill',
'message-line',
'messenger-fill',
'messenger-line',
'meteor-fill',
'meteor-line',
'mic-2-fill',
'mic-2-line',
'mic-fill',
'mic-line',
'mic-off-fill',
'mic-off-line',
'mickey-fill',
'mickey-line',
'microscope-fill',
'microscope-line',
'microsoft-fill',
'microsoft-line',
'mind-map',
'mini-program-fill',
'mini-program-line',
'mist-fill',
'mist-line',
'money-cny-box-fill',
'money-cny-box-line',
'money-cny-circle-fill',
'money-cny-circle-line',
'money-dollar-box-fill',
'money-dollar-box-line',
'money-dollar-circle-fill',
'money-dollar-circle-line',
'money-euro-box-fill',
'money-euro-box-line',
'money-euro-circle-fill',
'money-euro-circle-line',
'money-pound-box-fill',
'money-pound-box-line',
'money-pound-circle-fill',
'money-pound-circle-line',
'moon-clear-fill',
'moon-clear-line',
'moon-cloudy-fill',
'moon-cloudy-line',
'moon-fill',
'moon-foggy-fill',
'moon-foggy-line',
'moon-line',
'more-2-fill',
'more-2-line',
'more-fill',
'more-line',
'motorbike-fill',
'motorbike-line',
'mouse-fill',
'mouse-line',
'movie-2-fill',
'movie-2-line',
'movie-fill',
'movie-line',
'music-2-fill',
'music-2-line',
'music-fill',
'music-line',
'mv-fill',
'mv-line',
'navigation-fill',
'navigation-line',
'netease-cloud-music-fill',
'netease-cloud-music-line',
'netflix-fill',
'netflix-line',
'newspaper-fill',
'newspaper-line',
'node-tree',
'notification-2-fill',
'notification-2-line',
'notification-3-fill',
'notification-3-line',
'notification-4-fill',
'notification-4-line',
'notification-badge-fill',
'notification-badge-line',
'notification-fill',
'notification-line',
'notification-off-fill',
'notification-off-line',
'npmjs-fill',
'npmjs-line',
'number-0',
'number-1',
'number-2',
'number-3',
'number-4',
'number-5',
'number-6',
'number-7',
'number-8',
'number-9',
'numbers-fill',
'numbers-line',
'nurse-fill',
'nurse-line',
'oil-fill',
'oil-line',
'omega',
'open-arm-fill',
'open-arm-line',
'open-source-fill',
'open-source-line',
'opera-fill',
'opera-line',
'order-play-fill',
'order-play-line',
'organization-chart',
'outlet-2-fill',
'outlet-2-line',
'outlet-fill',
'outlet-line',
'page-separator',
'pages-fill',
'pages-line',
'paint-brush-fill',
'paint-brush-line',
'paint-fill',
'paint-line',
'palette-fill',
'palette-line',
'pantone-fill',
'pantone-line',
'paragraph',
'parent-fill',
'parent-line',
'parentheses-fill',
'parentheses-line',
'parking-box-fill',
'parking-box-line',
'parking-fill',
'parking-line',
'passport-fill',
'passport-line',
'patreon-fill',
'patreon-line',
'pause-circle-fill',
'pause-circle-line',
'pause-fill',
'pause-line',
'pause-mini-fill',
'pause-mini-line',
'paypal-fill',
'paypal-line',
'pen-nib-fill',
'pen-nib-line',
'pencil-fill',
'pencil-line',
'pencil-ruler-2-fill',
'pencil-ruler-2-line',
'pencil-ruler-fill',
'pencil-ruler-line',
'percent-fill',
'percent-line',
'phone-camera-fill',
'phone-camera-line',
'phone-fill',
'phone-find-fill',
'phone-find-line',
'phone-line',
'phone-lock-fill',
'phone-lock-line',
'picture-in-picture-2-fill',
'picture-in-picture-2-line',
'picture-in-picture-exit-fill',
'picture-in-picture-exit-line',
'picture-in-picture-fill',
'picture-in-picture-line',
'pie-chart-2-fill',
'pie-chart-2-line',
'pie-chart-box-fill',
'pie-chart-box-line',
'pie-chart-fill',
'pie-chart-line',
'pin-distance-fill',
'pin-distance-line',
'ping-pong-fill',
'ping-pong-line',
'pinterest-fill',
'pinterest-line',
'pinyin-input',
'pixelfed-fill',
'pixelfed-line',
'plane-fill',
'plane-line',
'plant-fill',
'plant-line',
'play-circle-fill',
'play-circle-line',
'play-fill',
'play-line',
'play-list-2-fill',
'play-list-2-line',
'play-list-add-fill',
'play-list-add-line',
'play-list-fill',
'play-list-line',
'play-mini-fill',
'play-mini-line',
'playstation-fill',
'playstation-line',
'plug-2-fill',
'plug-2-line',
'plug-fill',
'plug-line',
'polaroid-2-fill',
'polaroid-2-line',
'polaroid-fill',
'polaroid-line',
'police-car-fill',
'police-car-line',
'price-tag-2-fill',
'price-tag-2-line',
'price-tag-3-fill',
'price-tag-3-line',
'price-tag-fill',
'price-tag-line',
'printer-cloud-fill',
'printer-cloud-line',
'printer-fill',
'printer-line',
'product-hunt-fill',
'product-hunt-line',
'profile-fill',
'profile-line',
'projector-2-fill',
'projector-2-line',
'projector-fill',
'projector-line',
'psychotherapy-fill',
'psychotherapy-line',
'pulse-fill',
'pulse-line',
'pushpin-2-fill',
'pushpin-2-line',
'pushpin-fill',
'pushpin-line',
'qq-fill',
'qq-line',
'qr-code-fill',
'qr-code-line',
'qr-scan-2-fill',
'qr-scan-2-line',
'qr-scan-fill',
'qr-scan-line',
'question-answer-fill',
'question-answer-line',
'question-fill',
'question-line',
'question-mark',
'questionnaire-fill',
'questionnaire-line',
'quill-pen-fill',
'quill-pen-line',
'radar-fill',
'radar-line',
'radio-2-fill',
'radio-2-line',
'radio-button-fill',
'radio-button-line',
'radio-fill',
'radio-line',
'rainbow-fill',
'rainbow-line',
'rainy-fill',
'rainy-line',
'reactjs-fill',
'reactjs-line',
'record-circle-fill',
'record-circle-line',
'record-mail-fill',
'record-mail-line',
'recycle-fill',
'recycle-line',
'red-packet-fill',
'red-packet-line',
'reddit-fill',
'reddit-line',
'refresh-fill',
'refresh-line',
'refund-2-fill',
'refund-2-line',
'refund-fill',
'refund-line',
'registered-fill',
'registered-line',
'remixicon-fill',
'remixicon-line',
'remote-control-2-fill',
'remote-control-2-line',
'remote-control-fill',
'remote-control-line',
'repeat-2-fill',
'repeat-2-line',
'repeat-fill',
'repeat-line',
'repeat-one-fill',
'repeat-one-line',
'reply-all-fill',
'reply-all-line',
'reply-fill',
'reply-line',
'reserved-fill',
'reserved-line',
'rest-time-fill',
'rest-time-line',
'restart-fill',
'restart-line',
'restaurant-2-fill',
'restaurant-2-line',
'restaurant-fill',
'restaurant-line',
'rewind-fill',
'rewind-line',
'rewind-mini-fill',
'rewind-mini-line',
'rhythm-fill',
'rhythm-line',
'riding-fill',
'riding-line',
'road-map-fill',
'road-map-line',
'roadster-fill',
'roadster-line',
'robot-fill',
'robot-line',
'rocket-2-fill',
'rocket-2-line',
'rocket-fill',
'rocket-line',
'rotate-lock-fill',
'rotate-lock-line',
'rounded-corner',
'route-fill',
'route-line',
'router-fill',
'router-line',
'rss-fill',
'rss-line',
'ruler-2-fill',
'ruler-2-line',
'ruler-fill',
'ruler-line',
'run-fill',
'run-line',
'safari-fill',
'safari-line',
'safe-2-fill',
'safe-2-line',
'safe-fill',
'safe-line',
'sailboat-fill',
'sailboat-line',
'save-2-fill',
'save-2-line',
'save-3-fill',
'save-3-line',
'save-fill',
'save-line',
'scales-2-fill',
'scales-2-line',
'scales-3-fill',
'scales-3-line',
'scales-fill',
'scales-line',
'scan-2-fill',
'scan-2-line',
'scan-fill',
'scan-line',
'scissors-2-fill',
'scissors-2-line',
'scissors-cut-fill',
'scissors-cut-line',
'scissors-fill',
'scissors-line',
'screenshot-2-fill',
'screenshot-2-line',
'screenshot-fill',
'screenshot-line',
'sd-card-fill',
'sd-card-line',
'sd-card-mini-fill',
'sd-card-mini-line',
'search-2-fill',
'search-2-line',
'search-eye-fill',
'search-eye-line',
'search-fill',
'search-line',
'secure-payment-fill',
'secure-payment-line',
'seedling-fill',
'seedling-line',
'send-backward',
'send-plane-2-fill',
'send-plane-2-line',
'send-plane-fill',
'send-plane-line',
'send-to-back',
'sensor-fill',
'sensor-line',
'separator',
'server-fill',
'server-line',
'service-fill',
'service-line',
'settings-2-fill',
'settings-2-line',
'settings-3-fill',
'settings-3-line',
'settings-4-fill',
'settings-4-line',
'settings-5-fill',
'settings-5-line',
'settings-6-fill',
'settings-6-line',
'settings-fill',
'settings-line',
'shape-2-fill',
'shape-2-line',
'shape-fill',
'shape-line',
'share-box-fill',
'share-box-line',
'share-circle-fill',
'share-circle-line',
'share-fill',
'share-forward-2-fill',
'share-forward-2-line',
'share-forward-box-fill',
'share-forward-box-line',
'share-forward-fill',
'share-forward-line',
'share-line',
'shield-check-fill',
'shield-check-line',
'shield-cross-fill',
'shield-cross-line',
'shield-fill',
'shield-flash-fill',
'shield-flash-line',
'shield-keyhole-fill',
'shield-keyhole-line',
'shield-line',
'shield-star-fill',
'shield-star-line',
'shield-user-fill',
'shield-user-line',
'ship-2-fill',
'ship-2-line',
'ship-fill',
'ship-line',
'shirt-fill',
'shirt-line',
'shopping-bag-2-fill',
'shopping-bag-2-line',
'shopping-bag-3-fill',
'shopping-bag-3-line',
'shopping-bag-fill',
'shopping-bag-line',
'shopping-basket-2-fill',
'shopping-basket-2-line',
'shopping-basket-fill',
'shopping-basket-line',
'shopping-cart-2-fill',
'shopping-cart-2-line',
'shopping-cart-fill',
'shopping-cart-line',
'showers-fill',
'showers-line',
'shuffle-fill',
'shuffle-line',
'shut-down-fill',
'shut-down-line',
'side-bar-fill',
'side-bar-line',
'signal-tower-fill',
'signal-tower-line',
'signal-wifi-1-fill',
'signal-wifi-1-line',
'signal-wifi-2-fill',
'signal-wifi-2-line',
'signal-wifi-3-fill',
'signal-wifi-3-line',
'signal-wifi-error-fill',
'signal-wifi-error-line',
'signal-wifi-fill',
'signal-wifi-line',
'signal-wifi-off-fill',
'signal-wifi-off-line',
'sim-card-2-fill',
'sim-card-2-line',
'sim-card-fill',
'sim-card-line',
'single-quotes-l',
'single-quotes-r',
'sip-fill',
'sip-line',
'skip-back-fill',
'skip-back-line',
'skip-back-mini-fill',
'skip-back-mini-line',
'skip-forward-fill',
'skip-forward-line',
'skip-forward-mini-fill',
'skip-forward-mini-line',
'skull-2-fill',
'skull-2-line',
'skull-fill',
'skull-line',
'skype-fill',
'skype-line',
'slack-fill',
'slack-line',
'slice-fill',
'slice-line',
'slideshow-2-fill',
'slideshow-2-line',
'slideshow-3-fill',
'slideshow-3-line',
'slideshow-4-fill',
'slideshow-4-line',
'slideshow-fill',
'slideshow-line',
'smartphone-fill',
'smartphone-line',
'snapchat-fill',
'snapchat-line',
'snowy-fill',
'snowy-line',
'sort-asc',
'sort-desc',
'sound-module-fill',
'sound-module-line',
'soundcloud-fill',
'soundcloud-line',
'space-ship-fill',
'space-ship-line',
'space',
'spam-2-fill',
'spam-2-line',
'spam-3-fill',
'spam-3-line',
'spam-fill',
'spam-line',
'speaker-2-fill',
'speaker-2-line',
'speaker-3-fill',
'speaker-3-line',
'speaker-fill',
'speaker-line',
'spectrum-fill',
'spectrum-line',
'speed-fill',
'speed-line',
'speed-mini-fill',
'speed-mini-line',
'split-cells-horizontal',
'split-cells-vertical',
'spotify-fill',
'spotify-line',
'spy-fill',
'spy-line',
'stack-fill',
'stack-line',
'stack-overflow-fill',
'stack-overflow-line',
'stackshare-fill',
'stackshare-line',
'star-fill',
'star-half-fill',
'star-half-line',
'star-half-s-fill',
'star-half-s-line',
'star-line',
'star-s-fill',
'star-s-line',
'star-smile-fill',
'star-smile-line',
'steam-fill',
'steam-line',
'steering-2-fill',
'steering-2-line',
'steering-fill',
'steering-line',
'stethoscope-fill',
'stethoscope-line',
'sticky-note-2-fill',
'sticky-note-2-line',
'sticky-note-fill',
'sticky-note-line',
'stock-fill',
'stock-line',
'stop-circle-fill',
'stop-circle-line',
'stop-fill',
'stop-line',
'stop-mini-fill',
'stop-mini-line',
'store-2-fill',
'store-2-line',
'store-3-fill',
'store-3-line',
'store-fill',
'store-line',
'strikethrough-2',
'strikethrough',
'subscript-2',
'subscript',
'subtract-fill',
'subtract-line',
'subway-fill',
'subway-line',
'subway-wifi-fill',
'subway-wifi-line',
'suitcase-2-fill',
'suitcase-2-line',
'suitcase-3-fill',
'suitcase-3-line',
'suitcase-fill',
'suitcase-line',
'sun-cloudy-fill',
'sun-cloudy-line',
'sun-fill',
'sun-foggy-fill',
'sun-foggy-line',
'sun-line',
'superscript-2',
'superscript',
'surgical-mask-fill',
'surgical-mask-line',
'surround-sound-fill',
'surround-sound-line',
'survey-fill',
'survey-line',
'swap-box-fill',
'swap-box-line',
'swap-fill',
'swap-line',
'switch-fill',
'switch-line',
'sword-fill',
'sword-line',
'syringe-fill',
'syringe-line',
't-box-fill',
't-box-line',
't-shirt-2-fill',
't-shirt-2-line',
't-shirt-air-fill',
't-shirt-air-line',
't-shirt-fill',
't-shirt-line',
'table-2',
'table-alt-fill',
'table-alt-line',
'table-fill',
'table-line',
'tablet-fill',
'tablet-line',
'takeaway-fill',
'takeaway-line',
'taobao-fill',
'taobao-line',
'tape-fill',
'tape-line',
'task-fill',
'task-line',
'taxi-fill',
'taxi-line',
'taxi-wifi-fill',
'taxi-wifi-line',
'team-fill',
'team-line',
'telegram-fill',
'telegram-line',
'temp-cold-fill',
'temp-cold-line',
'temp-hot-fill',
'temp-hot-line',
'terminal-box-fill',
'terminal-box-line',
'terminal-fill',
'terminal-line',
'terminal-window-fill',
'terminal-window-line',
'test-tube-fill',
'test-tube-line',
'text-direction-l',
'text-direction-r',
'text-spacing',
'text-wrap',
'text',
'thermometer-fill',
'thermometer-line',
'thumb-down-fill',
'thumb-down-line',
'thumb-up-fill',
'thumb-up-line',
'thunderstorms-fill',
'thunderstorms-line',
'ticket-2-fill',
'ticket-2-line',
'ticket-fill',
'ticket-line',
'time-fill',
'time-line',
'timer-2-fill',
'timer-2-line',
'timer-fill',
'timer-flash-fill',
'timer-flash-line',
'timer-line',
'todo-fill',
'todo-line',
'toggle-fill',
'toggle-line',
'tools-fill',
'tools-line',
'tornado-fill',
'tornado-line',
'trademark-fill',
'trademark-line',
'traffic-light-fill',
'traffic-light-line',
'train-fill',
'train-line',
'train-wifi-fill',
'train-wifi-line',
'translate-2',
'translate',
'travesti-fill',
'travesti-line',
'treasure-map-fill',
'treasure-map-line',
'trello-fill',
'trello-line',
'trophy-fill',
'trophy-line',
'truck-fill',
'truck-line',
'tumblr-fill',
'tumblr-line',
'tv-2-fill',
'tv-2-line',
'tv-fill',
'tv-line',
'twitch-fill',
'twitch-line',
'twitter-fill',
'twitter-line',
'typhoon-fill',
'typhoon-line',
'u-disk-fill',
'u-disk-line',
'ubuntu-fill',
'ubuntu-line',
'umbrella-fill',
'umbrella-line',
'underline',
'uninstall-fill',
'uninstall-line',
'unsplash-fill',
'unsplash-line',
'upload-2-fill',
'upload-2-line',
'upload-cloud-2-fill',
'upload-cloud-2-line',
'upload-cloud-fill',
'upload-cloud-line',
'upload-fill',
'upload-line',
'usb-fill',
'usb-line',
'user-2-fill',
'user-2-line',
'user-3-fill',
'user-3-line',
'user-4-fill',
'user-4-line',
'user-5-fill',
'user-5-line',
'user-6-fill',
'user-6-line',
'user-add-fill',
'user-add-line',
'user-fill',
'user-follow-fill',
'user-follow-line',
'user-heart-fill',
'user-heart-line',
'user-line',
'user-location-fill',
'user-location-line',
'user-received-2-fill',
'user-received-2-line',
'user-received-fill',
'user-received-line',
'user-search-fill',
'user-search-line',
'user-settings-fill',
'user-settings-line',
'user-shared-2-fill',
'user-shared-2-line',
'user-shared-fill',
'user-shared-line',
'user-smile-fill',
'user-smile-line',
'user-star-fill',
'user-star-line',
'user-unfollow-fill',
'user-unfollow-line',
'user-voice-fill',
'user-voice-line',
'video-add-fill',
'video-add-line',
'video-chat-fill',
'video-chat-line',
'video-download-fill',
'video-download-line',
'video-fill',
'video-line',
'video-upload-fill',
'video-upload-line',
'vidicon-2-fill',
'vidicon-2-line',
'vidicon-fill',
'vidicon-line',
'vimeo-fill',
'vimeo-line',
'vip-crown-2-fill',
'vip-crown-2-line',
'vip-crown-fill',
'vip-crown-line',
'vip-diamond-fill',
'vip-diamond-line',
'vip-fill',
'vip-line',
'virus-fill',
'virus-line',
'visa-fill',
'visa-line',
'voice-recognition-fill',
'voice-recognition-line',
'voiceprint-fill',
'voiceprint-line',
'volume-down-fill',
'volume-down-line',
'volume-mute-fill',
'volume-mute-line',
'volume-off-vibrate-fill',
'volume-off-vibrate-line',
'volume-up-fill',
'volume-up-line',
'volume-vibrate-fill',
'volume-vibrate-line',
'vuejs-fill',
'vuejs-line',
'walk-fill',
'walk-line',
'wallet-2-fill',
'wallet-2-line',
'wallet-3-fill',
'wallet-3-line',
'wallet-fill',
'wallet-line',
'water-flash-fill',
'water-flash-line',
'webcam-fill',
'webcam-line',
'wechat-2-fill',
'wechat-2-line',
'wechat-fill',
'wechat-line',
'wechat-pay-fill',
'wechat-pay-line',
'weibo-fill',
'weibo-line',
'whatsapp-fill',
'whatsapp-line',
'wheelchair-fill',
'wheelchair-line',
'wifi-fill',
'wifi-line',
'wifi-off-fill',
'wifi-off-line',
'window-2-fill',
'window-2-line',
'window-fill',
'window-line',
'windows-fill',
'windows-line',
'windy-fill',
'windy-line',
'wireless-charging-fill',
'wireless-charging-line',
'women-fill',
'women-line',
'wubi-input',
'xbox-fill',
'xbox-line',
'xing-fill',
'xing-line',
'youtube-fill',
'youtube-line',
'zcool-fill',
'zcool-line',
'zhihu-fill',
'zhihu-line',
'zoom-in-fill',
'zoom-in-line',
'zoom-out-fill',
'zoom-out-line',
'zzz-fill',
'zzz-line',
'arrow-down-double-fill',
'arrow-down-double-line',
'arrow-left-double-fill',
'arrow-left-double-line',
'arrow-right-double-fill',
'arrow-right-double-line',
'arrow-turn-back-fill',
'arrow-turn-back-line',
'arrow-turn-forward-fill',
'arrow-turn-forward-line',
'arrow-up-double-fill',
'arrow-up-double-line',
'bard-fill',
'bard-line',
'bootstrap-fill',
'bootstrap-line',
'box-1-fill',
'box-1-line',
'box-2-fill',
'box-2-line',
'box-3-fill',
'box-3-line',
'brain-fill',
'brain-line',
'candle-fill',
'candle-line',
'cash-fill',
'cash-line',
'contract-left-fill',
'contract-left-line',
'contract-left-right-fill',
'contract-left-right-line',
'contract-right-fill',
'contract-right-line',
'contract-up-down-fill',
'contract-up-down-line',
'copilot-fill',
'copilot-line',
'corner-down-left-fill',
'corner-down-left-line',
'corner-down-right-fill',
'corner-down-right-line',
'corner-left-down-fill',
'corner-left-down-line',
'corner-left-up-fill',
'corner-left-up-line',
'corner-right-down-fill',
'corner-right-down-line',
'corner-right-up-fill',
'corner-right-up-line',
'corner-up-left-double-fill',
'corner-up-left-double-line',
'corner-up-left-fill',
'corner-up-left-line',
'corner-up-right-double-fill',
'corner-up-right-double-line',
'corner-up-right-fill',
'corner-up-right-line',
'cross-fill',
'cross-line',
'edge-new-fill',
'edge-new-line',
'equal-fill',
'equal-line',
'expand-left-fill',
'expand-left-line',
'expand-left-right-fill',
'expand-left-right-line',
'expand-right-fill',
'expand-right-line',
'expand-up-down-fill',
'expand-up-down-line',
'flickr-fill',
'flickr-line',
'forward-10-fill',
'forward-10-line',
'forward-15-fill',
'forward-15-line',
'forward-30-fill',
'forward-30-line',
'forward-5-fill',
'forward-5-line',
'graduation-cap-fill',
'graduation-cap-line',
'home-office-fill',
'home-office-line',
'hourglass-2-fill',
'hourglass-2-line',
'hourglass-fill',
'hourglass-line',
'javascript-fill',
'javascript-line',
'loop-left-fill',
'loop-left-line',
'loop-right-fill',
'loop-right-line',
'memories-fill',
'memories-line',
'meta-fill',
'meta-line',
'microsoft-loop-fill',
'microsoft-loop-line',
'nft-fill',
'nft-line',
'notion-fill',
'notion-line',
'openai-fill',
'openai-line',
'overline',
'p2p-fill',
'p2p-line',
'presentation-fill',
'presentation-line',
'replay-10-fill',
'replay-10-line',
'replay-15-fill',
'replay-15-line',
'replay-30-fill',
'replay-30-line',
'replay-5-fill',
'replay-5-line',
'school-fill',
'school-line',
'shining-2-fill',
'shining-2-line',
'shining-fill',
'shining-line',
'sketching',
'skip-down-fill',
'skip-down-line',
'skip-left-fill',
'skip-left-line',
'skip-right-fill',
'skip-right-line',
'skip-up-fill',
'skip-up-line',
'slow-down-fill',
'slow-down-line',
'sparkling-2-fill',
'sparkling-2-line',
'sparkling-fill',
'sparkling-line',
'speak-fill',
'speak-line',
'speed-up-fill',
'speed-up-line',
'tiktok-fill',
'tiktok-line',
'token-swap-fill',
'token-swap-line',
'unpin-fill',
'unpin-line',
'wechat-channels-fill',
'wechat-channels-line',
'wordpress-fill',
'wordpress-line',
'blender-fill',
'blender-line',
'emoji-sticker-fill',
'emoji-sticker-line',
'git-close-pull-request-fill',
'git-close-pull-request-line',
'instance-fill',
'instance-line',
'megaphone-fill',
'megaphone-line',
'pass-expired-fill',
'pass-expired-line',
'pass-pending-fill',
'pass-pending-line',
'pass-valid-fill',
'pass-valid-line',
'ai-generate',
'calendar-close-fill',
'calendar-close-line',
'draggable',
'font-family',
'font-mono',
'font-sans-serif',
'font-sans',
'hard-drive-3-fill',
'hard-drive-3-line',
'kick-fill',
'kick-line',
'list-check-3',
'list-indefinite',
'list-ordered-2',
'list-radio',
'openbase-fill',
'openbase-line',
'planet-fill',
'planet-line',
'prohibited-fill',
'prohibited-line',
'quote-text',
'seo-fill',
'seo-line',
'slash-commands',
'archive-2-fill',
'archive-2-line',
'inbox-2-fill',
'inbox-2-line',
'shake-hands-fill',
'shake-hands-line',
'supabase-fill',
'supabase-line',
'water-percent-fill',
'water-percent-line',
'yuque-fill',
'yuque-line',
'crosshair-2-fill',
'crosshair-2-line',
'crosshair-fill',
'crosshair-line',
'file-close-fill',
'file-close-line',
'infinity-fill',
'infinity-line',
'rfid-fill',
'rfid-line',
'slash-commands-2',
'user-forbid-fill',
'user-forbid-line',
'beer-fill',
'beer-line',
'circle-fill',
'circle-line',
'dropdown-list',
'file-image-fill',
'file-image-line',
'file-pdf-2-fill',
'file-pdf-2-line',
'file-video-fill',
'file-video-line',
'folder-image-fill',
'folder-image-line',
'folder-video-fill',
'folder-video-line',
'hexagon-fill',
'hexagon-line',
'menu-search-fill',
'menu-search-line',
'octagon-fill',
'octagon-line',
'pentagon-fill',
'pentagon-line',
'rectangle-fill',
'rectangle-line',
'robot-2-fill',
'robot-2-line',
'shapes-fill',
'shapes-line',
'square-fill',
'square-line',
'tent-fill',
'tent-line',
'threads-fill',
'threads-line',
'tree-fill',
'tree-line',
'triangle-fill',
'triangle-line',
'twitter-x-fill',
'twitter-x-line',
'verified-badge-fill',
'verified-badge-line',
'armchair-fill',
'armchair-line',
'bnb-fill',
'bnb-line',
'bread-fill',
'bread-line',
'btc-fill',
'btc-line',
'calendar-schedule-fill',
'calendar-schedule-line',
'dice-1-fill',
'dice-1-line',
'dice-2-fill',
'dice-2-line',
'dice-3-fill',
'dice-3-line',
'dice-4-fill',
'dice-4-line',
'dice-5-fill',
'dice-5-line',
'dice-6-fill',
'dice-6-line',
'dice-fill',
'dice-line',
'drinks-fill',
'drinks-line',
'equalizer-2-fill',
'equalizer-2-line',
'equalizer-3-fill',
'equalizer-3-line',
'eth-fill',
'eth-line',
'flower-fill',
'flower-line',
'glasses-2-fill',
'glasses-2-line',
'glasses-fill',
'glasses-line',
'goggles-fill',
'goggles-line',
'image-circle-fill',
'image-circle-line',
'info-i',
'money-rupee-circle-fill',
'money-rupee-circle-line',
'news-fill',
'news-line',
'robot-3-fill',
'robot-3-line',
'share-2-fill',
'share-2-line',
'sofa-fill',
'sofa-line',
'svelte-fill',
'svelte-line',
'vk-fill',
'vk-line',
'xrp-fill',
'xrp-line',
'xtz-fill',
'xtz-line',
'archive-stack-fill',
'archive-stack-line',
'bowl-fill',
'bowl-line',
'calendar-view',
'carousel-view',
'code-block',
'color-filter-fill',
'color-filter-line',
'contacts-book-3-fill',
'contacts-book-3-line',
'contract-fill',
'contract-line',
'drinks-2-fill',
'drinks-2-line',
'export-fill',
'export-line',
'file-check-fill',
'file-check-line',
'focus-mode',
'folder-6-fill',
'folder-6-line',
'folder-check-fill',
'folder-check-line',
'folder-close-fill',
'folder-close-line',
'folder-cloud-fill',
'folder-cloud-line',
'gallery-view-2',
'gallery-view',
'hand',
'import-fill',
'import-line',
'information-2-fill',
'information-2-line',
'kanban-view-2',
'kanban-view',
'list-view',
'lock-star-fill',
'lock-star-line',
'puzzle-2-fill',
'puzzle-2-line',
'puzzle-fill',
'puzzle-line',
'ram-2-fill',
'ram-2-line',
'ram-fill',
'ram-line',
'receipt-fill',
'receipt-line',
'shadow-fill',
'shadow-line',
'sidebar-fold-fill',
'sidebar-fold-line',
'sidebar-unfold-fill',
'sidebar-unfold-line',
'slideshow-view',
'sort-alphabet-asc',
'sort-alphabet-desc',
'sort-number-asc',
'sort-number-desc',
'stacked-view',
'sticky-note-add-fill',
'sticky-note-add-line',
'swap-2-fill',
'swap-2-line',
'swap-3-fill',
'swap-3-line',
'table-3',
'table-view',
'text-block',
'text-snippet',
'timeline-view',
'blogger-fill',
'blogger-line',
'chat-thread-fill',
'chat-thread-line',
'discount-percent-fill',
'discount-percent-line',
'exchange-2-fill',
'exchange-2-line',
'git-fork-fill',
'git-fork-line',
'input-field',
'progress-1-fill',
'progress-1-line',
'progress-2-fill',
'progress-2-line',
'progress-3-fill',
'progress-3-line',
'progress-4-fill',
'progress-4-line',
'progress-5-fill',
'progress-5-line',
'progress-6-fill',
'progress-6-line',
'progress-7-fill',
'progress-7-line',
'progress-8-fill',
'progress-8-line',
'remix-run-fill',
'remix-run-line',
'signpost-fill',
'signpost-line',
'time-zone-fill',
'time-zone-line',
'arrow-down-wide-fill',
'arrow-down-wide-line',
'arrow-left-wide-fill',
'arrow-left-wide-line',
'arrow-right-wide-fill',
'arrow-right-wide-line',
'arrow-up-wide-fill',
'arrow-up-wide-line',
'bluesky-fill',
'bluesky-line',
'expand-height-fill',
'expand-height-line',
'expand-width-fill',
'expand-width-line',
'forward-end-fill',
'forward-end-line',
'forward-end-mini-fill',
'forward-end-mini-line',
'friendica-fill',
'friendica-line',
'git-pr-draft-fill',
'git-pr-draft-line',
'play-reverse-fill',
'play-reverse-line',
'play-reverse-mini-fill',
'play-reverse-mini-line',
'rewind-start-fill',
'rewind-start-line',
'rewind-start-mini-fill',
'rewind-start-mini-line',
'scroll-to-bottom-fill',
'scroll-to-bottom-line',
'add-large-fill',
'add-large-line',
'aed-electrodes-fill',
'aed-electrodes-line',
'aed-fill',
'aed-line',
'alibaba-cloud-fill',
'alibaba-cloud-line',
'align-item-bottom-fill',
'align-item-bottom-line',
'align-item-horizontal-center-fill',
'align-item-horizontal-center-line',
'align-item-left-fill',
'align-item-left-line',
'align-item-right-fill',
'align-item-right-line',
'align-item-top-fill',
'align-item-top-line',
'align-item-vertical-center-fill',
'align-item-vertical-center-line',
'apps-2-add-fill',
'apps-2-add-line',
'close-large-fill',
'close-large-line',
'collapse-diagonal-2-fill',
'collapse-diagonal-2-line',
'collapse-diagonal-fill',
'collapse-diagonal-line',
'dashboard-horizontal-fill',
'dashboard-horizontal-line',
'expand-diagonal-2-fill',
'expand-diagonal-2-line',
'expand-diagonal-fill',
'expand-diagonal-line',
'firebase-fill',
'firebase-line',
'flip-horizontal-2-fill',
'flip-horizontal-2-line',
'flip-horizontal-fill',
'flip-horizontal-line',
'flip-vertical-2-fill',
'flip-vertical-2-line',
'flip-vertical-fill',
'flip-vertical-line',
'formula',
'function-add-fill',
'function-add-line',
'goblet-2-fill',
'goblet-2-line',
'golf-ball-fill',
'golf-ball-line',
'group-3-fill',
'group-3-line',
'heart-add-2-fill',
'heart-add-2-line',
'id-card-fill',
'id-card-line',
'information-off-fill',
'information-off-line',
'java-fill',
'java-line',
'layout-grid-2-fill',
'layout-grid-2-line',
'layout-horizontal-fill',
'layout-horizontal-line',
'layout-vertical-fill',
'layout-vertical-line',
'menu-fold-2-fill',
'menu-fold-2-line',
'menu-fold-3-fill',
'menu-fold-3-line',
'menu-fold-4-fill',
'menu-fold-4-line',
'menu-unfold-2-fill',
'menu-unfold-2-line',
'menu-unfold-3-fill',
'menu-unfold-3-line',
'menu-unfold-4-fill',
'menu-unfold-4-line',
'mobile-download-fill',
'mobile-download-line',
'nextjs-fill',
'nextjs-line',
'nodejs-fill',
'nodejs-line',
'pause-large-fill',
'pause-large-line',
'play-large-fill',
'play-large-line',
'play-reverse-large-fill',
'play-reverse-large-line',
'police-badge-fill',
'police-badge-line',
'prohibited-2-fill',
'prohibited-2-line',
'shopping-bag-4-fill',
'shopping-bag-4-line',
'snowflake-fill',
'snowflake-line',
'square-root',
'stop-large-fill',
'stop-large-line',
'tailwind-css-fill',
'tailwind-css-line',
'tooth-fill',
'tooth-line',
'video-off-fill',
'video-off-line',
'video-on-fill',
'video-on-line',
'webhook-fill',
'webhook-line',
'weight-fill',
'weight-line',
'book-shelf-fill',
'book-shelf-line',
'brain-2-fill',
'brain-2-line',
'chat-search-fill',
'chat-search-line',
'chat-unread-fill',
'chat-unread-line',
'collapse-horizontal-fill',
'collapse-horizontal-line',
'collapse-vertical-fill',
'collapse-vertical-line',
'dna-fill',
'dna-line',
'dropper-fill',
'dropper-line',
'expand-diagonal-s-2-fill',
'expand-diagonal-s-2-line',
'expand-diagonal-s-fill',
'expand-diagonal-s-line',
'expand-horizontal-fill',
'expand-horizontal-line',
'expand-horizontal-s-fill',
'expand-horizontal-s-line',
'expand-vertical-fill',
'expand-vertical-line',
'expand-vertical-s-fill',
'expand-vertical-s-line',
'gemini-fill',
'gemini-line',
'reset-left-fill',
'reset-left-line',
'reset-right-fill',
'reset-right-line',
'stairs-fill',
'stairs-line',
'telegram-2-fill',
'telegram-2-line',
'triangular-flag-fill',
'triangular-flag-line',
'user-minus-fill',
'user-minus-line',
]
......@@ -5,17 +5,20 @@ import { setupRouter } from '/@/router'
import { setupStore } from '/@/store'
// 全局方法
import { addDateRange, resetForm, useDict } from '/@/utils/index'
import { useDict } from './utils/dict'
import { download } from '/@/utils/download'
import { addDateRange, handleTree, resetForm } from '/@/utils/index'
// svg图标
import elementIcons from '/@/icon/elementIcon'
const app = createApp(App)
// 全局方法挂载
app.config.globalProperties.download = download
app.config.globalProperties.useDict = useDict
app.config.globalProperties.resetForm = resetForm
app.config.globalProperties.addDateRange = addDateRange
app.config.globalProperties.handleTree = handleTree
setupVab(app)
setupI18n(app)
......
......@@ -14,11 +14,19 @@
</vab-query-form-top-panel>
</vab-query-form>
</el-col>
<el-col v-for="(item, index) in queryIcon" :key="index" :span="6">
<!-- <el-col v-for="(item, index) in queryIcon" :key="index" :span="6">
<vab-card @click="handleIcon(item)">
<vab-icon :icon="item" />
</vab-card>
</el-col>
</el-col> -->
<div class="icon-layout">
<div v-for="(item, index) in queryIcon" :key="index" class="icon-item">
<vab-card @click="handleIcon(item)">
<vab-icon :icon="item" />
</vab-card>
</div>
</div>
<el-col :span="24">
<vab-pagination
:current-page="queryForm.pageNo"
......@@ -34,7 +42,8 @@
<script lang="ts" setup>
import { Search } from '@element-plus/icons-vue'
import { getIconList } from '/@/api/icon'
// import { getIconList } from '/@/api/icon'
import IconList from '/@/icon/list'
defineOptions({
name: 'VabIconSelector',
......@@ -48,7 +57,7 @@ const total = ref<number>(0)
const queryIcon = ref<any>([])
const queryForm = reactive<any>({
pageNo: 1,
pageSize: 20,
pageSize: 30,
title: '',
})
......@@ -69,9 +78,13 @@ const queryData = () => {
}
const fetchData = async () => {
const { data } = await getIconList(queryForm)
queryIcon.value = data.list
total.value = data.total
// const { data } = await getIconList(queryForm)
const { title, pageNo = 1, pageSize = 72 } = queryForm
const mockList = IconList.filter((item) => !(title && !item.includes(title)))
const list = mockList.filter((item, index) => index < pageSize * pageNo && index >= pageSize * (pageNo - 1))
queryIcon.value = list
total.value = mockList.length
}
const handleIcon = (item: any) => {
......@@ -86,7 +99,7 @@ onBeforeMount(() => {
<style lang="scss">
.icon-selector-popper {
width: 302px !important;
// width: 302px !important;
.vab-query-form {
margin-top: calc(var(--el-margin) / 2);
......@@ -117,5 +130,28 @@ onBeforeMount(() => {
.el-pagination {
margin-top: calc(0 - var(--el-margin)) !important;
}
.icon-layout {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: flex-start;
padding: 10px;
.icon-item {
.vab-card {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
cursor: pointer;
.vab-icon {
font-size: 24px;
}
}
}
}
}
</style>
......@@ -6,6 +6,7 @@ import type { RouteRecordName, RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import { authentication, base, disableRouterWarning, isHashRouterMode } from '/@/config'
import { setupPermissions } from '/@/router/permissions'
import { isHttp } from '/@/utils/validate'
import Layout from '/@vab/layouts/index.vue'
export const constantRoutes: VabRouteRecord[] = [
......@@ -247,7 +248,9 @@ const fatteningRoutes = (routes: VabRouteRecord[]): VabRouteRecord[] => {
const addRouter = (routes: VabRouteRecord[]) => {
routes.forEach((route: VabRouteRecord) => {
if (!router.hasRoute(route.name)) router.addRoute(route as RouteRecordRaw)
if (!router.hasRoute(route.name) && !isHttp(route.path)) {
router.addRoute(route as RouteRecordRaw)
}
if (route.children) addRouter(route.children)
})
}
......
export default {
200: '服务器成功返回请求数据',
201: '新建或修改数据成功',
202: '一个请求已经进入后台排队(异步任务)',
204: '删除数据成功',
205: '后端code指令强制开启锁屏',
400: '发出信息有误',
401: '用户没有权限(令牌失效、用户名、密码错误、登录过期)',
402: '令牌过期',
403: '用户得到授权,但是访问是被禁止的',
404: '访问资源不存在',
406: '请求格式不可得',
410: '请求资源被永久删除,且不会被看到',
500: '服务器发生错误',
502: '网关错误',
503: '服务不可用,服务器暂时过载或维护',
504: '网关超时',
default: '系统未知错误,请反馈给管理员',
}
import { ElLoading, ElMessage } from 'element-plus'
import { saveAs } from 'file-saver'
import CODE_MESSAGE from '/@/utils/codeMessage'
import { blobValidate, tansParams } from '/@/utils/index'
import request from '/@/utils/request'
// 通用下载方法
export function download(url: string, params: any, filename: any, config: any) {
let downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' })
return request
.post(url, params, {
transformRequest: [
(params) => {
return tansParams(params)
},
],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config,
})
.then(async (data: any) => {
const isBlob = blobValidate(data)
if (isBlob) {
const blob = new Blob([data as any])
saveAs(blob, filename)
} else {
const resText = await data.text()
const rspObj: { code: keyof typeof CODE_MESSAGE; msg: string } = JSON.parse(resText)
const errMsg = CODE_MESSAGE[rspObj.code] || rspObj.msg || CODE_MESSAGE['default']
ElMessage.error(errMsg)
}
downloadLoadingInstance.close()
})
.catch((error) => {
console.error(error)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close()
})
}
......@@ -244,3 +244,85 @@ export function parseStrEmpty(str: any) {
}
return str
}
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params: any) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName]
const part = `${encodeURIComponent(propName)}=`
if (value !== null && value !== '' && value !== undefined) {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== '' && value[key] !== undefined) {
let params = `${propName}[${key}]`
const subPart = `${encodeURIComponent(params)}=`
result += `${subPart}${encodeURIComponent(value[key])}&`
}
}
} else {
result += `${part}${encodeURIComponent(value)}&`
}
}
}
return result
}
// 验证是否为blob格式
export function blobValidate(data: any) {
return data.type !== 'application/json'
}
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data: any, id: any, parentId: any, children: any) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children',
}
const childrenListMap: any = {}
const nodeIds: any = {}
const tree = []
for (let d of data) {
let parentId = d[config.parentId]
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = []
}
nodeIds[d[config.id]] = d
childrenListMap[parentId].push(d)
}
for (let d of data) {
let parentId = d[config.parentId]
if (nodeIds[parentId] == null) {
tree.push(d)
}
}
for (let t of tree) {
adaptToChildrenList(t)
}
function adaptToChildrenList(o: any) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]]
}
if (o[config.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c)
}
}
}
return tree
}
......@@ -4,6 +4,7 @@ import { contentType, debounce, messageName, statusName, successCode, timeout }
import router from '/@/router'
import { useSettingsStore } from '/@/store/modules/settings'
import { useUserStore } from '/@/store/modules/user'
import CODE_MESSAGE from '/@/utils/codeMessage'
import { isArray } from '/@/utils/validate'
import { addErrorLog, needErrorLog } from '/@vab/plugins/errorLog'
import { gp } from '/@vab/plugins/vab'
......@@ -17,24 +18,24 @@ let requests: any[] = []
// 操作正常Code数组
const codeVerificationArray = isArray(successCode) ? [...successCode] : [successCode]
const CODE_MESSAGE: any = {
200: '服务器成功返回请求数据',
201: '新建或修改数据成功',
202: '一个请求已经进入后台排队(异步任务)',
204: '删除数据成功',
205: '后端code指令强制开启锁屏',
400: '发出信息有误',
401: '用户没有权限(令牌失效、用户名、密码错误、登录过期)',
402: '令牌过期',
403: '用户得到授权,但是访问是被禁止的',
404: '访问资源不存在',
406: '请求格式不可得',
410: '请求资源被永久删除,且不会被看到',
500: '服务器发生错误',
502: '网关错误',
503: '服务不可用,服务器暂时过载或维护',
504: '网关超时',
}
// const CODE_MESSAGE: any = {
// 200: '服务器成功返回请求数据',
// 201: '新建或修改数据成功',
// 202: '一个请求已经进入后台排队(异步任务)',
// 204: '删除数据成功',
// 205: '后端code指令强制开启锁屏',
// 400: '发出信息有误',
// 401: '用户没有权限(令牌失效、用户名、密码错误、登录过期)',
// 402: '令牌过期',
// 403: '用户得到授权,但是访问是被禁止的',
// 404: '访问资源不存在',
// 406: '请求格式不可得',
// 410: '请求资源被永久删除,且不会被看到',
// 500: '服务器发生错误',
// 502: '网关错误',
// 503: '服务不可用,服务器暂时过载或维护',
// 504: '网关超时',
// }
/**
* axios请求拦截器配置
......
......@@ -15,8 +15,10 @@ export const convertRouter = (asyncRoutes: VabRouteRecord[]) => {
if (route.component === 'Layout') route.component = () => import('/@vab/layouts/index.vue')
else {
const index = route.component.indexOf('views')
const path = index > 0 ? route.component.slice(index) : `${route.component}`
console.log(path)
let path = index > 0 ? route.component.slice(index) : `${route.component}`
if (!path.includes('.vue')) {
path += '.vue'
}
route.component = routeAllPathToCompMap[`../${path}`]
}
if (route.children && route.children.length > 0) route.children = convertRouter(route.children)
......
......@@ -206,3 +206,12 @@ export const isEnglish = (value: string) => {
const reg = /^[A-Za-z]+$/
return reg.test(value)
}
/**
* 判断url是否是http或https
* @param {string} url
* @returns {Boolean}
*/
export function isHttp(url: any) {
return url.includes('http://') || url.includes('https://')
}
......@@ -21,7 +21,7 @@
<vab-card class="auto-height-card">
<vab-query-form>
<vab-query-form-top-panel>
<el-form ref="queryRef" inline :model="queryForm" @submit.prevent>
<el-form ref="queryRef" inline label-width="70px" :model="queryForm" @submit.prevent>
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="queryForm.userName"
......@@ -56,7 +56,7 @@
/>
</el-form-item>
<el-form-item>
<el-button icon="Search" :loading="listLoading" native-type="submit" type="primary" @click="queryData">查询</el-button>
<el-button icon="Search" :loading="listLoading" native-type="submit" type="primary" @click="handleQuery">查询</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button class="hidden-xs-only" text type="primary" @click="handleFold">
<span v-if="fold">展开</span>
......@@ -66,58 +66,46 @@
</el-form-item>
</el-form>
</vab-query-form-top-panel>
<vab-query-form-left-panel :span="20">
<vab-query-form-left-panel>
<el-button icon="Plus" type="primary" @click="handleAdd">添加</el-button>
<el-button icon="Delete" type="danger" @click="handleDelete">删除</el-button>
<el-button icon="Upload" plain type="warning" @click="handleExport">导出</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel :span="4">
<vab-query-form-right-panel>
<div class="custom-table-right-tools">
<el-button @click="queryData">
<el-button @click="handleQuery">
<vab-icon icon="refresh-line" />
</el-button>
<el-button @click="clickFullScreen">
<vab-icon :icon="isFullscreen ? 'fullscreen-exit-fill' : 'fullscreen-fill'" />
</el-button>
<el-popover popper-class="custom-table-checkbox">
<template #reference>
<el-button>
<vab-icon icon="settings-line" />
</el-button>
</template>
<vab-draggable v-model="columns" :animation="600" target=".el-checkbox-group">
<el-checkbox-group v-model="checkList">
<el-checkbox
v-for="item in columns"
:key="item.label"
:disabled="item.disableCheck"
:label="item.label"
:value="item.label"
>
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</vab-draggable>
</el-popover>
</div>
</vab-query-form-right-panel>
</vab-query-form>
<el-table ref="tableRef" v-loading="listLoading" border :data="list" @selection-change="setSelectRows">
<!-- <el-table-column type="selection" width="38" />
<el-table-column align="center" label="序号" width="55">
<template #default="{ $index }">
{{ $index + 1 }}
</template>
</el-table-column>
<el-table-column align="center" label="用户编号" min-width="180" prop="userId" show-overflow-tooltip />
<el-table-column align="center" label="用户名称" min-width="120" prop="userName" />
<el-table-column align="center" label="用户昵称" min-width="120" prop="nickName" />
<el-table-column align="center" label="部门" min-width="120" prop="nickName" />
<el-table-column align="center" label="邮箱" min-width="120" prop="email" show-overflow-tooltip />
<el-table-column align="center" label="角色" min-width="155">
<template #default="{ row }">
<el-space wrap>
<el-tag v-for="(item, index) in row.roles" :key="index">
{{ item }}
</el-tag>
</el-space>
</template>
</el-table-column>
<el-table-column align="center" label="修改时间" min-width="160" prop="datetime" show-overflow-tooltip />
<el-table-column align="center" label="操作" width="150">
<template #default="{ row }">
<el-button text type="primary" @click="handleEdit(row)">编辑</el-button>
<el-button text type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty class="vab-data-empty" description="暂无数据" />
</template> -->
<el-table-column align="center" type="selection" width="50" />
<el-table-column key="userId" align="center" label="用户编号" min-width="120" prop="userId" />
<!-- <el-table-column key="userId" align="center" label="用户编号" min-width="120" prop="userId" />
<el-table-column key="userName" align="center" label="用户名称" min-width="160" prop="userName" :show-overflow-tooltip="true" />
<el-table-column key="nickName" align="center" label="用户昵称" min-width="160" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column
......@@ -134,7 +122,25 @@
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column align="center" label="创建时间" prop="createTime" width="160" />
<el-table-column align="center" label="创建时间" prop="createTime" width="160" /> -->
<el-table-column
v-for="(item, index) in finallyColumns"
:key="index"
align="center"
:fixed="item.fixed"
:label="item.label"
:min-width="item.minWidth || 160"
:prop="item.prop"
show-overflow-tooltip
:sortable="item.sortable"
>
<template #default="scope">
<span v-if="item.label === '状态'">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)" />
</span>
<span v-else>{{ scope.row[item.prop] }}</span>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" min-width="240">
<template #default="scope">
<div v-if="scope.row.userId !== 1" class="table-operation-button">
......@@ -171,7 +177,7 @@
<script lang="ts" setup>
import type { TableInstance } from 'element-plus'
import { deptTreeSelect } from '/@/api/departmentManagement'
import { doDelete, getList } from '/@/api/userManagement'
import { changeUserStatus, doDelete, getList } from '/@/api/userManagement'
defineOptions({
name: 'UserManagement',
......@@ -193,6 +199,7 @@ const isFullscreen = ref<boolean>(false)
const total = ref<number>(0)
const selectRows = ref<any>([])
const checkList = ref<any>([])
const queryForm = reactive<any>({
pageNum: 1,
pageSize: 20,
......@@ -203,63 +210,15 @@ const queryForm = reactive<any>({
dateRange: [],
})
const setSelectRows = (value: string) => {
selectRows.value = value
}
// const handleEdit = (row: any = {}) => {
// editRef.value.showEdit(row)
// }
/** 修改按钮操作 */
function handleUpdate(row: any) {
console.log(row)
// reset();
// const userId = row.userId || ids.value;
// getUser(userId).then(response => {
// form.value = response.data;
// form.value.deptId = response.data.deptId;
// postOptions.value = response.posts;
// roleOptions.value = response.roles;
// form.value.postIds = response.postIds;
// form.value.roleIds = response.roleIds;
// open.value = true;
// title.value = "修改用户";
// form.password = "";
// });
}
const handleDelete = (row: any = {}) => {
if (row.id) {
$baseConfirm('您确定要删除当前项吗', null, async () => {
const { msg }: any = await doDelete({ ids: row.id })
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
} else {
if (selectRows.value.length > 0) {
const ids = selectRows.value.map((item: { id: any }) => item.id).join(',')
$baseConfirm('您确定要删除选中项吗', null, async () => {
const { msg }: any = await doDelete({ ids })
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
} else {
$baseMessage('您未选中任何行', 'warning', 'hey')
}
}
}
const handleSizeChange = (value: number) => {
queryForm.pageNum = 1
queryForm.pageSize = value
fetchData()
}
const handleCurrentChange = (value: number) => {
queryForm.pageNum = value
fetchData()
}
const columns = ref<any>([
{ label: '用户编号', prop: 'userId', minWidth: 120, checked: true },
{ label: '用户名称', prop: 'userName', minWidth: 160, checked: true },
{ label: '部门', prop: 'deptName', minWidth: 160, checked: true },
{ label: '手机号码', prop: 'phonenumber', minWidth: 120, checked: true },
{ label: '状态', prop: 'status', minWidth: 120, checked: true },
{ label: '创建时间', prop: 'createTime', minWidth: 160, checked: true },
])
const finallyColumns = computed(() => columns.value.filter((item: any) => checkList.value.includes(item.label)))
/** 根据名称筛选部门树 */
watch(deptName, (val) => {
......@@ -280,6 +239,10 @@ onActivated(() => {
})
onBeforeMount(() => {
columns.value.forEach((item: any) => {
if (item.checked) checkList.value.push(item.label)
})
getDeptTree()
fetchData()
})
......@@ -315,20 +278,6 @@ const handleFold = () => {
fold.value = !fold.value
}
/** 搜索按钮操作 */
const queryData = () => {
queryForm.pageNum = 1
fetchData()
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm('queryRef')
queryForm.deptId = undefined
// proxy.$refs.deptTreeRef.setCurrentKey(null);
handleQuery()
}
/** 查询用户列表 */
const fetchData = async () => {
listLoading.value = true
......@@ -346,17 +295,42 @@ function handleQuery() {
fetchData()
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm('queryRef')
queryForm.deptId = undefined
proxy.$refs.deptTreeRef.setCurrentKey(null)
handleQuery()
}
const handleSizeChange = (value: number) => {
queryForm.pageNum = 1
queryForm.pageSize = value
fetchData()
}
const handleCurrentChange = (value: number) => {
queryForm.pageNum = value
fetchData()
}
const setSelectRows = (value: string) => {
selectRows.value = value
}
/** 用户状态修改 */
function handleStatusChange(row: any) {
console.log(row)
// let text = row.status === "0" ? "启用" : "停用";
// proxy.$modal.confirm('确认要"' + text + '""' + row.userName + '"用户吗?').then(function () {
// return changeUserStatus(row.userId, row.status);
// }).then(() => {
// proxy.$modal.msgSuccess(text + "成功");
// }).catch(function () {
// row.status = row.status === "0" ? "1" : "0";
// });
const text = row.status === '0' ? '启用' : '停用'
$baseConfirm(`确认要"${text}" "${row.userName}"用户吗?`, null, async () => {
return changeUserStatus(row.userId, row.status)
})
.then(() => {
proxy.$modal.msgSuccess(`${text}成功`)
})
.catch(() => {
row.status = row.status === '0' ? '1' : '0'
})
}
/** 更多操作 */
......@@ -382,6 +356,7 @@ function handleAuthRole(row: any) {
// const userId = row.userId;
// router.push("/system/user-auth/role/" + userId);
}
/** 重置密码按钮操作 */
function handleResetPwd(row: any) {
console.log(row)
......@@ -408,4 +383,42 @@ function handleResetPwd(row: any) {
const handleAdd = () => {
editRef.value.showEdit()
}
/** 修改按钮操作 */
function handleUpdate(row: any) {
editRef.value.showEdit(row)
}
/** 删除按钮操作 */
const handleDelete = (row: any = {}) => {
if (row.userId) {
$baseConfirm('您确定要删除当前项吗', null, async () => {
const { msg }: any = await doDelete(row.userId)
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
} else {
if (selectRows.value.length > 0) {
const ids = selectRows.value.map((item: { userId: any }) => item.userId)
$baseConfirm('您确定要删除选中项吗', null, async () => {
const { msg }: any = await doDelete(ids)
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
} else {
$baseMessage('您未选中任何行', 'warning', 'hey')
}
}
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
'system/user/export',
{
...queryForm,
},
`user_${Date.now()}.xlsx`
)
}
</script>
<template>
<div class="comprehensive-table-container auto-height-container" :class="{ 'fullscreen-container': isFullscreen }">
<vab-query-form>
<vab-query-form-top-panel>
<el-form ref="queryRef" inline label-width="70px" :model="queryForm" @submit.prevent>
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="queryForm.menuName" clearable placeholder="请输入菜单名称" style="width: 200px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item v-show="!fold" label="状态" prop="status">
<el-select v-model="queryForm.status" clearable placeholder="菜单状态" style="width: 200px">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button icon="Search" :loading="listLoading" native-type="submit" type="primary" @click="handleQuery">查询</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button class="hidden-xs-only" text type="primary" @click="handleFold">
<span v-if="fold">展开</span>
<span v-else>合并</span>
<vab-icon class="vab-dropdown" :class="{ 'vab-dropdown-active': fold }" icon="arrow-up-s-line" />
</el-button>
</el-form-item>
</el-form>
</vab-query-form-top-panel>
<vab-query-form-left-panel>
<el-button icon="Plus" type="primary" @click="handleAdd">添加</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel>
<div class="custom-table-right-tools">
<el-button @click="handleQuery">
<vab-icon icon="refresh-line" />
</el-button>
<el-button @click="clickFullScreen">
<vab-icon :icon="isFullscreen ? 'fullscreen-exit-fill' : 'fullscreen-fill'" />
</el-button>
</div>
</vab-query-form-right-panel>
</vab-query-form>
<el-table
ref="tableRef"
v-loading="listLoading"
border
:data="list"
row-key="menuId"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="菜单名称" prop="menuName" :show-overflow-tooltip="true" width="160" />
<el-table-column align="center" label="图标" prop="icon" width="160" />
<el-table-column label="排序" prop="orderNum" width="60" />
<el-table-column label="权限标识" prop="perms" :show-overflow-tooltip="true" />
<el-table-column label="组件路径" prop="component" :show-overflow-tooltip="true" />
<el-table-column label="状态" prop="status" width="80">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column align="center" label="创建时间" prop="createTime" width="160" />
<el-table-column align="center" fixed="right" label="操作" min-width="150">
<template #default="scope">
<el-button icon="Edit" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<el-button icon="Plus" link type="primary" @click="handleAdd(scope.row)">新增</el-button>
<el-button icon="Delete" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty class="vab-data-empty" description="暂无数据" />
</template>
</el-table>
<menu-management-edit2 ref="editRef" @fetch-data="fetchData" />
</div>
</template>
<script lang="ts" setup>
import type { TableInstance } from 'element-plus'
import { doDelete, listMenu } from '/@/api/menuManagement'
defineOptions({
name: 'MenuManagement',
})
const { proxy } = getCurrentInstance() as any
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const { exit, enter, isFullscreen: _isFullscreen } = useFullscreen()
const isFullscreen = ref<boolean>(false)
const tableRef = ref<TableInstance>()
const fold = ref<boolean>(true)
const editRef = ref<any>(null)
const list = ref<any>([])
const listLoading = ref<boolean>(true)
const queryForm = reactive<any>({
menuName: undefined,
visible: undefined,
})
watch(
_isFullscreen,
() => {
if (_isFullscreen.value) isFullscreen.value = true
else isFullscreen.value = false
},
{ immediate: true }
)
onActivated(() => {
tableRef.value?.doLayout()
})
onBeforeMount(() => {
fetchData()
})
/** 点击全屏 */
const clickFullScreen = () => {
isFullscreen.value = !isFullscreen.value
isFullscreen.value ? enter() : exit()
}
/** 搜索条件折叠 */
const handleFold = () => {
fold.value = !fold.value
}
/** 查询菜单列表 */
const fetchData = async () => {
listLoading.value = true
const pp = Object.assign({}, queryForm)
const { data } = (await listMenu(pp)) as any
list.value = proxy.handleTree(data, 'menuId')
listLoading.value = false
}
/** 搜索按钮操作 */
function handleQuery() {
fetchData()
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm('queryRef')
handleQuery()
}
/** 新增按钮操作 */
const handleAdd = (row: any = {}) => {
editRef.value.showAdd(row)
}
/** 修改按钮操作 */
const handleUpdate = (row: any = {}) => {
editRef.value.showEdit(row)
}
/** 删除按钮操作 */
const handleDelete = (row: any = {}) => {
if (row.menuId) {
$baseConfirm(`是否确认删除名称为"${row.menuName}"的数据项?`, null, async () => {
const { msg }: any = await doDelete(row.menuId)
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
}
}
</script>
<template>
<vab-dialog v-model="dialogFormVisible" append-to-body :title="title" width="830px" @close="close">
<el-form ref="formRef" inline label-width="140px" :model="form" :rules="rules">
<el-row>
<el-col :span="24">
<el-form-item label="上级菜单">
<el-tree-select
v-model="form.parentId"
check-strictly
:data="menuOptions"
placeholder="选择上级菜单"
:props="{ value: 'menuId', label: 'menuName', children: 'children' }"
value-key="menuId"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="菜单类型" prop="menuType">
<el-radio-group v-model="form.menuType">
<el-radio value="M">目录</el-radio>
<el-radio value="C">菜单</el-radio>
<el-radio value="F">按钮</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="form.menuType != 'F'" :span="12">
<el-form-item label="菜单图标" prop="icon">
<!-- <el-popover placement="bottom-start" trigger="click" :width="540">
<template #reference>
<el-input v-model="form.icon" placeholder="点击选择图标" readonly @blur="showSelectIcon">
<template #prefix>
<svg-icon v-if="form.icon" class="el-input__icon" :icon-class="form.icon" style="width: 16px; height: 32px" />
<el-icon v-else style="width: 16px; height: 32px"><search /></el-icon>
</template>
</el-input>
</template>
<icon-select ref="iconSelectRef" :active-icon="form.icon" @selected="selected" />
</el-popover> -->
<el-popover v-model:visible="visible" popper-class="icon-selector-popper" trigger="click" :width="520">
<template #reference>
<el-input v-model="form.icon" clearable />
</template>
<vab-icon-selector @handle-icon="handleIcon" />
</el-popover>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="显示排序" prop="orderNum">
<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="form.menuName" placeholder="请输入菜单名称" />
</el-form-item>
</el-col>
<el-col v-if="form.menuType == 'C'" :span="12">
<el-form-item prop="routeName">
<template #label>
<span>
<el-tooltip
content="默认不填则和路由地址相同:如地址为:`user`,则名称为`User`(注意:因为router会删除名称相同路由,为避免名字的冲突,特殊情况下请自定义,保证唯一性)"
placement="top"
>
<el-icon><question-filled /></el-icon>
</el-tooltip>
路由名称
</span>
</template>
<el-input v-model="form.routeName" placeholder="请输入路由名称" />
</el-form-item>
</el-col>
<el-col v-if="form.menuType != 'F'" :span="12">
<el-form-item>
<template #label>
<span>
<el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
是否外链
</span>
</template>
<el-radio-group v-model="form.isFrame">
<el-radio value="0"></el-radio>
<el-radio value="1"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="form.menuType != 'F'" :span="12">
<el-form-item prop="path">
<template #label>
<span>
<el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
路由地址
</span>
</template>
<el-input v-model="form.path" placeholder="请输入路由地址" />
</el-form-item>
</el-col>
<el-col v-if="form.menuType == 'C'" :span="12">
<el-form-item prop="component">
<template #label>
<span>
<el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
组件路径
</span>
</template>
<el-input v-model="form.component" placeholder="请输入组件路径" />
</el-form-item>
</el-col>
<el-col v-if="form.menuType != 'M'" :span="12">
<el-form-item>
<el-input v-model="form.perms" maxlength="100" placeholder="请输入权限标识" />
<template #label>
<span>
<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
权限字符
</span>
</template>
</el-form-item>
</el-col>
<el-col v-if="form.menuType == 'C'" :span="12">
<el-form-item>
<el-input v-model="form.query" maxlength="255" placeholder="请输入路由参数" />
<template #label>
<span>
<el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
路由参数
</span>
</template>
</el-form-item>
</el-col>
<el-col v-if="form.menuType == 'C'" :span="12">
<el-form-item>
<template #label>
<span>
<el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
是否缓存
</span>
</template>
<el-radio-group v-model="form.isCache">
<el-radio value="0">缓存</el-radio>
<el-radio value="1">不缓存</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col v-if="form.menuType != 'F'" :span="12">
<el-form-item>
<template #label>
<span>
<el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
显示状态
</span>
</template>
<el-radio-group v-model="form.visible">
<el-radio v-for="dict in sys_show_hide" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<template #label>
<span>
<el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
<el-icon><question-filled /></el-icon>
</el-tooltip>
菜单状态
</span>
</template>
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button type="primary" @click="save">确定</el-button>
<el-button @click="cancel">取 消</el-button>
</template>
</vab-dialog>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { doAdd, doEdit, getMenu, listMenu } from '/@/api/menuManagement'
defineOptions({
name: 'MenuManagementEdit2',
})
const { proxy } = getCurrentInstance() as any
const { sys_show_hide, sys_normal_disable } = proxy.useDict('sys_show_hide', 'sys_normal_disable')
const emit = defineEmits(['fetch-data'])
const visible = ref<boolean>(false)
const menuOptions = ref<any>([])
const formRef = ref<FormInstance>()
const form = ref<any>({
menuId: undefined,
parentId: 0,
menuName: undefined,
icon: undefined,
menuType: 'M',
orderNum: undefined,
isFrame: '1',
isCache: '0',
visible: '0',
status: '0',
})
const rules = reactive<any>({
menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
})
const title = ref<string>('')
const dialogFormVisible = ref<boolean>(false)
const handleIcon = (item: string) => {
form.value.icon = item
visible.value = false
}
const showAdd = (row?: any) => {
reset()
getTreeselect()
title.value = '添加'
dialogFormVisible.value = true
nextTick(() => {
if (row != null && row.menuId) {
form.value.parentId = row.menuId
} else {
form.value.parentId = 0
}
})
}
const showEdit = (row: any) => {
reset()
getTreeselect()
title.value = '编辑'
dialogFormVisible.value = true
nextTick(() => {
getMenu(row.menuId).then((response: any) => {
form.value = response.data
})
})
}
/** 查询菜单下拉树结构 */
function getTreeselect() {
menuOptions.value = []
listMenu().then((response) => {
const menu = { menuId: 0, menuName: '主类目', children: [] }
menu.children = proxy.handleTree(response.data, 'menuId')
menuOptions.value.push(menu)
})
}
/** 表单重置 */
function reset() {
form.value = {
menuId: undefined,
parentId: 0,
menuName: undefined,
icon: undefined,
menuType: 'M',
orderNum: undefined,
isFrame: '1',
isCache: '0',
visible: '0',
status: '0',
}
proxy.resetForm('formRef')
}
const close = () => {
formRef.value?.clearValidate()
formRef.value?.resetFields()
emit('fetch-data')
}
const cancel = () => {
close()
dialogFormVisible.value = false
}
const save = () => {
formRef.value?.validate(async (valid: any) => {
if (valid) {
if (form.value.menuId) {
await doEdit(form.value)
await $baseMessage('修改成功', 'success', 'hey')
} else {
await doAdd(form.value)
await $baseMessage('添加成功', 'success', 'hey')
}
cancel()
}
})
}
defineExpose({
showAdd,
showEdit,
})
</script>
<style lang="scss" scoped>
:deep() {
.el-form-item__content {
min-width: 200px;
.el-input {
width: 200px;
}
}
}
</style>
<template>
<div class="column-table-container no-background-container auto-height-container" :class="{ 'fullscreen-container': isFullscreen }">
<el-row :gutter="20">
<el-col :lg="5" :md="24" :sm="24" :xl="4" :xs="24">
<vab-card class="auto-height-card">
<el-input v-model="deptName" clearable placeholder="请输入部门名称" prefix-icon="Search" style="margin-bottom: 10px" />
<el-tree
ref="deptTreeRef"
:data="deptOptions"
default-expand-all
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
node-key="id"
:props="{ label: 'label', children: 'children' }"
@node-click="handleNodeClick"
/>
</vab-card>
</el-col>
<el-col :lg="19" :md="24" :sm="24" :xl="20" :xs="24">
<vab-card class="auto-height-card">
<vab-query-form>
<vab-query-form-top-panel>
<el-form ref="queryRef" inline label-width="70px" :model="queryForm" @submit.prevent>
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="queryForm.userName"
clearable
placeholder="请输入用户名称"
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item v-show="!fold" label="手机号码" prop="phonenumber">
<el-input
v-model="queryForm.phonenumber"
clearable
placeholder="请输入手机号码"
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item v-show="!fold" label="状态" prop="status">
<el-select v-model="queryForm.status" clearable placeholder="用户状态" style="width: 240px">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item v-show="!fold" label="创建时间" prop="dateRange" style="width: 308px">
<el-date-picker
v-model="queryForm.dateRange"
end-placeholder="结束日期"
range-separator="-"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD"
/>
</el-form-item>
<el-form-item>
<el-button icon="Search" :loading="listLoading" native-type="submit" type="primary" @click="handleQuery">查询</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
<el-button class="hidden-xs-only" text type="primary" @click="handleFold">
<span v-if="fold">展开</span>
<span v-else>合并</span>
<vab-icon class="vab-dropdown" :class="{ 'vab-dropdown-active': fold }" icon="arrow-up-s-line" />
</el-button>
</el-form-item>
</el-form>
</vab-query-form-top-panel>
<vab-query-form-left-panel>
<el-button icon="Plus" type="primary" @click="handleAdd">添加</el-button>
<el-button icon="Delete" type="danger" @click="handleDelete">删除</el-button>
<el-button icon="Upload" plain type="warning" @click="handleExport">导出</el-button>
</vab-query-form-left-panel>
<vab-query-form-right-panel>
<div class="custom-table-right-tools">
<el-button @click="handleQuery">
<vab-icon icon="refresh-line" />
</el-button>
<el-button @click="clickFullScreen">
<vab-icon :icon="isFullscreen ? 'fullscreen-exit-fill' : 'fullscreen-fill'" />
</el-button>
<el-popover popper-class="custom-table-checkbox">
<template #reference>
<el-button>
<vab-icon icon="settings-line" />
</el-button>
</template>
<vab-draggable v-model="columns" :animation="600" target=".el-checkbox-group">
<el-checkbox-group v-model="checkList">
<el-checkbox
v-for="item in columns"
:key="item.label"
:disabled="item.disableCheck"
:label="item.label"
:value="item.label"
>
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</vab-draggable>
</el-popover>
</div>
</vab-query-form-right-panel>
</vab-query-form>
<el-table ref="tableRef" v-loading="listLoading" border :data="list" @selection-change="setSelectRows">
<el-table-column align="center" type="selection" width="50" />
<!-- <el-table-column key="userId" align="center" label="用户编号" min-width="120" prop="userId" />
<el-table-column key="userName" align="center" label="用户名称" min-width="160" prop="userName" :show-overflow-tooltip="true" />
<el-table-column key="nickName" align="center" label="用户昵称" min-width="160" prop="nickName" :show-overflow-tooltip="true" />
<el-table-column
key="deptName"
align="center"
label="部门"
min-width="160"
prop="dept.deptName"
:show-overflow-tooltip="true"
/>
<el-table-column key="phonenumber" align="center" label="手机号码" prop="phonenumber" width="120" />
<el-table-column key="status" align="center" label="状态">
<template #default="scope">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column align="center" label="创建时间" prop="createTime" width="160" /> -->
<el-table-column
v-for="(item, index) in finallyColumns"
:key="index"
align="center"
:fixed="item.fixed"
:label="item.label"
:min-width="item.minWidth || 160"
:prop="item.prop"
show-overflow-tooltip
:sortable="item.sortable"
>
<template #default="scope">
<span v-if="item.label === '状态'">
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)" />
</span>
<span v-else>{{ scope.row[item.prop] }}</span>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" min-width="240">
<template #default="scope">
<div v-if="scope.row.userId !== 1" class="table-operation-button">
<el-button icon="Edit" link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<el-button icon="Delete" link type="primary" @click="handleDelete(scope.row)">删除</el-button>
<el-dropdown style="margin-left: 12px" @command="(command) => handleCommand(command, scope.row)">
<el-button icon="DArrowRight" link type="primary">更多</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="handleResetPwd" icon="Key">重置密码</el-dropdown-item>
<el-dropdown-item command="handleAuthRole" icon="CircleCheck">分配角色</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</el-table-column>
</el-table>
<vab-pagination
:current-page="queryForm.pageNum"
:page-size="queryForm.pageSize"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</vab-card>
</el-col>
</el-row>
<user-management-edit2 ref="editRef" :dept-options="deptOptions" @fetch-data="fetchData" />
</div>
</template>
<script lang="ts" setup>
import type { TableInstance } from 'element-plus'
import { deptTreeSelect } from '/@/api/departmentManagement'
import { changeUserStatus, doDelete, getList } from '/@/api/userManagement'
defineOptions({
name: 'UserManagement',
})
const { proxy } = getCurrentInstance() as any
const { sys_normal_disable } = proxy.useDict('sys_normal_disable')
const { exit, enter, isFullscreen: _isFullscreen } = useFullscreen()
const deptName = ref<string>('')
const deptOptions = ref<any>([])
const tableRef = ref<TableInstance>()
const fold = ref<boolean>(true)
const editRef = ref<any>(null)
const list = ref<any>([])
const listLoading = ref<boolean>(true)
const isFullscreen = ref<boolean>(false)
const total = ref<number>(0)
const selectRows = ref<any>([])
const checkList = ref<any>([])
const queryForm = reactive<any>({
pageNum: 1,
pageSize: 20,
userName: undefined,
phonenumber: undefined,
status: undefined,
deptId: undefined,
dateRange: [],
})
const columns = ref<any>([
{ label: '用户编号', prop: 'userId', minWidth: 120, checked: true },
{ label: '用户名称', prop: 'userName', minWidth: 160, checked: true },
{ label: '部门', prop: 'deptName', minWidth: 160, checked: true },
{ label: '手机号码', prop: 'phonenumber', minWidth: 120, checked: true },
{ label: '状态', prop: 'status', minWidth: 120, checked: true },
{ label: '创建时间', prop: 'createTime', minWidth: 160, checked: true },
])
const finallyColumns = computed(() => columns.value.filter((item: any) => checkList.value.includes(item.label)))
/** 根据名称筛选部门树 */
watch(deptName, (val) => {
proxy.$refs['deptTreeRef'].filter(val)
})
watch(
_isFullscreen,
() => {
if (_isFullscreen.value) isFullscreen.value = true
else isFullscreen.value = false
},
{ immediate: true }
)
onActivated(() => {
tableRef.value?.doLayout()
})
onBeforeMount(() => {
columns.value.forEach((item: any) => {
if (item.checked) checkList.value.push(item.label)
})
getDeptTree()
fetchData()
})
/** 通过条件过滤节点 */
const filterNode = (value: any, data: any) => {
if (!value) return true
return data.label.includes(value)
}
/** 节点单击事件 */
function handleNodeClick(data: any) {
queryForm.deptId = data.id
console.log(queryForm)
handleQuery()
}
/** 查询部门下拉树结构 */
function getDeptTree() {
deptTreeSelect().then((response) => {
deptOptions.value = response.data
})
}
/** 点击全屏 */
const clickFullScreen = () => {
isFullscreen.value = !isFullscreen.value
isFullscreen.value ? enter() : exit()
}
/** 搜索条件折叠 */
const handleFold = () => {
fold.value = !fold.value
}
/** 查询用户列表 */
const fetchData = async () => {
listLoading.value = true
const pp = Object.assign({}, proxy.addDateRange(queryForm, queryForm.dateRange))
delete pp.dateRange
const { rows, total: _total } = (await getList(pp)) as any
list.value = rows
total.value = _total
listLoading.value = false
}
/** 搜索按钮操作 */
function handleQuery() {
queryForm.pageNum = 1
fetchData()
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm('queryRef')
queryForm.deptId = undefined
proxy.$refs.deptTreeRef.setCurrentKey(null)
handleQuery()
}
const handleSizeChange = (value: number) => {
queryForm.pageNum = 1
queryForm.pageSize = value
fetchData()
}
const handleCurrentChange = (value: number) => {
queryForm.pageNum = value
fetchData()
}
const setSelectRows = (value: string) => {
selectRows.value = value
}
/** 用户状态修改 */
function handleStatusChange(row: any) {
const text = row.status === '0' ? '启用' : '停用'
$baseConfirm(
`确认要"${text}" "${row.userName}"用户吗?`,
null,
async () => {
await changeUserStatus(row.userId, row.status)
$baseMessage(`${text}成功`, 'success', 'hey')
},
() => {
row.status = row.status === '0' ? '1' : '0'
}
)
}
/** 更多操作 */
function handleCommand(command: string, row: any) {
switch (command) {
case 'handleResetPwd': {
handleResetPwd(row)
break
}
case 'handleAuthRole': {
handleAuthRole(row)
break
}
default: {
break
}
}
}
/** 跳转角色分配 */
function handleAuthRole(row: any) {
console.log(row)
// const userId = row.userId;
// router.push("/system/user-auth/role/" + userId);
}
/** 重置密码按钮操作 */
function handleResetPwd(row: any) {
console.log(row)
// proxy.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
// confirmButtonText: "确定",
// cancelButtonText: "取消",
// closeOnClickModal: false,
// inputPattern: /^.{5,20}$/,
// inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
// inputValidator: (value) => {
// if (/<|>|"|'|\||\\/.test(value)) {
// return "不能包含非法字符:< > \" ' \\\ |"
// }
// },
// }).then(({ value }) => {
// const encryptPassword = hex_md5(value)
// resetUserPwd(row.userId, encryptPassword).then(response => {
// proxy.$modal.msgSuccess("修改成功,新密码是:" + value);
// });
// }).catch(() => { });
}
/** 新增按钮操作 */
const handleAdd = () => {
editRef.value.showEdit()
}
/** 修改按钮操作 */
function handleUpdate(row: any) {
editRef.value.showEdit(row)
}
/** 删除按钮操作 */
const handleDelete = (row: any = {}) => {
if (row.userId) {
$baseConfirm('您确定要删除当前项吗', null, async () => {
const { msg }: any = await doDelete(row.userId)
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
} else {
if (selectRows.value.length > 0) {
const ids = selectRows.value.map((item: { userId: any }) => item.userId)
$baseConfirm('您确定要删除选中项吗', null, async () => {
const { msg }: any = await doDelete(ids)
$baseMessage(msg, 'success', 'hey')
await fetchData()
})
} else {
$baseMessage('您未选中任何行', 'warning', 'hey')
}
}
}
/** 导出按钮操作 */
function handleExport() {
proxy.download(
'system/user/export',
{
...queryForm,
},
`user_${Date.now()}.xlsx`
)
}
</script>
<template>
<vab-dialog v-model="dialogFormVisible" append-to-body :title="title" width="600px" @close="close">
<el-form ref="formRef" label-width="80px" :model="form" :rules="rules">
<el-row>
<el-col :span="12">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="form.nickName" maxlength="30" placeholder="请输入用户昵称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="form.deptId"
check-strictly
:data="deptOptions"
placeholder="请选择归属部门"
:props="{ value: 'id', label: 'label', children: 'children' }"
value-key="id"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" maxlength="11" placeholder="请输入手机号码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" maxlength="50" placeholder="请输入邮箱" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" maxlength="30" placeholder="请输入用户名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户密码" prop="password">
<el-input v-model="form.password" maxlength="20" placeholder="请输入用户密码" show-password type="password" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择">
<el-option
v-for="item in postOptions"
:key="item.postId"
:disabled="item.status == 1"
:label="item.postName"
:value="item.postId"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="角色">
<el-select v-model="form.roleIds" multiple placeholder="请选择">
<el-option
v-for="item in roleOptions"
:key="item.roleId"
:disabled="item.status == 1"
:label="item.roleName"
:value="item.roleId"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="请输入内容" type="textarea" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button type="primary" @click="save">确定</el-button>
<el-button @click="cancel">取 消</el-button>
</template>
</vab-dialog>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { doAdd, doEdit, getUser } from '/@/api/userManagement'
defineOptions({
name: 'UserManagementEdit2',
})
const { proxy } = getCurrentInstance() as any
const { sys_normal_disable, sys_user_sex } = proxy.useDict('sys_normal_disable', 'sys_user_sex')
const emit = defineEmits(['fetch-data'])
defineProps({
deptOptions: {
type: Array,
default: () => [],
},
})
const postOptions = ref<any>([])
const roleOptions = ref<any>([])
const formRef = ref<FormInstance>()
const form = ref<any>({
userId: undefined,
deptId: undefined,
userName: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: '0',
remark: undefined,
postIds: [],
roleIds: [],
})
const rules = reactive<any>({
userName: [
{ required: true, message: '用户名称不能为空', trigger: 'blur' },
{ min: 2, max: 20, message: '用户名称长度必须介于 2 和 20 之间', trigger: 'blur' },
],
nickName: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }],
password: [
{ required: true, message: '用户密码不能为空', trigger: 'blur' },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\\ |', trigger: 'blur' },
],
email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }],
phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }],
})
const title = ref<string>('')
const dialogFormVisible = ref<boolean>(false)
const showEdit = async (row: any) => {
reset()
const { posts, roles, data, postIds, roleIds } = (await getUser(row?.userId)) as any
postOptions.value = posts
roleOptions.value = roles
dialogFormVisible.value = true
nextTick(() => {
if (row) {
title.value = '编辑'
form.value = data
form.value.postIds = postIds
form.value.roleIds = roleIds
} else {
title.value = '添加'
}
})
}
defineExpose({
showEdit,
})
onMounted(async () => {})
/** 重置操作表单 */
function reset() {
form.value = {
userId: undefined,
deptId: undefined,
userName: undefined,
nickName: undefined,
password: undefined,
phonenumber: undefined,
email: undefined,
sex: undefined,
status: '0',
remark: undefined,
postIds: [],
roleIds: [],
}
proxy.resetForm('userRef')
}
const close = () => {
formRef.value?.clearValidate()
formRef.value?.resetFields()
emit('fetch-data')
}
const cancel = () => {
close()
dialogFormVisible.value = false
}
const save = () => {
formRef.value?.validate(async (valid: any) => {
if (valid) {
if (form.value.userId) {
await doEdit(form.value)
await $baseMessage('修改成功', 'success', 'hey')
} else {
await doAdd(form.value)
await $baseMessage('添加成功', 'success', 'hey')
}
cancel()
}
})
}
</script>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论