Compare commits
10 Commits
7aa001fee9
...
0aca9430c2
Author | SHA1 | Date |
---|---|---|
|
0aca9430c2 | |
|
9c91d8d68e | |
|
28ac2dd7b6 | |
|
72d6fd5a4e | |
|
0735371c63 | |
|
5a00171c9a | |
|
9af5c99c2c | |
|
64ec1206e8 | |
|
ed301a5b9d | |
|
f236109945 |
|
@ -4,7 +4,6 @@ dist
|
|||
dist-ssr
|
||||
*.local
|
||||
/dist*
|
||||
*-lock.*
|
||||
pnpm-debug
|
||||
stats.html
|
||||
.idea
|
||||
|
|
|
@ -34,6 +34,8 @@ export default tseslint.config({
|
|||
'prettier/prettier': 'error',
|
||||
'no-useless-escape': 0,
|
||||
'no-undef': 0,
|
||||
'@typescript-eslint/no-unused-expressions': 0,
|
||||
'@typescript-eslint/no-unsafe-function-type': 0,
|
||||
'vue/no-setup-props-destructure': 0,
|
||||
'vue/script-setup-uses-vars': 1,
|
||||
'vue/no-reserved-component-names': 0,
|
||||
|
@ -52,7 +54,6 @@ export default tseslint.config({
|
|||
'@typescript-eslint/no-unused-vars': 0,
|
||||
'no-unused-vars': 0,
|
||||
'space-before-function-paren': 0,
|
||||
|
||||
'vue/attributes-order': 0,
|
||||
'vue/one-component-per-file': 0,
|
||||
'vue/html-closing-bracket-newline': 0,
|
||||
|
|
|
@ -348,6 +348,14 @@ const adminList = [
|
|||
meta: {
|
||||
title: 'router.iAgree'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tree',
|
||||
component: 'views/Components/Tree',
|
||||
name: 'Tree',
|
||||
meta: {
|
||||
title: 'router.tree'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
102
package.json
102
package.json
|
@ -29,89 +29,89 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@iconify/iconify": "^3.1.1",
|
||||
"@iconify/vue": "^4.1.2",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"@iconify/vue": "^4.3.0",
|
||||
"@vueuse/core": "^12.3.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.10",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.7.2",
|
||||
"axios": "^1.7.9",
|
||||
"cropperjs": "^1.6.2",
|
||||
"dayjs": "^1.11.11",
|
||||
"dayjs": "^1.11.13",
|
||||
"driver.js": "^1.3.1",
|
||||
"echarts": "^5.5.1",
|
||||
"echarts": "^5.6.0",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "2.7.7",
|
||||
"element-plus": "2.9.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.12.3",
|
||||
"url": "^0.11.3",
|
||||
"vue": "3.4.32",
|
||||
"vue-draggable-plus": "^0.5.2",
|
||||
"vue-i18n": "9.13.1",
|
||||
"pinia": "^2.3.0",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"qs": "^6.13.1",
|
||||
"url": "^0.11.4",
|
||||
"vue": "3.5.13",
|
||||
"vue-draggable-plus": "^0.6.0",
|
||||
"vue-i18n": "11.0.1",
|
||||
"vue-json-pretty": "^2.4.0",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-types": "^5.1.3",
|
||||
"xgplayer": "^3.0.18"
|
||||
"xgplayer": "^3.0.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@iconify/json": "^2.2.229",
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@commitlint/cli": "^19.6.1",
|
||||
"@commitlint/config-conventional": "^19.6.0",
|
||||
"@iconify/json": "^2.2.293",
|
||||
"@intlify/unplugin-vue-i18n": "^6.0.3",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/inquirer": "^9.0.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mockjs": "^1.0.10",
|
||||
"@types/node": "^20.14.11",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qs": "^6.9.15",
|
||||
"@types/qs": "^6.9.17",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.16.1",
|
||||
"@typescript-eslint/parser": "^7.16.1",
|
||||
"@unocss/transformer-variant-group": "^0.61.5",
|
||||
"@vitejs/plugin-legacy": "^5.4.1",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"chalk": "^5.3.0",
|
||||
"consola": "^3.2.3",
|
||||
"eslint": "^9.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
||||
"@typescript-eslint/parser": "^8.19.1",
|
||||
"@unocss/transformer-variant-group": "^0.65.4",
|
||||
"@vitejs/plugin-legacy": "^6.0.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"chalk": "^5.4.1",
|
||||
"consola": "^3.3.3",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-define-config": "^2.1.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-vue": "^9.27.0",
|
||||
"esno": "^4.7.0",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"esno": "^4.8.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"husky": "^9.1.0",
|
||||
"inquirer": "^10.0.3",
|
||||
"less": "^4.2.0",
|
||||
"lint-staged": "^15.2.7",
|
||||
"husky": "^9.1.7",
|
||||
"inquirer": "^12.3.0",
|
||||
"less": "^4.2.1",
|
||||
"lint-staged": "^15.3.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"plop": "^4.0.1",
|
||||
"postcss": "^8.4.39",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-html": "^1.7.0",
|
||||
"postcss-less": "^6.0.0",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier": "^3.4.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.18.1",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"stylelint": "^16.7.0",
|
||||
"rollup": "^4.30.1",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"stylelint": "^16.12.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recommended": "^14.0.1",
|
||||
"stylelint-config-standard": "^36.0.1",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"terser": "^5.31.3",
|
||||
"typescript": "5.5.3",
|
||||
"typescript-eslint": "^7.16.1",
|
||||
"unocss": "^0.61.5",
|
||||
"vite": "5.3.4",
|
||||
"terser": "^5.37.0",
|
||||
"typescript": "5.7.3",
|
||||
"typescript-eslint": "^8.19.1",
|
||||
"unocss": "^0.65.4",
|
||||
"vite": "6.0.7",
|
||||
"vite-plugin-ejs": "^1.7.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-mock": "2.9.6",
|
||||
|
@ -120,9 +120,9 @@
|
|||
"vite-plugin-style-import": "2.0.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-url-copy": "^1.1.4",
|
||||
"vue-tsc": "^2.0.26"
|
||||
"vue-tsc": "^2.2.0"
|
||||
},
|
||||
"packageManager": "pnpm@8.1.0",
|
||||
"packageManager": "pnpm@9.15.3",
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"pnpm": ">=8.1.0"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -54,11 +54,11 @@ const props = defineProps({
|
|||
default: false
|
||||
},
|
||||
loadingIcon: {
|
||||
type: [String, Object] as PropType<String | Component>,
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
default: undefined
|
||||
},
|
||||
icon: {
|
||||
type: [String, Object] as PropType<String | Component>,
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
default: undefined
|
||||
},
|
||||
autofocus: {
|
||||
|
@ -82,7 +82,7 @@ const props = defineProps({
|
|||
default: false
|
||||
},
|
||||
tag: {
|
||||
type: [String, Object] as PropType<String | Component>,
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
default: 'button'
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,7 +12,7 @@ const props = withDefaults(
|
|||
language?: string
|
||||
themeSelector?: boolean
|
||||
theme?: string
|
||||
editorOption?: Object
|
||||
editorOption?: object
|
||||
modelValue: string
|
||||
}>(),
|
||||
{
|
||||
|
|
|
@ -161,7 +161,7 @@ export const initModel = (schema: FormSchema[], formModel: Recordable) => {
|
|||
// 如果 schema 对应的 field 不存在,则删除 model 中的对应的 field
|
||||
for (let i = 0; i < schema.length; i++) {
|
||||
const key = schema[i].field
|
||||
if (!get(model, key) && get(model, key) !== 0) {
|
||||
if (!Object.prototype.hasOwnProperty.call(model, key)) {
|
||||
delete model[key]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ export interface SelectComponentProps extends Omit<Partial<ISelectProps>, 'optio
|
|||
children?: string
|
||||
}
|
||||
on?: {
|
||||
change?: (value: string | number | boolean | Object) => void
|
||||
change?: (value: string | number | boolean | object) => void
|
||||
visibleChange?: (visible: boolean) => void
|
||||
removeTag?: (tag: any) => void
|
||||
clear?: () => void
|
||||
|
@ -186,7 +186,7 @@ export interface SelectV2ComponentProps {
|
|||
placement?: AutocompleteProps['placement']
|
||||
collapseTagsTooltip?: boolean
|
||||
on?: {
|
||||
change?: (value: string | number | boolean | Object) => void
|
||||
change?: (value: string | number | boolean | object) => void
|
||||
visibleChange?: (visible: boolean) => void
|
||||
removeTag?: (tag: any) => void
|
||||
clear?: () => void
|
||||
|
@ -556,7 +556,7 @@ export interface TreeSelectComponentProps
|
|||
allowDrag?: (...args: any[]) => boolean
|
||||
allowDrop?: (...args: any[]) => boolean
|
||||
on?: {
|
||||
change?: (value: string | number | boolean | Object) => void
|
||||
change?: (value: string | number | boolean | object) => void
|
||||
visibleChange?: (visible: boolean) => void
|
||||
removeTag?: (tag: any) => void
|
||||
clear?: () => void
|
||||
|
|
|
@ -32,7 +32,7 @@ const setSystemTheme = (color: string) => {
|
|||
setCssVar('--el-color-primary', color)
|
||||
appStore.setTheme({ elColorPrimary: color })
|
||||
const leftMenuBgColor = useCssVar('--left-menu-bg-color', document.documentElement)
|
||||
setMenuTheme(trim(unref(leftMenuBgColor)))
|
||||
setMenuTheme(trim(unref(leftMenuBgColor) as string))
|
||||
}
|
||||
|
||||
// 头部主题相关
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import Tree from './src/Tree.vue'
|
||||
|
||||
export { Tree }
|
|
@ -0,0 +1,147 @@
|
|||
<script lang="tsx" setup>
|
||||
import { defineProps, defineEmits, ref, CSSProperties } from 'vue'
|
||||
import { ElTree } from 'element-plus'
|
||||
|
||||
interface TreeProps {
|
||||
data: any[]
|
||||
treeProps?: Record<string, any>
|
||||
width?: string
|
||||
height?: string
|
||||
}
|
||||
const props = defineProps<TreeProps>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'node-click', nodeData: any): void
|
||||
(e: 'node-expand', nodeData: any): void
|
||||
(e: 'node-collapse', nodeData: any): void
|
||||
}>()
|
||||
|
||||
const treeContainer = ref<any>(null)
|
||||
const showTreeMenu = ref(false)
|
||||
const contextNode = ref<any>(null)
|
||||
|
||||
const menuStyle = ref<any>({})
|
||||
|
||||
const defaultWidth = '300px'
|
||||
const defaultHeight = '400px'
|
||||
|
||||
// 关闭菜单
|
||||
const closeTreeMenu = () => {
|
||||
showTreeMenu.value = false
|
||||
document.removeEventListener('click', closeTreeMenu)
|
||||
document.removeEventListener('contextmenu', closeTreeMenu)
|
||||
}
|
||||
|
||||
// 右键菜单事件处理函数
|
||||
const openTreeMenu = (event: MouseEvent, data: any, _node: any, _target: HTMLElement) => {
|
||||
contextNode.value = data
|
||||
if (!treeContainer.value) return
|
||||
|
||||
const containerRect = treeContainer.value.getBoundingClientRect()
|
||||
const nodeRect = (event.target as HTMLElement).getBoundingClientRect()
|
||||
|
||||
// 计算菜单相对于父容器定位的坐标
|
||||
const top = nodeRect.top - containerRect.top + treeContainer.value.scrollTop
|
||||
const left = nodeRect.left - containerRect.left + treeContainer.value.scrollLeft
|
||||
|
||||
menuStyle.value = {
|
||||
position: 'absolute',
|
||||
top: `${top + 20}px`,
|
||||
left: `${left + 20}px`
|
||||
}
|
||||
|
||||
showTreeMenu.value = true
|
||||
|
||||
// 点击其他地方或再次右键关闭菜单
|
||||
document.addEventListener('click', closeTreeMenu)
|
||||
document.addEventListener('contextmenu', closeTreeMenu)
|
||||
}
|
||||
|
||||
// 节点点击事件
|
||||
const handleNodeClick = (data: any) => {
|
||||
emit('node-click', data)
|
||||
closeTreeMenu()
|
||||
}
|
||||
|
||||
// 节点展开事件
|
||||
const handleNodeExpand = (data: any) => {
|
||||
emit('node-expand', data)
|
||||
closeTreeMenu()
|
||||
}
|
||||
|
||||
// 节点关闭事件
|
||||
const handleNodeCollapse = (data: any) => {
|
||||
emit('node-collapse', data)
|
||||
closeTreeMenu()
|
||||
}
|
||||
|
||||
// 计算容器样式
|
||||
const containerStyle: CSSProperties = {
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
width: props.width ?? defaultWidth,
|
||||
height: props.height ?? defaultHeight
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="tree-container" ref="treeContainer" :style="containerStyle">
|
||||
<ElTree
|
||||
v-bind="treeProps"
|
||||
:data="data"
|
||||
@node-click="handleNodeClick"
|
||||
@node-expand="handleNodeExpand"
|
||||
@node-collapse="handleNodeCollapse"
|
||||
@node-contextmenu="openTreeMenu"
|
||||
>
|
||||
<template #default="{ node }">
|
||||
<!-- 如果使用者提供了 render-node slot,则渲染使用者的内容 -->
|
||||
<template v-if="$slots['render-node']">
|
||||
<slot name="render-node" :node="node"></slot>
|
||||
</template>
|
||||
<!-- 否则使用默认节点显示(比如使用 node.label )-->
|
||||
<template v-else>
|
||||
<span>{{ node.label }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</ElTree>
|
||||
<div class="treeMenu" v-show="showTreeMenu" :style="menuStyle">
|
||||
<!-- 用户通过 context-menu slot 来自定义菜单内容 -->
|
||||
<slot name="context-menu" :node="contextNode" :data="contextNode">
|
||||
<!-- 如果用户不提供 context-menu slot,可给一个默认内容 -->
|
||||
<div style="padding: 8px">No menu defined</div>
|
||||
</slot>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="less">
|
||||
.treeMenu {
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
background-color: rgb(255 255 255 / 90%);
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 4px 10px rgb(0 0 0 / 40%);
|
||||
|
||||
/* 移除 overflow: hidden; 或尝试不使用负的 top 值 */
|
||||
|
||||
/* overflow: hidden; */
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
|
||||
/* 将箭头向上移动到菜单外部 */
|
||||
top: -6px;
|
||||
left: 50%;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid rgb(206 194 194);
|
||||
|
||||
/* 创建一个向上的箭头 */
|
||||
border-left: 6px solid transparent;
|
||||
content: '';
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -21,7 +21,7 @@ export const NO_REDIRECT_WHITE_LIST = ['/login']
|
|||
/**
|
||||
* 不重置路由白名单
|
||||
*/
|
||||
export const NO_RESET_WHITE_LIST = ['Redirect', 'Login', 'NoFind', 'Root']
|
||||
export const NO_RESET_WHITE_LIST = ['Redirect', 'RedirectWrap', 'Login', 'NoFind', 'Root']
|
||||
|
||||
/**
|
||||
* 表格默认过滤列设置字段
|
||||
|
|
|
@ -13,7 +13,7 @@ export const useNProgress = () => {
|
|||
await nextTick()
|
||||
const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef
|
||||
if (bar) {
|
||||
bar.style.background = unref(primaryColor.value)
|
||||
bar.style.background = unref(primaryColor.value) as string
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,22 +25,24 @@ export const useTable = (config: UseTableConfig) => {
|
|||
const pageSize = ref(10)
|
||||
const total = ref(0)
|
||||
const dataList = ref<any[]>([])
|
||||
let isPageSizeChange = false
|
||||
|
||||
watch(
|
||||
() => currentPage.value,
|
||||
() => {
|
||||
methods.getList()
|
||||
if (!isPageSizeChange) methods.getList()
|
||||
isPageSizeChange = false
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => pageSize.value,
|
||||
() => {
|
||||
// 当前页不为1时,修改页数后会导致多次调用getList方法
|
||||
if (unref(currentPage) === 1) {
|
||||
methods.getList()
|
||||
} else {
|
||||
currentPage.value = 1
|
||||
isPageSizeChange = true
|
||||
methods.getList()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,7 +190,8 @@ export default {
|
|||
personalCenter: 'Personal center',
|
||||
personal: 'Personal',
|
||||
avatars: 'Avatars',
|
||||
iAgree: 'I agree'
|
||||
iAgree: 'I agree',
|
||||
tree: 'Tree'
|
||||
},
|
||||
permission: {
|
||||
hasPermission: 'Please set the operation permission value'
|
||||
|
@ -393,6 +394,11 @@ export default {
|
|||
logoStyle: 'Logo style',
|
||||
size: 'size config'
|
||||
},
|
||||
treeDemo: {
|
||||
treeTitle: 'Tree control (right-click node to customize menu options)',
|
||||
message:
|
||||
'The tree component is based on the secondary packaging of the tree component of ElementPlus'
|
||||
},
|
||||
highlightDemo: {
|
||||
highlight: 'Highlight',
|
||||
message: 'The best time to plant a tree is ten years ago, followed by now.',
|
||||
|
|
|
@ -186,7 +186,8 @@ export default {
|
|||
personalCenter: '个人中心',
|
||||
personal: '个人',
|
||||
avatars: '头像列表',
|
||||
iAgree: '我同意'
|
||||
iAgree: '我同意',
|
||||
tree: 'Tree 树形控件'
|
||||
},
|
||||
permission: {
|
||||
hasPermission: '请设置操作权限值'
|
||||
|
@ -385,6 +386,10 @@ export default {
|
|||
logoStyle: 'logo样式',
|
||||
size: '大小配置'
|
||||
},
|
||||
treeDemo: {
|
||||
treeTitle: '树形控件(节点右键可自定义菜单选项)',
|
||||
message: '基于 ElementPlus 的 Tree 组件二次封装'
|
||||
},
|
||||
highlightDemo: {
|
||||
highlight: '高亮',
|
||||
message: '种一棵树最好的时间是十年前,其次就是现在。',
|
||||
|
|
|
@ -20,7 +20,7 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
|
|||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
name: 'Redirect',
|
||||
name: 'RedirectWrap',
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
|
|
|
@ -287,11 +287,11 @@ export const useAppStore = defineStore('app', {
|
|||
// 左侧菜单选中背景颜色
|
||||
leftMenuBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
: hexToRGB(unref(primaryColor) as string, 0.1),
|
||||
// 左侧菜单收起选中背景颜色
|
||||
leftMenuCollapseBgActiveColor: isDarkColor
|
||||
? 'var(--el-color-primary)'
|
||||
: hexToRGB(unref(primaryColor), 0.1),
|
||||
: hexToRGB(unref(primaryColor) as string, 0.1),
|
||||
// 左侧菜单字体颜色
|
||||
leftMenuTextColor: isDarkColor ? '#bfcbd9' : '#333',
|
||||
// 左侧菜单选中字体颜色
|
||||
|
|
|
@ -77,9 +77,20 @@ export const usePermissionStore = defineStore('permission', {
|
|||
this.menuTabRouters = routers
|
||||
}
|
||||
},
|
||||
persist: {
|
||||
paths: ['routers', 'addRouters', 'menuTabRouters']
|
||||
}
|
||||
persist: [
|
||||
{
|
||||
pick: ['routers'],
|
||||
storage: localStorage
|
||||
},
|
||||
{
|
||||
pick: ['addRouters'],
|
||||
storage: localStorage
|
||||
},
|
||||
{
|
||||
pick: ['menuTabRouters'],
|
||||
storage: localStorage
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export const usePermissionStoreWithOut = () => {
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
<script setup lang="tsx">
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { Tree } from '@/components/Tree'
|
||||
import { ContentWrap } from '@/components/ContentWrap'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const treeData = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '北京',
|
||||
children: [
|
||||
{
|
||||
id: 5,
|
||||
name: '朝阳',
|
||||
children: [
|
||||
{
|
||||
id: 17,
|
||||
name: '双塔',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: '龙城',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '丰台',
|
||||
children: [
|
||||
{
|
||||
id: 19,
|
||||
name: '新村',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: '大红门',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
name: '长辛店',
|
||||
children: [
|
||||
{
|
||||
id: 22,
|
||||
name: '东山坡',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
name: '北关',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
name: '光明里',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
name: '赵辛店',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
name: '西峰寺',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '海淀',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '房山',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: '顺义',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '上海',
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
name: '黄埔',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: '徐汇',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '广州',
|
||||
children: [
|
||||
{
|
||||
id: 13,
|
||||
name: '荔湾',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: '白云',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: '越秀',
|
||||
children: []
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: '南沙',
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const handleNodeClick = (data: any) => {
|
||||
console.log('Node clicked:', data)
|
||||
}
|
||||
|
||||
const addOrg = (node: any) => {
|
||||
ElMessageBox.prompt('请输入分组名称', '添加子分组', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: /\S/,
|
||||
inputErrorMessage: '分组名称不能为空'
|
||||
}).then(({ value }) => {
|
||||
node.children.push({
|
||||
id: node.children.length + 1,
|
||||
name: value,
|
||||
children: []
|
||||
})
|
||||
ElMessage.success('添加成功')
|
||||
})
|
||||
}
|
||||
const editOrg = (node: any) => {
|
||||
ElMessageBox.prompt('请输入新的分组名称', '修改分组名称', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputValue: node.name,
|
||||
inputPattern: /\S/,
|
||||
inputErrorMessage: '分组名称不能为空'
|
||||
}).then(({ value }) => {
|
||||
node.name = value
|
||||
ElMessage.success('修改成功')
|
||||
})
|
||||
}
|
||||
|
||||
const deleteOrg = (node: any) => {
|
||||
ElMessageBox.confirm(`删除 [${node.name}] 分组、下级子分组 <br>是否继续?`, '提示', {
|
||||
dangerouslyUseHTMLString: true,
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
center: true
|
||||
}).then(() => {
|
||||
const id = node.id
|
||||
// 查找 treeData 中对应的节点,并删除
|
||||
const deleteNode = (data: any) => {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (data[i].id === id) {
|
||||
data.splice(i, 1)
|
||||
return
|
||||
}
|
||||
if (data[i].children) {
|
||||
deleteNode(data[i].children)
|
||||
}
|
||||
}
|
||||
}
|
||||
deleteNode(treeData.value)
|
||||
ElMessage.success('删除成功')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentWrap :title="t('treeDemo.treeTitle')" :message="t('qrcodeDemo.qrcodeDes')">
|
||||
<Tree
|
||||
:data="treeData"
|
||||
:tree-props="{
|
||||
highlightCurrent: true,
|
||||
nodeKey: 'id',
|
||||
props: {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}
|
||||
}"
|
||||
width="300px"
|
||||
height="400px"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<!-- 自定义右键菜单 -->
|
||||
<template #context-menu="{ node }">
|
||||
<div class="menuItem" @click="addOrg(node)">
|
||||
<Icon icon="ep:plus" style="color: #1e9fff" />
|
||||
<span>添加子分组</span>
|
||||
</div>
|
||||
<div class="menuItem" @click="editOrg(node)">
|
||||
<Icon icon="ep:edit-pen" style="color: #1e9fff" />
|
||||
修改分组名称
|
||||
</div>
|
||||
<div class="menuItem" @click="deleteOrg(node)">
|
||||
<Icon icon="ep:delete" style="color: #1e9fff" />
|
||||
删除分组及子分组
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 自定义节点显示 -->
|
||||
<!-- <template #render-node="{ node }">
|
||||
<span v-if="node.isLeaf">[FILE] {{ node.label }}</span>
|
||||
<span v-else>[FOLDER] {{ node.label }}</span>
|
||||
</template> -->
|
||||
</Tree>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.menuItem {
|
||||
display: flex;
|
||||
padding: 2px 10px;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
align-items: center; /* 垂直居中 */
|
||||
gap: 5px; /* 图标和文字之间的间距,可根据需要调整 */
|
||||
}
|
||||
|
||||
.menuItem:hover {
|
||||
cursor: pointer;
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue