Commit cd8e924d4641fc46cacd4a934478d8861e8c3c04
1 parent
9a1ba749
perf(tree): strengthen BasicTree function
Showing
20 changed files
with
394 additions
and
138 deletions
CHANGELOG.zh_CN.md
... | ... | @@ -11,8 +11,9 @@ |
11 | 11 | - 新增修改密码界面 |
12 | 12 | - 新增部门管理示例界面 |
13 | 13 | - 新增 WebSocket 示例和服务脚本 |
14 | -- BasicTree 组件新增 `renderIcon` 属性用于控制层级图标显示 | |
15 | -- BasicTree->actionItem 新增 show 属性,用于动态控制按钮显示 | |
14 | +- Tree 组件新增 `renderIcon` 属性用于控制层级图标显示 | |
15 | +- Tree->actionItem 新增 show 属性,用于动态控制按钮显示 | |
16 | +- Tree 新增工具栏/title/搜索功能 | |
16 | 17 | |
17 | 18 | ### ⚡ Performance Improvements |
18 | 19 | ... | ... |
package.json
src/components/StrengthMeter/src/index.vue
... | ... | @@ -23,7 +23,8 @@ |
23 | 23 | |
24 | 24 | import { Input } from 'ant-design-vue'; |
25 | 25 | |
26 | - import zxcvbn from '@zxcvbn-ts/core'; | |
26 | + // @ts-ignore | |
27 | + import { zxcvbn } from '@zxcvbn-ts/core'; | |
27 | 28 | import { useDesign } from '/@/hooks/web/useDesign'; |
28 | 29 | import { propTypes } from '/@/utils/propTypes'; |
29 | 30 | ... | ... |
src/components/Tree/src/TreeHeader.vue
0 → 100644
1 | +<template> | |
2 | + <div class="flex px-2 py-1.5 items-center border-b-1"> | |
3 | + <BasicTitle :helpMessage="helpMessage" v-if="title">{{ title }}</BasicTitle> | |
4 | + | |
5 | + <div class="flex flex-1 justify-end items-center cursor-pointer" v-if="search || toolbar"> | |
6 | + <div class="mr-1 w-2/3" v-if="search"> | |
7 | + <InputSearch :placeholder="t('common.searchText')" size="small" @change="handleSearch" /> | |
8 | + </div> | |
9 | + <Dropdown @click.prevent v-if="toolbar"> | |
10 | + <Icon icon="ion:ellipsis-vertical" /> | |
11 | + <template #overlay> | |
12 | + <Menu @click="handleMenuClick"> | |
13 | + <MenuItem v-for="item in toolbarList" :key="item.value"> | |
14 | + {{ item.label }} | |
15 | + </MenuItem> | |
16 | + </Menu> | |
17 | + </template> | |
18 | + </Dropdown> | |
19 | + </div> | |
20 | + </div> | |
21 | +</template> | |
22 | +<script lang="ts"> | |
23 | + import type { PropType } from 'vue'; | |
24 | + import { defineComponent, ref } from 'vue'; | |
25 | + | |
26 | + import { Dropdown, Menu, Checkbox, Input } from 'ant-design-vue'; | |
27 | + import { Icon } from '/@/components/Icon'; | |
28 | + import { BasicTitle } from '/@/components/Basic'; | |
29 | + | |
30 | + import { propTypes } from '/@/utils/propTypes'; | |
31 | + | |
32 | + import { useI18n } from '/@/hooks/web/useI18n'; | |
33 | + import { useDebounce } from '/@/hooks/core/useDebounce'; | |
34 | + | |
35 | + import { ToolbarEnum } from './enum'; | |
36 | + | |
37 | + interface MenuInfo { | |
38 | + key: ToolbarEnum; | |
39 | + } | |
40 | + export default defineComponent({ | |
41 | + name: 'BasicTreeHeader', | |
42 | + components: { | |
43 | + BasicTitle, | |
44 | + Icon, | |
45 | + Checkbox, | |
46 | + Dropdown, | |
47 | + Menu, | |
48 | + MenuItem: Menu.Item, | |
49 | + InputSearch: Input.Search, | |
50 | + }, | |
51 | + props: { | |
52 | + helpMessage: { | |
53 | + type: [String, Array] as PropType<string | string[]>, | |
54 | + default: '', | |
55 | + }, | |
56 | + title: propTypes.string, | |
57 | + toolbar: propTypes.bool, | |
58 | + search: propTypes.bool, | |
59 | + checkAll: propTypes.func, | |
60 | + expandAll: propTypes.func, | |
61 | + }, | |
62 | + emits: ['strictly-change', 'search'], | |
63 | + setup(props, { emit }) { | |
64 | + const { t } = useI18n(); | |
65 | + const toolbarList = ref([ | |
66 | + { label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL }, | |
67 | + { label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL }, | |
68 | + { label: t('component.tree.expandAll'), value: ToolbarEnum.EXPAND_ALL }, | |
69 | + { label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL }, | |
70 | + { label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY }, | |
71 | + { label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY }, | |
72 | + ]); | |
73 | + | |
74 | + function handleMenuClick(e: MenuInfo) { | |
75 | + const { key } = e; | |
76 | + switch (key) { | |
77 | + case ToolbarEnum.SELECT_ALL: | |
78 | + props.checkAll?.(true); | |
79 | + break; | |
80 | + case ToolbarEnum.UN_SELECT_ALL: | |
81 | + props.checkAll?.(false); | |
82 | + break; | |
83 | + case ToolbarEnum.EXPAND_ALL: | |
84 | + props.expandAll?.(true); | |
85 | + break; | |
86 | + case ToolbarEnum.UN_EXPAND_ALL: | |
87 | + props.expandAll?.(false); | |
88 | + break; | |
89 | + case ToolbarEnum.CHECK_STRICTLY: | |
90 | + emit('strictly-change', false); | |
91 | + break; | |
92 | + case ToolbarEnum.CHECK_UN_STRICTLY: | |
93 | + emit('strictly-change', true); | |
94 | + break; | |
95 | + } | |
96 | + } | |
97 | + | |
98 | + function emitChange(value?: string): void { | |
99 | + emit('search', value); | |
100 | + } | |
101 | + const [debounceEmitChange] = useDebounce(emitChange, 200); | |
102 | + | |
103 | + function handleSearch(e: ChangeEvent): void { | |
104 | + debounceEmitChange(e.target.value); | |
105 | + } | |
106 | + | |
107 | + return { t, toolbarList, handleMenuClick, handleSearch }; | |
108 | + }, | |
109 | + }); | |
110 | +</script> | ... | ... |
src/components/Tree/src/enum.ts
0 → 100644
src/components/Tree/src/index.vue
1 | 1 | <script lang="tsx"> |
2 | 2 | import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types'; |
3 | 3 | |
4 | - import { | |
5 | - defineComponent, | |
6 | - reactive, | |
7 | - computed, | |
8 | - unref, | |
9 | - ref, | |
10 | - watchEffect, | |
11 | - onMounted, | |
12 | - toRaw, | |
13 | - } from 'vue'; | |
4 | + import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw } from 'vue'; | |
14 | 5 | import { Tree } from 'ant-design-vue'; |
15 | 6 | import { TreeIcon } from './TreeIcon'; |
7 | + import TreeHeader from './TreeHeader.vue'; | |
16 | 8 | // import { DownOutlined } from '@ant-design/icons-vue'; |
17 | 9 | |
18 | 10 | import { omit, get } from 'lodash-es'; |
19 | 11 | import { isBoolean, isFunction } from '/@/utils/is'; |
20 | 12 | import { extendSlots } from '/@/utils/helper/tsxHelper'; |
13 | + import { filter } from '/@/utils/helper/treeHelper'; | |
21 | 14 | |
22 | 15 | import { useTree } from './useTree'; |
23 | 16 | import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu'; |
... | ... | @@ -30,18 +23,25 @@ |
30 | 23 | expandedKeys: Keys; |
31 | 24 | selectedKeys: Keys; |
32 | 25 | checkedKeys: CheckKeys; |
26 | + checkStrictly: boolean; | |
33 | 27 | } |
34 | 28 | export default defineComponent({ |
35 | 29 | name: 'BasicTree', |
36 | 30 | props: basicProps, |
37 | - emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'get'], | |
31 | + emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change'], | |
38 | 32 | setup(props, { attrs, slots, emit }) { |
39 | 33 | const state = reactive<State>({ |
34 | + checkStrictly: props.checkStrictly, | |
40 | 35 | expandedKeys: props.expandedKeys || [], |
41 | 36 | selectedKeys: props.selectedKeys || [], |
42 | 37 | checkedKeys: props.checkedKeys || [], |
43 | 38 | }); |
44 | 39 | |
40 | + const searchState = reactive({ | |
41 | + startSearch: false, | |
42 | + searchData: [] as TreeItem[], | |
43 | + }); | |
44 | + | |
45 | 45 | const treeDataRef = ref<TreeItem[]>([]); |
46 | 46 | |
47 | 47 | const [createContextMenu] = useContextMenu(); |
... | ... | @@ -77,6 +77,7 @@ |
77 | 77 | expandedKeys: state.expandedKeys, |
78 | 78 | selectedKeys: state.selectedKeys, |
79 | 79 | checkedKeys: state.checkedKeys, |
80 | + checkStrictly: state.checkStrictly, | |
80 | 81 | replaceFields: unref(getReplaceFields), |
81 | 82 | 'onUpdate:expandedKeys': (v: Keys) => { |
82 | 83 | state.expandedKeys = v; |
... | ... | @@ -88,21 +89,27 @@ |
88 | 89 | }, |
89 | 90 | onCheck: (v: CheckKeys) => { |
90 | 91 | state.checkedKeys = v; |
92 | + emit('change', v); | |
91 | 93 | emit('update:value', v); |
92 | 94 | }, |
93 | 95 | onRightClick: handleRightClick, |
94 | 96 | }; |
95 | - propsData = omit(propsData, 'treeData'); | |
97 | + propsData = omit(propsData, 'treeData', 'class'); | |
96 | 98 | return propsData; |
97 | 99 | }); |
98 | 100 | |
99 | - const getTreeData = computed((): TreeItem[] => unref(treeDataRef)); | |
100 | - | |
101 | - const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree( | |
102 | - treeDataRef, | |
103 | - getReplaceFields | |
101 | + const getTreeData = computed((): TreeItem[] => | |
102 | + searchState.startSearch ? searchState.searchData : unref(treeDataRef) | |
104 | 103 | ); |
105 | 104 | |
105 | + const { | |
106 | + deleteNodeByKey, | |
107 | + insertNodeByKey, | |
108 | + filterByLevel, | |
109 | + updateNodeByKey, | |
110 | + getAllKeys, | |
111 | + } = useTree(treeDataRef, getReplaceFields); | |
112 | + | |
106 | 113 | function getIcon(params: Recordable, icon?: string) { |
107 | 114 | if (!icon) { |
108 | 115 | if (props.renderIcon && isFunction(props.renderIcon)) { |
... | ... | @@ -112,60 +119,6 @@ |
112 | 119 | return icon; |
113 | 120 | } |
114 | 121 | |
115 | - function renderAction(node: TreeItem) { | |
116 | - const { actionList } = props; | |
117 | - if (!actionList || actionList.length === 0) return; | |
118 | - return actionList.map((item, index) => { | |
119 | - if (isFunction(item.show)) { | |
120 | - return item.show?.(node); | |
121 | - } | |
122 | - | |
123 | - if (isBoolean(item.show)) { | |
124 | - return item.show; | |
125 | - } | |
126 | - | |
127 | - return ( | |
128 | - <span key={index} class={`${prefixCls}__action`}> | |
129 | - {item.render(node)} | |
130 | - </span> | |
131 | - ); | |
132 | - }); | |
133 | - } | |
134 | - | |
135 | - function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) { | |
136 | - if (!data) { | |
137 | - return null; | |
138 | - } | |
139 | - return data.map((item) => { | |
140 | - const { title: titleField, key: keyField, children: childrenField } = unref( | |
141 | - getReplaceFields | |
142 | - ); | |
143 | - | |
144 | - const propsData = omit(item, 'title'); | |
145 | - const icon = getIcon({ ...item, level }, item.icon); | |
146 | - return ( | |
147 | - <Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}> | |
148 | - {{ | |
149 | - title: () => ( | |
150 | - <span class={`${prefixCls}-title`}> | |
151 | - {icon && <TreeIcon icon={icon} />} | |
152 | - <span | |
153 | - class={`${prefixCls}__content`} | |
154 | - // style={unref(getContentStyle)} | |
155 | - > | |
156 | - {get(item, titleField)} | |
157 | - </span> | |
158 | - <span class={`${prefixCls}__actions`}> {renderAction({ ...item, level })}</span> | |
159 | - </span> | |
160 | - ), | |
161 | - default: () => | |
162 | - renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }), | |
163 | - }} | |
164 | - </Tree.TreeNode> | |
165 | - ); | |
166 | - }); | |
167 | - } | |
168 | - | |
169 | 122 | async function handleRightClick({ event, node }: any) { |
170 | 123 | const { rightMenuList: menuList = [], beforeRightClick } = props; |
171 | 124 | let rightMenuList: ContextMenuItem[] = []; |
... | ... | @@ -205,6 +158,32 @@ |
205 | 158 | return state.checkedKeys; |
206 | 159 | } |
207 | 160 | |
161 | + function checkAll(checkAll: boolean) { | |
162 | + state.checkedKeys = checkAll ? getAllKeys() : ([] as Keys); | |
163 | + } | |
164 | + | |
165 | + function expandAll(expandAll: boolean) { | |
166 | + state.expandedKeys = expandAll ? getAllKeys() : ([] as Keys); | |
167 | + } | |
168 | + | |
169 | + function onStrictlyChange(strictly: boolean) { | |
170 | + state.checkStrictly = strictly; | |
171 | + } | |
172 | + | |
173 | + function handleSearch(searchValue: string) { | |
174 | + if (!searchValue) { | |
175 | + searchState.startSearch = false; | |
176 | + return; | |
177 | + } | |
178 | + searchState.startSearch = true; | |
179 | + | |
180 | + searchState.searchData = filter(unref(treeDataRef), (node) => { | |
181 | + const { title } = node; | |
182 | + return title?.includes(searchValue) ?? false; | |
183 | + // || key?.includes(searchValue); | |
184 | + }); | |
185 | + } | |
186 | + | |
208 | 187 | watchEffect(() => { |
209 | 188 | treeDataRef.value = props.treeData as TreeItem[]; |
210 | 189 | state.expandedKeys = props.expandedKeys; |
... | ... | @@ -212,6 +191,16 @@ |
212 | 191 | state.checkedKeys = props.checkedKeys; |
213 | 192 | }); |
214 | 193 | |
194 | + watchEffect(() => { | |
195 | + if (props.value) { | |
196 | + state.checkedKeys = props.value; | |
197 | + } | |
198 | + }); | |
199 | + | |
200 | + watchEffect(() => { | |
201 | + state.checkStrictly = props.checkStrictly; | |
202 | + }); | |
203 | + | |
215 | 204 | const instance: TreeActionType = { |
216 | 205 | setExpandedKeys, |
217 | 206 | getExpandedKeys, |
... | ... | @@ -222,6 +211,8 @@ |
222 | 211 | insertNodeByKey, |
223 | 212 | deleteNodeByKey, |
224 | 213 | updateNodeByKey, |
214 | + checkAll, | |
215 | + expandAll, | |
225 | 216 | filterByLevel: (level: number) => { |
226 | 217 | state.expandedKeys = filterByLevel(level); |
227 | 218 | }, |
... | ... | @@ -229,19 +220,83 @@ |
229 | 220 | |
230 | 221 | useExpose<TreeActionType>(instance); |
231 | 222 | |
232 | - onMounted(() => { | |
233 | - emit('get', instance); | |
234 | - }); | |
223 | + function renderAction(node: TreeItem) { | |
224 | + const { actionList } = props; | |
225 | + if (!actionList || actionList.length === 0) return; | |
226 | + return actionList.map((item, index) => { | |
227 | + if (isFunction(item.show)) { | |
228 | + return item.show?.(node); | |
229 | + } | |
230 | + | |
231 | + if (isBoolean(item.show)) { | |
232 | + return item.show; | |
233 | + } | |
234 | + | |
235 | + return ( | |
236 | + <span key={index} class={`${prefixCls}__action`}> | |
237 | + {item.render(node)} | |
238 | + </span> | |
239 | + ); | |
240 | + }); | |
241 | + } | |
242 | + | |
243 | + function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) { | |
244 | + if (!data) { | |
245 | + return null; | |
246 | + } | |
247 | + return data.map((item) => { | |
248 | + const { title: titleField, key: keyField, children: childrenField } = unref( | |
249 | + getReplaceFields | |
250 | + ); | |
235 | 251 | |
252 | + const propsData = omit(item, 'title'); | |
253 | + const icon = getIcon({ ...item, level }, item.icon); | |
254 | + return ( | |
255 | + <Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}> | |
256 | + {{ | |
257 | + title: () => ( | |
258 | + <span class={`${prefixCls}-title pl-2`}> | |
259 | + {icon && <TreeIcon icon={icon} />} | |
260 | + <span | |
261 | + class={`${prefixCls}__content`} | |
262 | + // style={unref(getContentStyle)} | |
263 | + > | |
264 | + {get(item, titleField)} | |
265 | + </span> | |
266 | + <span class={`${prefixCls}__actions`}> {renderAction({ ...item, level })}</span> | |
267 | + </span> | |
268 | + ), | |
269 | + default: () => | |
270 | + renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }), | |
271 | + }} | |
272 | + </Tree.TreeNode> | |
273 | + ); | |
274 | + }); | |
275 | + } | |
236 | 276 | return () => { |
277 | + const { title, helpMessage, toolbar, search } = props; | |
237 | 278 | return ( |
238 | - <Tree {...unref(getBindValues)} showIcon={false} class={[prefixCls]}> | |
239 | - {{ | |
240 | - // switcherIcon: () => <DownOutlined />, | |
241 | - default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }), | |
242 | - ...extendSlots(slots), | |
243 | - }} | |
244 | - </Tree> | |
279 | + <div class={[prefixCls, 'h-full bg-white']}> | |
280 | + {(title || toolbar || search) && ( | |
281 | + <TreeHeader | |
282 | + checkAll={checkAll} | |
283 | + expandAll={expandAll} | |
284 | + title={title} | |
285 | + search={search} | |
286 | + toolbar={toolbar} | |
287 | + helpMessage={helpMessage} | |
288 | + onStrictlyChange={onStrictlyChange} | |
289 | + onSearch={handleSearch} | |
290 | + /> | |
291 | + )} | |
292 | + <Tree {...unref(getBindValues)} showIcon={false}> | |
293 | + {{ | |
294 | + // switcherIcon: () => <DownOutlined />, | |
295 | + default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }), | |
296 | + ...extendSlots(slots), | |
297 | + }} | |
298 | + </Tree> | |
299 | + </div> | |
245 | 300 | ); |
246 | 301 | }; |
247 | 302 | }, |
... | ... | @@ -251,8 +306,6 @@ |
251 | 306 | @prefix-cls: ~'@{namespace}-basic-tree'; |
252 | 307 | |
253 | 308 | .@{prefix-cls} { |
254 | - position: relative; | |
255 | - | |
256 | 309 | .ant-tree-node-content-wrapper { |
257 | 310 | position: relative; |
258 | 311 | |
... | ... | @@ -278,14 +331,14 @@ |
278 | 331 | } |
279 | 332 | |
280 | 333 | &__content { |
281 | - display: inline-block; | |
334 | + // display: inline-block; | |
282 | 335 | overflow: hidden; |
283 | 336 | } |
284 | 337 | |
285 | 338 | &__actions { |
286 | 339 | position: absolute; |
287 | 340 | top: 2px; |
288 | - right: 2px; | |
341 | + right: 3px; | |
289 | 342 | display: flex; |
290 | 343 | } |
291 | 344 | ... | ... |
src/components/Tree/src/props.ts
... | ... | @@ -2,11 +2,26 @@ import type { PropType } from 'vue'; |
2 | 2 | import type { ReplaceFields, ActionItem, Keys, CheckKeys } from './types'; |
3 | 3 | import type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; |
4 | 4 | import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; |
5 | +import { propTypes } from '/@/utils/propTypes'; | |
5 | 6 | |
6 | 7 | export const basicProps = { |
8 | + value: { | |
9 | + type: Array as PropType<Keys>, | |
10 | + }, | |
7 | 11 | renderIcon: { |
8 | 12 | type: Function as PropType<(params: Recordable) => string>, |
9 | 13 | }, |
14 | + | |
15 | + helpMessage: { | |
16 | + type: [String, Array] as PropType<string | string[]>, | |
17 | + default: '', | |
18 | + }, | |
19 | + | |
20 | + title: propTypes.string, | |
21 | + toolbar: propTypes.bool, | |
22 | + search: propTypes.bool, | |
23 | + checkStrictly: propTypes.bool, | |
24 | + | |
10 | 25 | replaceFields: { |
11 | 26 | type: Object as PropType<ReplaceFields>, |
12 | 27 | }, | ... | ... |
src/components/Tree/src/types.ts
... | ... | @@ -21,6 +21,8 @@ export type CheckKeys = |
21 | 21 | | { checked: string[] | number[]; halfChecked: string[] | number[] }; |
22 | 22 | |
23 | 23 | export interface TreeActionType { |
24 | + checkAll: (checkAll: boolean) => void; | |
25 | + expandAll: (expandAll: boolean) => void; | |
24 | 26 | setExpandedKeys: (keys: Keys) => void; |
25 | 27 | getExpandedKeys: () => Keys; |
26 | 28 | setSelectedKeys: (keys: Keys) => void; | ... | ... |
src/components/Tree/src/useTree.ts
1 | -import type { InsertNodeParams, ReplaceFields } from './types'; | |
1 | +import type { InsertNodeParams, Keys, ReplaceFields } from './types'; | |
2 | 2 | import type { Ref, ComputedRef } from 'vue'; |
3 | 3 | import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; |
4 | 4 | |
... | ... | @@ -10,6 +10,23 @@ export function useTree( |
10 | 10 | treeDataRef: Ref<TreeDataItem[]>, |
11 | 11 | getReplaceFields: ComputedRef<ReplaceFields> |
12 | 12 | ) { |
13 | + function getAllKeys(list?: TreeDataItem[]) { | |
14 | + const keys: string[] = []; | |
15 | + const treeData = list || unref(treeDataRef); | |
16 | + const { key: keyField, children: childrenField } = unref(getReplaceFields); | |
17 | + if (!childrenField || !keyField) return keys; | |
18 | + | |
19 | + for (let index = 0; index < treeData.length; index++) { | |
20 | + const node = treeData[index]; | |
21 | + keys.push(node[keyField]!); | |
22 | + const children = node[childrenField]; | |
23 | + if (children && children.length) { | |
24 | + keys.push(...(getAllKeys(children) as string[])); | |
25 | + } | |
26 | + } | |
27 | + return keys as Keys; | |
28 | + } | |
29 | + | |
13 | 30 | // Update node |
14 | 31 | function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) { |
15 | 32 | if (!key) return; |
... | ... | @@ -94,5 +111,5 @@ export function useTree( |
94 | 111 | } |
95 | 112 | } |
96 | 113 | } |
97 | - return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey }; | |
114 | + return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey, getAllKeys }; | |
98 | 115 | } | ... | ... |
src/locales/lang/en/component/tree.ts
0 → 100644
src/locales/lang/en/routes/demo/comp.ts
src/locales/lang/zh_CN/component/tree.ts
0 → 100644
src/locales/lang/zh_CN/routes/demo/comp.ts
src/router/menus/modules/demo/comp.ts
... | ... | @@ -6,7 +6,10 @@ const menu: MenuModule = { |
6 | 6 | menu: { |
7 | 7 | name: t('routes.demo.comp.comp'), |
8 | 8 | path: '/comp', |
9 | - | |
9 | + tag: { | |
10 | + dot: true, | |
11 | + type: 'warn', | |
12 | + }, | |
10 | 13 | children: [ |
11 | 14 | { |
12 | 15 | path: 'basic', |
... | ... | @@ -154,6 +157,10 @@ const menu: MenuModule = { |
154 | 157 | { |
155 | 158 | path: 'tree', |
156 | 159 | name: t('routes.demo.comp.tree'), |
160 | + tag: { | |
161 | + dot: true, | |
162 | + type: 'warn', | |
163 | + }, | |
157 | 164 | children: [ |
158 | 165 | { |
159 | 166 | path: 'basic', |
... | ... | @@ -162,6 +169,10 @@ const menu: MenuModule = { |
162 | 169 | { |
163 | 170 | path: 'editTree', |
164 | 171 | name: t('routes.demo.comp.editTree'), |
172 | + tag: { | |
173 | + dot: true, | |
174 | + type: 'warn', | |
175 | + }, | |
165 | 176 | }, |
166 | 177 | { |
167 | 178 | path: 'actionTree', |
... | ... | @@ -172,9 +183,6 @@ const menu: MenuModule = { |
172 | 183 | { |
173 | 184 | name: t('routes.demo.editor.editor'), |
174 | 185 | path: 'editor', |
175 | - tag: { | |
176 | - content: 'new', | |
177 | - }, | |
178 | 186 | children: [ |
179 | 187 | { |
180 | 188 | path: 'markdown', | ... | ... |
src/router/menus/modules/demo/system.ts
... | ... | @@ -22,21 +22,21 @@ const menu: MenuModule = { |
22 | 22 | path: 'role', |
23 | 23 | name: t('routes.demo.system.role'), |
24 | 24 | tag: { |
25 | - content: 'new', | |
25 | + dot: true, | |
26 | 26 | }, |
27 | 27 | }, |
28 | 28 | { |
29 | 29 | path: 'menu', |
30 | 30 | name: t('routes.demo.system.menu'), |
31 | 31 | tag: { |
32 | - content: 'new', | |
32 | + dot: true, | |
33 | 33 | }, |
34 | 34 | }, |
35 | 35 | { |
36 | 36 | path: 'dept', |
37 | 37 | name: t('routes.demo.system.dept'), |
38 | 38 | tag: { |
39 | - content: 'new', | |
39 | + dot: true, | |
40 | 40 | }, |
41 | 41 | }, |
42 | 42 | |
... | ... | @@ -44,7 +44,7 @@ const menu: MenuModule = { |
44 | 44 | path: 'changePassword', |
45 | 45 | name: t('routes.demo.system.password'), |
46 | 46 | tag: { |
47 | - content: 'new', | |
47 | + dot: true, | |
48 | 48 | }, |
49 | 49 | }, |
50 | 50 | ], | ... | ... |
src/store/modules/permission.ts
... | ... | @@ -18,7 +18,7 @@ import { transformObjToRoute } from '/@/router/helper/routeHelper'; |
18 | 18 | import { transformRouteToMenu } from '/@/router/helper/menuHelper'; |
19 | 19 | |
20 | 20 | import { useMessage } from '/@/hooks/web/useMessage'; |
21 | -import { useI18n } from '/@/hooks/web/useI18n'; | |
21 | +// import { useI18n } from '/@/hooks/web/useI18n'; | |
22 | 22 | import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; |
23 | 23 | |
24 | 24 | const { createMessage } = useMessage(); |
... | ... | @@ -84,7 +84,7 @@ class Permission extends VuexModule { |
84 | 84 | |
85 | 85 | @Action |
86 | 86 | async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> { |
87 | - const { t } = useI18n(); | |
87 | + // const { t } = useI18n(); | |
88 | 88 | let routes: AppRouteRecordRaw[] = []; |
89 | 89 | const roleList = toRaw(userStore.getRoleListState); |
90 | 90 | |
... | ... | @@ -101,7 +101,8 @@ class Permission extends VuexModule { |
101 | 101 | // If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below |
102 | 102 | } else if (permissionMode === PermissionModeEnum.BACK) { |
103 | 103 | createMessage.loading({ |
104 | - content: t('sys.app.menuLoading'), | |
104 | + content: 'Loading menu...', | |
105 | + // content: 't('sys.app.menuLoading')', | |
105 | 106 | duration: 1, |
106 | 107 | }); |
107 | 108 | // Here to get the background routing menu logic to modify by yourself | ... | ... |
src/views/demo/tree/ActionTree.vue
1 | 1 | <template> |
2 | 2 | <PageWrapper title="Tree函数操作示例" contentBackground contentClass="p-4"> |
3 | 3 | <div class="mb-4"> |
4 | + <a-button @click="expandAll(true)" class="mr-2"> 展开全部 </a-button> | |
5 | + <a-button @click="expandAll(false)" class="mr-2"> 折叠全部 </a-button> | |
6 | + <a-button @click="checkAll(true)" class="mr-2"> 全选 </a-button> | |
7 | + <a-button @click="checkAll(false)" class="mr-2"> 全不选 </a-button> | |
4 | 8 | <a-button @click="handleLevel(2)" class="mr-2"> 显示到第2级 </a-button> |
5 | 9 | <a-button @click="handleLevel(1)" class="mr-2"> 显示到第1级 </a-button> |
10 | + </div> | |
11 | + <div class="mb-4"> | |
6 | 12 | <a-button @click="handleSetCheckData" class="mr-2"> 设置勾选数据 </a-button> |
7 | 13 | <a-button @click="handleGetCheckData" class="mr-2"> 获取勾选数据 </a-button> |
8 | 14 | <a-button @click="handleSetSelectData" class="mr-2"> 设置选中数据 </a-button> |
... | ... | @@ -17,21 +23,18 @@ |
17 | 23 | <a-button @click="deleteNodeByKey('2-2')" class="mr-2"> 删除parent3节点 </a-button> |
18 | 24 | <a-button @click="updateNodeByKey('1-1')" class="mr-2"> 更新parent2节点 </a-button> |
19 | 25 | </div> |
20 | - <CollapseContainer title="函数操作" class="mr-4" :canExpan="false" :style="{ width: '33%' }"> | |
21 | - <BasicTree :treeData="treeData" ref="treeRef" :checkable="true" /> | |
22 | - </CollapseContainer> | |
26 | + <BasicTree :treeData="treeData" title="函数操作" ref="treeRef" :checkable="true" /> | |
23 | 27 | </PageWrapper> |
24 | 28 | </template> |
25 | 29 | <script lang="ts"> |
26 | 30 | import { defineComponent, ref, unref } from 'vue'; |
27 | 31 | import { BasicTree, TreeActionType } from '/@/components/Tree/index'; |
28 | 32 | import { treeData } from './data'; |
29 | - import { CollapseContainer } from '/@/components/Container/index'; | |
30 | 33 | import { useMessage } from '/@/hooks/web/useMessage'; |
31 | 34 | import { PageWrapper } from '/@/components/Page'; |
32 | 35 | |
33 | 36 | export default defineComponent({ |
34 | - components: { BasicTree, CollapseContainer, PageWrapper }, | |
37 | + components: { BasicTree, PageWrapper }, | |
35 | 38 | setup() { |
36 | 39 | const treeRef = ref<Nullable<TreeActionType>>(null); |
37 | 40 | const { createMessage } = useMessage(); |
... | ... | @@ -75,6 +78,14 @@ |
75 | 78 | createMessage.success(JSON.stringify(keys)); |
76 | 79 | } |
77 | 80 | |
81 | + function checkAll(checkAll: boolean) { | |
82 | + getTree().checkAll(checkAll); | |
83 | + } | |
84 | + | |
85 | + function expandAll(checkAll: boolean) { | |
86 | + getTree().expandAll(checkAll); | |
87 | + } | |
88 | + | |
78 | 89 | function appendNodeByKey(parentKey: string | null = null) { |
79 | 90 | getTree().insertNodeByKey({ |
80 | 91 | parentKey: parentKey, |
... | ... | @@ -112,6 +123,8 @@ |
112 | 123 | appendNodeByKey, |
113 | 124 | deleteNodeByKey, |
114 | 125 | updateNodeByKey, |
126 | + checkAll, | |
127 | + expandAll, | |
115 | 128 | }; |
116 | 129 | }, |
117 | 130 | }); | ... | ... |
src/views/demo/tree/EditTree.vue
1 | 1 | <template> |
2 | 2 | <PageWrapper title="Tree函数操作示例"> |
3 | 3 | <div class="flex"> |
4 | - <CollapseContainer title="右侧操作按钮/自定义图标" class="mr-4" :style="{ width: '33%' }"> | |
5 | - <BasicTree :treeData="treeData" :actionList="actionList" :renderIcon="createIcon" /> | |
6 | - </CollapseContainer> | |
7 | - | |
8 | - <CollapseContainer title="右键菜单" class="mr-4" :style="{ width: '33%' }"> | |
9 | - <BasicTree :treeData="treeData" :beforeRightClick="getRightMenuList" /> | |
10 | - </CollapseContainer> | |
4 | + <BasicTree | |
5 | + class="w-1/3" | |
6 | + title="右侧操作按钮/自定义图标" | |
7 | + helpMessage="帮助信息" | |
8 | + :treeData="treeData" | |
9 | + :actionList="actionList" | |
10 | + :renderIcon="createIcon" | |
11 | + /> | |
12 | + <BasicTree | |
13 | + class="w-1/3 mx-4" | |
14 | + title="右键菜单" | |
15 | + :treeData="treeData" | |
16 | + :beforeRightClick="getRightMenuList" | |
17 | + /> | |
18 | + <BasicTree | |
19 | + class="w-1/3" | |
20 | + title="工具栏使用" | |
21 | + toolbar | |
22 | + checkable | |
23 | + search | |
24 | + :treeData="treeData" | |
25 | + :beforeRightClick="getRightMenuList" | |
26 | + /> | |
11 | 27 | </div> |
12 | 28 | </PageWrapper> |
13 | 29 | </template> |
... | ... | @@ -15,12 +31,11 @@ |
15 | 31 | import { defineComponent, h } from 'vue'; |
16 | 32 | import { BasicTree, ActionItem, ContextMenuItem } from '/@/components/Tree/index'; |
17 | 33 | import { treeData } from './data'; |
18 | - import { CollapseContainer } from '/@/components/Container/index'; | |
19 | 34 | import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue'; |
20 | 35 | import { PageWrapper } from '/@/components/Page'; |
21 | 36 | |
22 | 37 | export default defineComponent({ |
23 | - components: { BasicTree, CollapseContainer, PageWrapper }, | |
38 | + components: { BasicTree, PageWrapper }, | |
24 | 39 | setup() { |
25 | 40 | function handlePlus(node: any) { |
26 | 41 | console.log(node); | ... | ... |
src/views/demo/tree/index.vue
1 | 1 | <template> |
2 | 2 | <PageWrapper title="Tree基础示例"> |
3 | 3 | <div class="flex"> |
4 | - <CollapseContainer title="基础示例" :style="{ width: '33%' }" class="mr-4"> | |
5 | - <BasicTree :treeData="treeData" /> | |
6 | - </CollapseContainer> | |
4 | + <BasicTree :treeData="treeData" title="基础示例" class="w-1/3" /> | |
7 | 5 | |
8 | - <CollapseContainer title="可勾选" class="mr-4" :style="{ width: '33%' }"> | |
9 | - <BasicTree :treeData="treeData" :checkable="true" /> | |
10 | - </CollapseContainer> | |
6 | + <BasicTree :treeData="treeData" title="可勾选" :checkable="true" class="w-1/3 mx-4" /> | |
11 | 7 | |
12 | - <CollapseContainer title="默认展开/勾选示例" :style="{ width: '33%' }"> | |
13 | - <BasicTree | |
14 | - :treeData="treeData" | |
15 | - :checkable="true" | |
16 | - :expandedKeys="['0-0']" | |
17 | - :checkedKeys="['0-0']" | |
18 | - /> | |
19 | - </CollapseContainer> | |
8 | + <BasicTree | |
9 | + title="默认展开/勾选示例" | |
10 | + :treeData="treeData" | |
11 | + :checkable="true" | |
12 | + :expandedKeys="['0-0']" | |
13 | + :checkedKeys="['0-0']" | |
14 | + class="w-1/3" | |
15 | + /> | |
20 | 16 | </div> |
21 | 17 | </PageWrapper> |
22 | 18 | </template> |
... | ... | @@ -24,11 +20,10 @@ |
24 | 20 | import { defineComponent } from 'vue'; |
25 | 21 | import { BasicTree } from '/@/components/Tree/index'; |
26 | 22 | import { treeData } from './data'; |
27 | - import { CollapseContainer } from '/@/components/Container/index'; | |
28 | 23 | import { PageWrapper } from '/@/components/Page'; |
29 | 24 | |
30 | 25 | export default defineComponent({ |
31 | - components: { BasicTree, CollapseContainer, PageWrapper }, | |
26 | + components: { BasicTree, PageWrapper }, | |
32 | 27 | setup() { |
33 | 28 | return { treeData }; |
34 | 29 | }, | ... | ... |
yarn.lock
... | ... | @@ -1791,10 +1791,10 @@ |
1791 | 1791 | micromatch "^4.0.2" |
1792 | 1792 | windicss "^2.2.3" |
1793 | 1793 | |
1794 | -"@zxcvbn-ts/core@^0.2.0": | |
1795 | - version "0.2.0" | |
1796 | - resolved "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-0.2.0.tgz#ba3af1fed2213464ae12c0ab565798590afe8ef7" | |
1797 | - integrity sha512-1NVKw2Tz3Iv3NE4RFTTcF2EQlmHfkNi48U0H80ZR/KLt3ANOFsCDp/mxGawdzCnBrf64E121xI49mpZDAACYZw== | |
1794 | +"@zxcvbn-ts/core@^0.3.0": | |
1795 | + version "0.3.0" | |
1796 | + resolved "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-0.3.0.tgz#1a021afef29b97a5f8f72458de005fa149628e32" | |
1797 | + integrity sha512-H1SOAoC7MbccN/CU9ENZHXwvwTwh6aRt88SOkGROAN9nT88o/qDPJ5B5bElRSbjKLfmmO1LqK2K4u2lUxjbQKQ== | |
1798 | 1798 | |
1799 | 1799 | JSONStream@^1.0.4: |
1800 | 1800 | version "1.3.5" | ... | ... |