diff --git a/README.md b/README.md index b39b305d..d686aa65 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Vue Vben Admin is a free and open source middle and back-end template. Using the - **Authority** Built-in complete dynamic routing permission generation scheme. - **Component** Multiple commonly used components are encapsulated twice -## 预览 +## Preview - [vue-vben-admin](https://vvbin.cn/next/) - Full version Chinese site - [vue-vben-admin-gh-pages](https://anncwb.github.io/vue-vben-admin/) - Full version of the github site diff --git a/build/vite/plugin/styleImport.ts b/build/vite/plugin/styleImport.ts index 5f96746c..1f606c4d 100644 --- a/build/vite/plugin/styleImport.ts +++ b/build/vite/plugin/styleImport.ts @@ -64,6 +64,7 @@ export function configStyleImportPlugin(_isBuild: boolean) { 'layout-footer': 'layout', 'layout-header': 'layout', 'month-picker': 'date-picker', + 'range-picker': 'date-picker', }; return ignoreList.includes(name) diff --git a/package.json b/package.json index c23cfcff..2974310b 100644 --- a/package.json +++ b/package.json @@ -35,19 +35,19 @@ "@ant-design/colors": "^6.0.0", "@ant-design/icons-vue": "^6.1.0", "@iconify/iconify": "^2.2.1", - "@logicflow/core": "^1.1.11", - "@logicflow/extension": "^1.1.11", - "@vue/runtime-core": "^3.2.31", - "@vue/shared": "^3.2.31", - "@vueuse/core": "^8.2.5", - "@vueuse/shared": "^8.2.5", + "@logicflow/core": "^1.1.13", + "@logicflow/extension": "^1.1.13", + "@vue/runtime-core": "^3.2.33", + "@vue/shared": "^3.2.33", + "@vueuse/core": "^8.3.0", + "@vueuse/shared": "^8.3.0", "@zxcvbn-ts/core": "^2.0.1", - "ant-design-vue": "3.1.1", + "ant-design-vue": "^3.2.0", "axios": "^0.26.1", - "codemirror": "^5.65.2", + "codemirror": "^5.65.3", "cropperjs": "^1.5.12", "crypto-js": "^4.1.1", - "dayjs": "^1.11.0", + "dayjs": "^1.11.1", "echarts": "^5.3.2", "intro.js": "^5.1.0", "lodash-es": "^4.17.21", @@ -59,11 +59,11 @@ "qrcode": "^1.5.0", "qs": "^6.10.3", "resize-observer-polyfill": "^1.5.1", - "showdown": "^2.0.3", + "showdown": "^2.1.0", "sortablejs": "^1.15.0", "tinymce": "^5.10.3", "vditor": "^3.8.13", - "vue": "^3.2.31", + "vue": "^3.2.33", "vue-i18n": "^9.1.9", "vue-json-pretty": "^2.0.6", "vue-router": "^4.0.14", @@ -75,7 +75,7 @@ "devDependencies": { "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", - "@iconify/json": "^2.1.25", + "@iconify/json": "^2.1.30", "@purge-icons/generated": "^0.8.1", "@types/codemirror": "^5.60.5", "@types/crypto-js": "^4.1.1", @@ -84,30 +84,30 @@ "@types/intro.js": "^3.0.2", "@types/lodash-es": "^4.17.6", "@types/mockjs": "^1.0.6", - "@types/node": "^17.0.23", + "@types/node": "^17.0.25", "@types/nprogress": "^0.2.0", "@types/qrcode": "^1.4.2", "@types/qs": "^6.9.7", "@types/showdown": "^1.9.4", "@types/sortablejs": "^1.10.7", - "@typescript-eslint/eslint-plugin": "^5.18.0", - "@typescript-eslint/parser": "^5.18.0", - "@vitejs/plugin-legacy": "^1.8.0", + "@typescript-eslint/eslint-plugin": "^5.20.0", + "@typescript-eslint/parser": "^5.20.0", + "@vitejs/plugin-legacy": "^1.8.1", "@vitejs/plugin-vue": "^2.3.1", - "@vitejs/plugin-vue-jsx": "^1.3.9", - "@vue/compiler-sfc": "3.2.31", - "@vue/test-utils": "^2.0.0-rc.19", + "@vitejs/plugin-vue-jsx": "^1.3.10", + "@vue/compiler-sfc": "^3.2.33", + "@vue/test-utils": "^2.0.0-rc.21", "autoprefixer": "^10.4.4", "commitizen": "^4.2.4", "conventional-changelog-cli": "^2.2.2", "cross-env": "^7.0.3", "dotenv": "^16.0.0", - "eslint": "^8.12.0", + "eslint": "^8.13.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-vue": "^8.6.0", "esno": "^0.14.1", - "fs-extra": "^10.0.1", + "fs-extra": "^10.1.0", "husky": "^7.0.4", "inquirer": "^8.2.2", "less": "^4.1.2", @@ -115,13 +115,13 @@ "npm-run-all": "^4.1.5", "picocolors": "^1.0.0", "postcss": "^8.4.12", - "postcss-html": "^1.3.0", + "postcss-html": "^1.4.1", "postcss-less": "^6.0.0", "prettier": "^2.6.2", "rimraf": "^3.0.2", - "rollup": "^2.70.1", + "rollup": "^2.70.2", "rollup-plugin-visualizer": "^5.6.0", - "stylelint": "^14.6.1", + "stylelint": "^14.7.1", "stylelint-config-prettier": "^9.0.3", "stylelint-config-recommended": "^7.0.0", "stylelint-config-recommended-vue": "^1.4.0", @@ -129,7 +129,7 @@ "stylelint-order": "^5.0.0", "ts-node": "^10.7.0", "typescript": "^4.6.3", - "vite": "^2.9.1", + "vite": "^2.9.5", "vite-plugin-compression": "^0.5.1", "vite-plugin-html": "^3.2.0", "vite-plugin-imagemin": "^0.6.1", @@ -141,7 +141,7 @@ "vite-plugin-svg-icons": "^2.0.1", "vite-plugin-theme": "^0.8.6", "vite-plugin-vue-setup-extend": "^0.4.0", - "vite-plugin-windicss": "^1.8.3", + "vite-plugin-windicss": "^1.8.4", "vue-eslint-parser": "^8.3.0", "vue-tsc": "^0.33.9" }, diff --git a/src/components/Form/src/components/ApiTree.vue b/src/components/Form/src/components/ApiTree.vue index b81d76b5..0ec6917d 100644 --- a/src/components/Form/src/components/ApiTree.vue +++ b/src/components/Form/src/components/ApiTree.vue @@ -24,6 +24,7 @@ params: { type: Object }, immediate: { type: Boolean, default: true }, resultField: propTypes.string.def(''), + afterFetch: { type: Function as PropType }, }, emits: ['options-change', 'change'], setup(props, { attrs, emit }) { @@ -61,7 +62,7 @@ }); async function fetch() { - const { api } = props; + const { api, afterFetch } = props; if (!api || !isFunction(api)) return; loading.value = true; treeData.value = []; @@ -71,6 +72,9 @@ } catch (e) { console.error(e); } + if (afterFetch && isFunction(afterFetch)) { + result = afterFetch(result); + } loading.value = false; if (!result) return; if (!isArray(result)) { diff --git a/src/components/Form/src/hooks/useFormEvents.ts b/src/components/Form/src/hooks/useFormEvents.ts index ac10f32a..86946b44 100644 --- a/src/components/Form/src/hooks/useFormEvents.ts +++ b/src/components/Form/src/hooks/useFormEvents.ts @@ -2,7 +2,7 @@ import type { ComputedRef, Ref } from 'vue'; import type { FormProps, FormSchema, FormActionType } from '../types/form'; import type { NamePath } from 'ant-design-vue/lib/form/interface'; import { unref, toRaw, nextTick } from 'vue'; -import { isArray, isFunction, isNullOrUnDef, isObject, isString } from '/@/utils/is'; +import { isArray, isFunction, isObject, isString, isDef } from '/@/utils/is'; import { deepMerge } from '/@/utils'; import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper'; import { dateUtil } from '/@/utils/dateUtil'; @@ -55,6 +55,10 @@ export function useFormEvents({ .map((item) => item.field) .filter(Boolean); + // key 支持 a.b.c 的嵌套写法 + const delimiter = '.'; + const nestKeyArray = fields.filter((item) => item.indexOf(delimiter) >= 0); + const validKeys: string[] = []; Object.keys(values).forEach((key) => { const schema = unref(getSchema).find((item) => item.field === key); @@ -85,6 +89,21 @@ export function useFormEvents({ formModel[key] = value; } validKeys.push(key); + } else { + nestKeyArray.forEach((nestKey: string) => { + try { + const value = eval('values' + delimiter + nestKey); + if (isDef(value)) { + formModel[nestKey] = value; + validKeys.push(nestKey); + } + } catch (e) { + // key not exist + if (isDef(defaultValueRef.value[nestKey])) { + formModel[nestKey] = defaultValueRef.value[nestKey]; + } + } + }); } }); validateFields(validKeys).catch((_) => {}); diff --git a/src/components/Table/src/types/pagination.ts b/src/components/Table/src/types/pagination.ts index fd2ecbe3..c705f33f 100644 --- a/src/components/Table/src/types/pagination.ts +++ b/src/components/Table/src/types/pagination.ts @@ -7,9 +7,18 @@ interface PaginationRenderProps { originalElement: any; } +type PaginationPositon = + | 'topLeft' + | 'topCenter' + | 'topRight' + | 'bottomLeft' + | 'bottomCenter' + | 'bottomRight'; + export declare class PaginationConfig extends Pagination { - position?: 'top' | 'bottom' | 'both'; + position?: PaginationPositon[]; } + export interface PaginationProps { /** * total number of data items @@ -96,4 +105,11 @@ export interface PaginationProps { * @type Function */ itemRender?: (props: PaginationRenderProps) => VNodeChild | JSX.Element; + + /** + * specify the position of Pagination + * @default ['bottomRight'] + * @type string[] + */ + position?: PaginationPositon[]; } diff --git a/src/components/Tree/src/Tree.vue b/src/components/Tree/src/Tree.vue index 5942e578..4e58e1ee 100644 --- a/src/components/Tree/src/Tree.vue +++ b/src/components/Tree/src/Tree.vue @@ -71,7 +71,7 @@ selectedKeys: state.selectedKeys, checkedKeys: state.checkedKeys, checkStrictly: state.checkStrictly, - filedNames: unref(getFieldNames), + fieldNames: unref(getFieldNames), 'onUpdate:expandedKeys': (v: KeyType[]) => { state.expandedKeys = v; emit('update:expandedKeys', v); @@ -119,6 +119,7 @@ getAllKeys, getChildrenKeys, getEnabledKeys, + getSelectedNode, } = useTree(treeDataRef, getFieldNames); function getIcon(params: Recordable, icon?: string) { @@ -293,6 +294,7 @@ () => { state.checkedKeys = toRaw(props.value || []); }, + { immediate: true }, ); watch( @@ -319,6 +321,7 @@ insertNodesByKey, deleteNodeByKey, updateNodeByKey, + getSelectedNode, checkAll, expandAll, filterByLevel: (level: number) => { diff --git a/src/components/Tree/src/tree.ts b/src/components/Tree/src/tree.ts index 420f16a5..c368b8bf 100644 --- a/src/components/Tree/src/tree.ts +++ b/src/components/Tree/src/tree.ts @@ -185,4 +185,9 @@ export interface TreeActionType { updateNodeByKey: (key: string, node: Omit) => void; setSearchValue: (value: string) => void; getSearchValue: () => string; + getSelectedNode: ( + key: KeyType, + treeList?: TreeItem[], + selectNode?: TreeItem | null, + ) => TreeItem | null; } diff --git a/src/components/Tree/src/useTree.ts b/src/components/Tree/src/useTree.ts index 44f2f3b8..4713a54c 100644 --- a/src/components/Tree/src/useTree.ts +++ b/src/components/Tree/src/useTree.ts @@ -1,4 +1,4 @@ -import type { InsertNodeParams, KeyType, FieldNames } from './tree'; +import type { InsertNodeParams, KeyType, FieldNames, TreeItem } from './tree'; import type { Ref, ComputedRef } from 'vue'; import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; @@ -176,6 +176,23 @@ export function useTree(treeDataRef: Ref, getFieldNames: Compute } } } + + // Get selected node + function getSelectedNode(key: KeyType, list?: TreeItem[], selectedNode?: TreeItem | null) { + if (!key && key !== 0) return null; + const treeData = list || unref(treeDataRef); + treeData.forEach((item) => { + if (selectedNode?.key || selectedNode?.key === 0) return selectedNode; + if (item.key === key) { + selectedNode = item; + return; + } + if (item.children && item.children.length) { + selectedNode = getSelectedNode(key, item.children, selectedNode); + } + }); + return selectedNode || null; + } return { deleteNodeByKey, insertNodeByKey, @@ -185,5 +202,6 @@ export function useTree(treeDataRef: Ref, getFieldNames: Compute getAllKeys, getChildrenKeys, getEnabledKeys, + getSelectedNode, }; } diff --git a/src/views/demo/form/index.vue b/src/views/demo/form/index.vue index 6d6799d6..c6e96bf6 100644 --- a/src/views/demo/form/index.vue +++ b/src/views/demo/form/index.vue @@ -460,6 +460,27 @@ span: 8, }, }, + { + field: 'field36', + component: 'ApiTree', + label: '远程Tree', + helpMessage: ['ApiTree组件', '使用接口提供的数据生成选项'], + required: true, + componentProps: { + api: treeOptionsListApi, + params: { + count: 2, + }, + afterFetch: (v) => { + //do something + return v; + }, + resultField: 'list', + }, + colProps: { + span: 8, + }, + }, { field: 'divider-linked', component: 'Divider', diff --git a/src/views/demo/permission/back/Btn.vue b/src/views/demo/permission/back/Btn.vue index 87d698a9..bc8b7024 100644 --- a/src/views/demo/permission/back/Btn.vue +++ b/src/views/demo/permission/back/Btn.vue @@ -12,14 +12,14 @@ show-icon /> - + 点击切换按钮权限(用户id为2) - + 点击切换按钮权限(用户id为1,默认) -