perf(tree): strengthen BasicTree function

This commit is contained in:
Vben 2021-03-03 23:35:30 +08:00
parent 9a1ba74920
commit cd8e924d46
20 changed files with 394 additions and 138 deletions

View File

@ -11,8 +11,9 @@
- 新增修改密码界面 - 新增修改密码界面
- 新增部门管理示例界面 - 新增部门管理示例界面
- 新增 WebSocket 示例和服务脚本 - 新增 WebSocket 示例和服务脚本
- BasicTree 组件新增 `renderIcon` 属性用于控制层级图标显示 - Tree 组件新增 `renderIcon` 属性用于控制层级图标显示
- BasicTree->actionItem 新增 show 属性,用于动态控制按钮显示 - Tree->actionItem 新增 show 属性,用于动态控制按钮显示
- Tree 新增工具栏/title/搜索功能
### ⚡ Performance Improvements ### ⚡ Performance Improvements

View File

@ -29,7 +29,7 @@
"dependencies": { "dependencies": {
"@iconify/iconify": "^2.0.0-rc.6", "@iconify/iconify": "^2.0.0-rc.6",
"@vueuse/core": "^4.3.1", "@vueuse/core": "^4.3.1",
"@zxcvbn-ts/core": "^0.2.0", "@zxcvbn-ts/core": "^0.3.0",
"ant-design-vue": "2.0.1", "ant-design-vue": "2.0.1",
"apexcharts": "^3.25.0", "apexcharts": "^3.25.0",
"axios": "^0.21.1", "axios": "^0.21.1",

View File

@ -23,7 +23,8 @@
import { Input } from 'ant-design-vue'; import { Input } from 'ant-design-vue';
import zxcvbn from '@zxcvbn-ts/core'; // @ts-ignore
import { zxcvbn } from '@zxcvbn-ts/core';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';

View File

@ -0,0 +1,110 @@
<template>
<div class="flex px-2 py-1.5 items-center border-b-1">
<BasicTitle :helpMessage="helpMessage" v-if="title">{{ title }}</BasicTitle>
<div class="flex flex-1 justify-end items-center cursor-pointer" v-if="search || toolbar">
<div class="mr-1 w-2/3" v-if="search">
<InputSearch :placeholder="t('common.searchText')" size="small" @change="handleSearch" />
</div>
<Dropdown @click.prevent v-if="toolbar">
<Icon icon="ion:ellipsis-vertical" />
<template #overlay>
<Menu @click="handleMenuClick">
<MenuItem v-for="item in toolbarList" :key="item.value">
{{ item.label }}
</MenuItem>
</Menu>
</template>
</Dropdown>
</div>
</div>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, ref } from 'vue';
import { Dropdown, Menu, Checkbox, Input } from 'ant-design-vue';
import { Icon } from '/@/components/Icon';
import { BasicTitle } from '/@/components/Basic';
import { propTypes } from '/@/utils/propTypes';
import { useI18n } from '/@/hooks/web/useI18n';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { ToolbarEnum } from './enum';
interface MenuInfo {
key: ToolbarEnum;
}
export default defineComponent({
name: 'BasicTreeHeader',
components: {
BasicTitle,
Icon,
Checkbox,
Dropdown,
Menu,
MenuItem: Menu.Item,
InputSearch: Input.Search,
},
props: {
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
title: propTypes.string,
toolbar: propTypes.bool,
search: propTypes.bool,
checkAll: propTypes.func,
expandAll: propTypes.func,
},
emits: ['strictly-change', 'search'],
setup(props, { emit }) {
const { t } = useI18n();
const toolbarList = ref([
{ label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
{ label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL },
{ label: t('component.tree.expandAll'), value: ToolbarEnum.EXPAND_ALL },
{ label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL },
{ label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY },
{ label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY },
]);
function handleMenuClick(e: MenuInfo) {
const { key } = e;
switch (key) {
case ToolbarEnum.SELECT_ALL:
props.checkAll?.(true);
break;
case ToolbarEnum.UN_SELECT_ALL:
props.checkAll?.(false);
break;
case ToolbarEnum.EXPAND_ALL:
props.expandAll?.(true);
break;
case ToolbarEnum.UN_EXPAND_ALL:
props.expandAll?.(false);
break;
case ToolbarEnum.CHECK_STRICTLY:
emit('strictly-change', false);
break;
case ToolbarEnum.CHECK_UN_STRICTLY:
emit('strictly-change', true);
break;
}
}
function emitChange(value?: string): void {
emit('search', value);
}
const [debounceEmitChange] = useDebounce(emitChange, 200);
function handleSearch(e: ChangeEvent): void {
debounceEmitChange(e.target.value);
}
return { t, toolbarList, handleMenuClick, handleSearch };
},
});
</script>

View File

@ -0,0 +1,8 @@
export enum ToolbarEnum {
SELECT_ALL,
UN_SELECT_ALL,
EXPAND_ALL,
UN_EXPAND_ALL,
CHECK_STRICTLY,
CHECK_UN_STRICTLY,
}

View File

@ -1,23 +1,16 @@
<script lang="tsx"> <script lang="tsx">
import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types'; import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types';
import { import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw } from 'vue';
defineComponent,
reactive,
computed,
unref,
ref,
watchEffect,
onMounted,
toRaw,
} from 'vue';
import { Tree } from 'ant-design-vue'; import { Tree } from 'ant-design-vue';
import { TreeIcon } from './TreeIcon'; import { TreeIcon } from './TreeIcon';
import TreeHeader from './TreeHeader.vue';
// import { DownOutlined } from '@ant-design/icons-vue'; // import { DownOutlined } from '@ant-design/icons-vue';
import { omit, get } from 'lodash-es'; import { omit, get } from 'lodash-es';
import { isBoolean, isFunction } from '/@/utils/is'; import { isBoolean, isFunction } from '/@/utils/is';
import { extendSlots } from '/@/utils/helper/tsxHelper'; import { extendSlots } from '/@/utils/helper/tsxHelper';
import { filter } from '/@/utils/helper/treeHelper';
import { useTree } from './useTree'; import { useTree } from './useTree';
import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu'; import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu';
@ -30,18 +23,25 @@
expandedKeys: Keys; expandedKeys: Keys;
selectedKeys: Keys; selectedKeys: Keys;
checkedKeys: CheckKeys; checkedKeys: CheckKeys;
checkStrictly: boolean;
} }
export default defineComponent({ export default defineComponent({
name: 'BasicTree', name: 'BasicTree',
props: basicProps, props: basicProps,
emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'get'], emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change'],
setup(props, { attrs, slots, emit }) { setup(props, { attrs, slots, emit }) {
const state = reactive<State>({ const state = reactive<State>({
checkStrictly: props.checkStrictly,
expandedKeys: props.expandedKeys || [], expandedKeys: props.expandedKeys || [],
selectedKeys: props.selectedKeys || [], selectedKeys: props.selectedKeys || [],
checkedKeys: props.checkedKeys || [], checkedKeys: props.checkedKeys || [],
}); });
const searchState = reactive({
startSearch: false,
searchData: [] as TreeItem[],
});
const treeDataRef = ref<TreeItem[]>([]); const treeDataRef = ref<TreeItem[]>([]);
const [createContextMenu] = useContextMenu(); const [createContextMenu] = useContextMenu();
@ -77,6 +77,7 @@
expandedKeys: state.expandedKeys, expandedKeys: state.expandedKeys,
selectedKeys: state.selectedKeys, selectedKeys: state.selectedKeys,
checkedKeys: state.checkedKeys, checkedKeys: state.checkedKeys,
checkStrictly: state.checkStrictly,
replaceFields: unref(getReplaceFields), replaceFields: unref(getReplaceFields),
'onUpdate:expandedKeys': (v: Keys) => { 'onUpdate:expandedKeys': (v: Keys) => {
state.expandedKeys = v; state.expandedKeys = v;
@ -88,21 +89,27 @@
}, },
onCheck: (v: CheckKeys) => { onCheck: (v: CheckKeys) => {
state.checkedKeys = v; state.checkedKeys = v;
emit('change', v);
emit('update:value', v); emit('update:value', v);
}, },
onRightClick: handleRightClick, onRightClick: handleRightClick,
}; };
propsData = omit(propsData, 'treeData'); propsData = omit(propsData, 'treeData', 'class');
return propsData; return propsData;
}); });
const getTreeData = computed((): TreeItem[] => unref(treeDataRef)); const getTreeData = computed((): TreeItem[] =>
searchState.startSearch ? searchState.searchData : unref(treeDataRef)
const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree(
treeDataRef,
getReplaceFields
); );
const {
deleteNodeByKey,
insertNodeByKey,
filterByLevel,
updateNodeByKey,
getAllKeys,
} = useTree(treeDataRef, getReplaceFields);
function getIcon(params: Recordable, icon?: string) { function getIcon(params: Recordable, icon?: string) {
if (!icon) { if (!icon) {
if (props.renderIcon && isFunction(props.renderIcon)) { if (props.renderIcon && isFunction(props.renderIcon)) {
@ -112,60 +119,6 @@
return icon; return icon;
} }
function renderAction(node: TreeItem) {
const { actionList } = props;
if (!actionList || actionList.length === 0) return;
return actionList.map((item, index) => {
if (isFunction(item.show)) {
return item.show?.(node);
}
if (isBoolean(item.show)) {
return item.show;
}
return (
<span key={index} class={`${prefixCls}__action`}>
{item.render(node)}
</span>
);
});
}
function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) {
if (!data) {
return null;
}
return data.map((item) => {
const { title: titleField, key: keyField, children: childrenField } = unref(
getReplaceFields
);
const propsData = omit(item, 'title');
const icon = getIcon({ ...item, level }, item.icon);
return (
<Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}>
{{
title: () => (
<span class={`${prefixCls}-title`}>
{icon && <TreeIcon icon={icon} />}
<span
class={`${prefixCls}__content`}
// style={unref(getContentStyle)}
>
{get(item, titleField)}
</span>
<span class={`${prefixCls}__actions`}> {renderAction({ ...item, level })}</span>
</span>
),
default: () =>
renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }),
}}
</Tree.TreeNode>
);
});
}
async function handleRightClick({ event, node }: any) { async function handleRightClick({ event, node }: any) {
const { rightMenuList: menuList = [], beforeRightClick } = props; const { rightMenuList: menuList = [], beforeRightClick } = props;
let rightMenuList: ContextMenuItem[] = []; let rightMenuList: ContextMenuItem[] = [];
@ -205,6 +158,32 @@
return state.checkedKeys; return state.checkedKeys;
} }
function checkAll(checkAll: boolean) {
state.checkedKeys = checkAll ? getAllKeys() : ([] as Keys);
}
function expandAll(expandAll: boolean) {
state.expandedKeys = expandAll ? getAllKeys() : ([] as Keys);
}
function onStrictlyChange(strictly: boolean) {
state.checkStrictly = strictly;
}
function handleSearch(searchValue: string) {
if (!searchValue) {
searchState.startSearch = false;
return;
}
searchState.startSearch = true;
searchState.searchData = filter(unref(treeDataRef), (node) => {
const { title } = node;
return title?.includes(searchValue) ?? false;
// || key?.includes(searchValue);
});
}
watchEffect(() => { watchEffect(() => {
treeDataRef.value = props.treeData as TreeItem[]; treeDataRef.value = props.treeData as TreeItem[];
state.expandedKeys = props.expandedKeys; state.expandedKeys = props.expandedKeys;
@ -212,6 +191,16 @@
state.checkedKeys = props.checkedKeys; state.checkedKeys = props.checkedKeys;
}); });
watchEffect(() => {
if (props.value) {
state.checkedKeys = props.value;
}
});
watchEffect(() => {
state.checkStrictly = props.checkStrictly;
});
const instance: TreeActionType = { const instance: TreeActionType = {
setExpandedKeys, setExpandedKeys,
getExpandedKeys, getExpandedKeys,
@ -222,6 +211,8 @@
insertNodeByKey, insertNodeByKey,
deleteNodeByKey, deleteNodeByKey,
updateNodeByKey, updateNodeByKey,
checkAll,
expandAll,
filterByLevel: (level: number) => { filterByLevel: (level: number) => {
state.expandedKeys = filterByLevel(level); state.expandedKeys = filterByLevel(level);
}, },
@ -229,19 +220,83 @@
useExpose<TreeActionType>(instance); useExpose<TreeActionType>(instance);
onMounted(() => { function renderAction(node: TreeItem) {
emit('get', instance); const { actionList } = props;
}); if (!actionList || actionList.length === 0) return;
return actionList.map((item, index) => {
if (isFunction(item.show)) {
return item.show?.(node);
}
if (isBoolean(item.show)) {
return item.show;
}
return () => {
return ( return (
<Tree {...unref(getBindValues)} showIcon={false} class={[prefixCls]}> <span key={index} class={`${prefixCls}__action`}>
{item.render(node)}
</span>
);
});
}
function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) {
if (!data) {
return null;
}
return data.map((item) => {
const { title: titleField, key: keyField, children: childrenField } = unref(
getReplaceFields
);
const propsData = omit(item, 'title');
const icon = getIcon({ ...item, level }, item.icon);
return (
<Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}>
{{
title: () => (
<span class={`${prefixCls}-title pl-2`}>
{icon && <TreeIcon icon={icon} />}
<span
class={`${prefixCls}__content`}
// style={unref(getContentStyle)}
>
{get(item, titleField)}
</span>
<span class={`${prefixCls}__actions`}> {renderAction({ ...item, level })}</span>
</span>
),
default: () =>
renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }),
}}
</Tree.TreeNode>
);
});
}
return () => {
const { title, helpMessage, toolbar, search } = props;
return (
<div class={[prefixCls, 'h-full bg-white']}>
{(title || toolbar || search) && (
<TreeHeader
checkAll={checkAll}
expandAll={expandAll}
title={title}
search={search}
toolbar={toolbar}
helpMessage={helpMessage}
onStrictlyChange={onStrictlyChange}
onSearch={handleSearch}
/>
)}
<Tree {...unref(getBindValues)} showIcon={false}>
{{ {{
// switcherIcon: () => <DownOutlined />, // switcherIcon: () => <DownOutlined />,
default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }), default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }),
...extendSlots(slots), ...extendSlots(slots),
}} }}
</Tree> </Tree>
</div>
); );
}; };
}, },
@ -251,8 +306,6 @@
@prefix-cls: ~'@{namespace}-basic-tree'; @prefix-cls: ~'@{namespace}-basic-tree';
.@{prefix-cls} { .@{prefix-cls} {
position: relative;
.ant-tree-node-content-wrapper { .ant-tree-node-content-wrapper {
position: relative; position: relative;
@ -278,14 +331,14 @@
} }
&__content { &__content {
display: inline-block; // display: inline-block;
overflow: hidden; overflow: hidden;
} }
&__actions { &__actions {
position: absolute; position: absolute;
top: 2px; top: 2px;
right: 2px; right: 3px;
display: flex; display: flex;
} }

View File

@ -2,11 +2,26 @@ import type { PropType } from 'vue';
import type { ReplaceFields, ActionItem, Keys, CheckKeys } from './types'; import type { ReplaceFields, ActionItem, Keys, CheckKeys } from './types';
import type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; import type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
import { propTypes } from '/@/utils/propTypes';
export const basicProps = { export const basicProps = {
value: {
type: Array as PropType<Keys>,
},
renderIcon: { renderIcon: {
type: Function as PropType<(params: Recordable) => string>, type: Function as PropType<(params: Recordable) => string>,
}, },
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
title: propTypes.string,
toolbar: propTypes.bool,
search: propTypes.bool,
checkStrictly: propTypes.bool,
replaceFields: { replaceFields: {
type: Object as PropType<ReplaceFields>, type: Object as PropType<ReplaceFields>,
}, },

View File

@ -21,6 +21,8 @@ export type CheckKeys =
| { checked: string[] | number[]; halfChecked: string[] | number[] }; | { checked: string[] | number[]; halfChecked: string[] | number[] };
export interface TreeActionType { export interface TreeActionType {
checkAll: (checkAll: boolean) => void;
expandAll: (expandAll: boolean) => void;
setExpandedKeys: (keys: Keys) => void; setExpandedKeys: (keys: Keys) => void;
getExpandedKeys: () => Keys; getExpandedKeys: () => Keys;
setSelectedKeys: (keys: Keys) => void; setSelectedKeys: (keys: Keys) => void;

View File

@ -1,4 +1,4 @@
import type { InsertNodeParams, ReplaceFields } from './types'; import type { InsertNodeParams, Keys, ReplaceFields } from './types';
import type { Ref, ComputedRef } from 'vue'; import type { Ref, ComputedRef } from 'vue';
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
@ -10,6 +10,23 @@ export function useTree(
treeDataRef: Ref<TreeDataItem[]>, treeDataRef: Ref<TreeDataItem[]>,
getReplaceFields: ComputedRef<ReplaceFields> getReplaceFields: ComputedRef<ReplaceFields>
) { ) {
function getAllKeys(list?: TreeDataItem[]) {
const keys: string[] = [];
const treeData = list || unref(treeDataRef);
const { key: keyField, children: childrenField } = unref(getReplaceFields);
if (!childrenField || !keyField) return keys;
for (let index = 0; index < treeData.length; index++) {
const node = treeData[index];
keys.push(node[keyField]!);
const children = node[childrenField];
if (children && children.length) {
keys.push(...(getAllKeys(children) as string[]));
}
}
return keys as Keys;
}
// Update node // Update node
function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) { function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) {
if (!key) return; if (!key) return;
@ -94,5 +111,5 @@ export function useTree(
} }
} }
} }
return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey }; return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey, getAllKeys };
} }

View File

@ -0,0 +1,9 @@
export default {
selectAll: 'Select All',
unSelectAll: 'Cancel Select',
expandAll: 'Expand All',
unExpandAll: 'Collapse all',
checkStrictly: 'Hierarchical association',
checkUnStrictly: 'Hierarchical independence',
};

View File

@ -12,7 +12,7 @@ export default {
tree: 'Tree', tree: 'Tree',
treeBasic: 'Basic', treeBasic: 'Basic',
editTree: 'Right-click', editTree: 'Searchable/toolbar',
actionTree: 'Function operation', actionTree: 'Function operation',
modal: 'Modal', modal: 'Modal',

View File

@ -0,0 +1,8 @@
export default {
selectAll: '选择全部',
unSelectAll: '取消选择',
expandAll: '展开全部',
unExpandAll: '折叠全部',
checkStrictly: '层级关联',
checkUnStrictly: '层级独立',
};

View File

@ -11,7 +11,7 @@ export default {
tree: 'Tree', tree: 'Tree',
treeBasic: '基础树', treeBasic: '基础树',
editTree: '右键示例', editTree: '可搜索/工具栏',
actionTree: '函数操作示例', actionTree: '函数操作示例',
modal: '弹窗扩展', modal: '弹窗扩展',

View File

@ -6,7 +6,10 @@ const menu: MenuModule = {
menu: { menu: {
name: t('routes.demo.comp.comp'), name: t('routes.demo.comp.comp'),
path: '/comp', path: '/comp',
tag: {
dot: true,
type: 'warn',
},
children: [ children: [
{ {
path: 'basic', path: 'basic',
@ -154,6 +157,10 @@ const menu: MenuModule = {
{ {
path: 'tree', path: 'tree',
name: t('routes.demo.comp.tree'), name: t('routes.demo.comp.tree'),
tag: {
dot: true,
type: 'warn',
},
children: [ children: [
{ {
path: 'basic', path: 'basic',
@ -162,6 +169,10 @@ const menu: MenuModule = {
{ {
path: 'editTree', path: 'editTree',
name: t('routes.demo.comp.editTree'), name: t('routes.demo.comp.editTree'),
tag: {
dot: true,
type: 'warn',
},
}, },
{ {
path: 'actionTree', path: 'actionTree',
@ -172,9 +183,6 @@ const menu: MenuModule = {
{ {
name: t('routes.demo.editor.editor'), name: t('routes.demo.editor.editor'),
path: 'editor', path: 'editor',
tag: {
content: 'new',
},
children: [ children: [
{ {
path: 'markdown', path: 'markdown',

View File

@ -22,21 +22,21 @@ const menu: MenuModule = {
path: 'role', path: 'role',
name: t('routes.demo.system.role'), name: t('routes.demo.system.role'),
tag: { tag: {
content: 'new', dot: true,
}, },
}, },
{ {
path: 'menu', path: 'menu',
name: t('routes.demo.system.menu'), name: t('routes.demo.system.menu'),
tag: { tag: {
content: 'new', dot: true,
}, },
}, },
{ {
path: 'dept', path: 'dept',
name: t('routes.demo.system.dept'), name: t('routes.demo.system.dept'),
tag: { tag: {
content: 'new', dot: true,
}, },
}, },
@ -44,7 +44,7 @@ const menu: MenuModule = {
path: 'changePassword', path: 'changePassword',
name: t('routes.demo.system.password'), name: t('routes.demo.system.password'),
tag: { tag: {
content: 'new', dot: true,
}, },
}, },
], ],

View File

@ -18,7 +18,7 @@ import { transformObjToRoute } from '/@/router/helper/routeHelper';
import { transformRouteToMenu } from '/@/router/helper/menuHelper'; import { transformRouteToMenu } from '/@/router/helper/menuHelper';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n'; // import { useI18n } from '/@/hooks/web/useI18n';
import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
const { createMessage } = useMessage(); const { createMessage } = useMessage();
@ -84,7 +84,7 @@ class Permission extends VuexModule {
@Action @Action
async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> { async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> {
const { t } = useI18n(); // const { t } = useI18n();
let routes: AppRouteRecordRaw[] = []; let routes: AppRouteRecordRaw[] = [];
const roleList = toRaw(userStore.getRoleListState); const roleList = toRaw(userStore.getRoleListState);
@ -101,7 +101,8 @@ class Permission extends VuexModule {
// If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below // If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
} else if (permissionMode === PermissionModeEnum.BACK) { } else if (permissionMode === PermissionModeEnum.BACK) {
createMessage.loading({ createMessage.loading({
content: t('sys.app.menuLoading'), content: 'Loading menu...',
// content: 't('sys.app.menuLoading')',
duration: 1, duration: 1,
}); });
// Here to get the background routing menu logic to modify by yourself // Here to get the background routing menu logic to modify by yourself

View File

@ -1,8 +1,14 @@
<template> <template>
<PageWrapper title="Tree函数操作示例" contentBackground contentClass="p-4"> <PageWrapper title="Tree函数操作示例" contentBackground contentClass="p-4">
<div class="mb-4"> <div class="mb-4">
<a-button @click="expandAll(true)" class="mr-2"> 展开全部 </a-button>
<a-button @click="expandAll(false)" class="mr-2"> 折叠全部 </a-button>
<a-button @click="checkAll(true)" class="mr-2"> 全选 </a-button>
<a-button @click="checkAll(false)" class="mr-2"> 全不选 </a-button>
<a-button @click="handleLevel(2)" class="mr-2"> 显示到第2级 </a-button> <a-button @click="handleLevel(2)" class="mr-2"> 显示到第2级 </a-button>
<a-button @click="handleLevel(1)" class="mr-2"> 显示到第1级 </a-button> <a-button @click="handleLevel(1)" class="mr-2"> 显示到第1级 </a-button>
</div>
<div class="mb-4">
<a-button @click="handleSetCheckData" class="mr-2"> 设置勾选数据 </a-button> <a-button @click="handleSetCheckData" class="mr-2"> 设置勾选数据 </a-button>
<a-button @click="handleGetCheckData" class="mr-2"> 获取勾选数据 </a-button> <a-button @click="handleGetCheckData" class="mr-2"> 获取勾选数据 </a-button>
<a-button @click="handleSetSelectData" class="mr-2"> 设置选中数据 </a-button> <a-button @click="handleSetSelectData" class="mr-2"> 设置选中数据 </a-button>
@ -17,21 +23,18 @@
<a-button @click="deleteNodeByKey('2-2')" class="mr-2"> 删除parent3节点 </a-button> <a-button @click="deleteNodeByKey('2-2')" class="mr-2"> 删除parent3节点 </a-button>
<a-button @click="updateNodeByKey('1-1')" class="mr-2"> 更新parent2节点 </a-button> <a-button @click="updateNodeByKey('1-1')" class="mr-2"> 更新parent2节点 </a-button>
</div> </div>
<CollapseContainer title="函数操作" class="mr-4" :canExpan="false" :style="{ width: '33%' }"> <BasicTree :treeData="treeData" title="函数操作" ref="treeRef" :checkable="true" />
<BasicTree :treeData="treeData" ref="treeRef" :checkable="true" />
</CollapseContainer>
</PageWrapper> </PageWrapper>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, unref } from 'vue'; import { defineComponent, ref, unref } from 'vue';
import { BasicTree, TreeActionType } from '/@/components/Tree/index'; import { BasicTree, TreeActionType } from '/@/components/Tree/index';
import { treeData } from './data'; import { treeData } from './data';
import { CollapseContainer } from '/@/components/Container/index';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { PageWrapper } from '/@/components/Page'; import { PageWrapper } from '/@/components/Page';
export default defineComponent({ export default defineComponent({
components: { BasicTree, CollapseContainer, PageWrapper }, components: { BasicTree, PageWrapper },
setup() { setup() {
const treeRef = ref<Nullable<TreeActionType>>(null); const treeRef = ref<Nullable<TreeActionType>>(null);
const { createMessage } = useMessage(); const { createMessage } = useMessage();
@ -75,6 +78,14 @@
createMessage.success(JSON.stringify(keys)); createMessage.success(JSON.stringify(keys));
} }
function checkAll(checkAll: boolean) {
getTree().checkAll(checkAll);
}
function expandAll(checkAll: boolean) {
getTree().expandAll(checkAll);
}
function appendNodeByKey(parentKey: string | null = null) { function appendNodeByKey(parentKey: string | null = null) {
getTree().insertNodeByKey({ getTree().insertNodeByKey({
parentKey: parentKey, parentKey: parentKey,
@ -112,6 +123,8 @@
appendNodeByKey, appendNodeByKey,
deleteNodeByKey, deleteNodeByKey,
updateNodeByKey, updateNodeByKey,
checkAll,
expandAll,
}; };
}, },
}); });

View File

@ -1,13 +1,29 @@
<template> <template>
<PageWrapper title="Tree函数操作示例"> <PageWrapper title="Tree函数操作示例">
<div class="flex"> <div class="flex">
<CollapseContainer title="右侧操作按钮/自定义图标" class="mr-4" :style="{ width: '33%' }"> <BasicTree
<BasicTree :treeData="treeData" :actionList="actionList" :renderIcon="createIcon" /> class="w-1/3"
</CollapseContainer> title="右侧操作按钮/自定义图标"
helpMessage="帮助信息"
<CollapseContainer title="右键菜单" class="mr-4" :style="{ width: '33%' }"> :treeData="treeData"
<BasicTree :treeData="treeData" :beforeRightClick="getRightMenuList" /> :actionList="actionList"
</CollapseContainer> :renderIcon="createIcon"
/>
<BasicTree
class="w-1/3 mx-4"
title="右键菜单"
:treeData="treeData"
:beforeRightClick="getRightMenuList"
/>
<BasicTree
class="w-1/3"
title="工具栏使用"
toolbar
checkable
search
:treeData="treeData"
:beforeRightClick="getRightMenuList"
/>
</div> </div>
</PageWrapper> </PageWrapper>
</template> </template>
@ -15,12 +31,11 @@
import { defineComponent, h } from 'vue'; import { defineComponent, h } from 'vue';
import { BasicTree, ActionItem, ContextMenuItem } from '/@/components/Tree/index'; import { BasicTree, ActionItem, ContextMenuItem } from '/@/components/Tree/index';
import { treeData } from './data'; import { treeData } from './data';
import { CollapseContainer } from '/@/components/Container/index';
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import { PageWrapper } from '/@/components/Page'; import { PageWrapper } from '/@/components/Page';
export default defineComponent({ export default defineComponent({
components: { BasicTree, CollapseContainer, PageWrapper }, components: { BasicTree, PageWrapper },
setup() { setup() {
function handlePlus(node: any) { function handlePlus(node: any) {
console.log(node); console.log(node);

View File

@ -1,22 +1,18 @@
<template> <template>
<PageWrapper title="Tree基础示例"> <PageWrapper title="Tree基础示例">
<div class="flex"> <div class="flex">
<CollapseContainer title="基础示例" :style="{ width: '33%' }" class="mr-4"> <BasicTree :treeData="treeData" title="基础示例" class="w-1/3" />
<BasicTree :treeData="treeData" />
</CollapseContainer>
<CollapseContainer title="可勾选" class="mr-4" :style="{ width: '33%' }"> <BasicTree :treeData="treeData" title="可勾选" :checkable="true" class="w-1/3 mx-4" />
<BasicTree :treeData="treeData" :checkable="true" />
</CollapseContainer>
<CollapseContainer title="默认展开/勾选示例" :style="{ width: '33%' }">
<BasicTree <BasicTree
title="默认展开/勾选示例"
:treeData="treeData" :treeData="treeData"
:checkable="true" :checkable="true"
:expandedKeys="['0-0']" :expandedKeys="['0-0']"
:checkedKeys="['0-0']" :checkedKeys="['0-0']"
class="w-1/3"
/> />
</CollapseContainer>
</div> </div>
</PageWrapper> </PageWrapper>
</template> </template>
@ -24,11 +20,10 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { BasicTree } from '/@/components/Tree/index'; import { BasicTree } from '/@/components/Tree/index';
import { treeData } from './data'; import { treeData } from './data';
import { CollapseContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page'; import { PageWrapper } from '/@/components/Page';
export default defineComponent({ export default defineComponent({
components: { BasicTree, CollapseContainer, PageWrapper }, components: { BasicTree, PageWrapper },
setup() { setup() {
return { treeData }; return { treeData };
}, },

View File

@ -1791,10 +1791,10 @@
micromatch "^4.0.2" micromatch "^4.0.2"
windicss "^2.2.3" windicss "^2.2.3"
"@zxcvbn-ts/core@^0.2.0": "@zxcvbn-ts/core@^0.3.0":
version "0.2.0" version "0.3.0"
resolved "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-0.2.0.tgz#ba3af1fed2213464ae12c0ab565798590afe8ef7" resolved "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-0.3.0.tgz#1a021afef29b97a5f8f72458de005fa149628e32"
integrity sha512-1NVKw2Tz3Iv3NE4RFTTcF2EQlmHfkNi48U0H80ZR/KLt3ANOFsCDp/mxGawdzCnBrf64E121xI49mpZDAACYZw== integrity sha512-H1SOAoC7MbccN/CU9ENZHXwvwTwh6aRt88SOkGROAN9nT88o/qDPJ5B5bElRSbjKLfmmO1LqK2K4u2lUxjbQKQ==
JSONStream@^1.0.4: JSONStream@^1.0.4:
version "1.3.5" version "1.3.5"