提交 74360564 作者: 方治民
...@@ -29,4 +29,5 @@ pnpm-debug.log* ...@@ -29,4 +29,5 @@ pnpm-debug.log*
*.sln *.sln
*.sw? *.sw?
.mocks/ .mocks
.history
...@@ -3,4 +3,6 @@ ...@@ -3,4 +3,6 @@
# shellcheck source=./_/husky.sh # shellcheck source=./_/husky.sh
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
PATH="/usr/local/bin:$PATH"
npx --no-install commitlint --edit "$1" npx --no-install commitlint --edit "$1"
...@@ -4,5 +4,7 @@ ...@@ -4,5 +4,7 @@
[ -n "$CI" ] && exit 0 [ -n "$CI" ] && exit 0
PATH="/usr/local/bin:$PATH"
# Format and submit code according to lintstagedrc.js configuration # Format and submit code according to lintstagedrc.js configuration
npm run lint:lint-staged npm run lint:lint-staged
{ {
"recommendations": [ "recommendations": [
"Vue.volar", "vue.volar",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
}, },
"files.exclude": { "files.exclude": {
"**/.cache": true, "**/.cache": true,
"**/.editorconfig": true,
"**/.eslintcache": true, "**/.eslintcache": true,
"**/bower_components": true, "**/bower_components": true,
"**/.idea": true, "**/.idea": true,
...@@ -135,11 +136,7 @@ ...@@ -135,11 +136,7 @@
"pnpm", "pnpm",
"antd" "antd"
], ],
"vue-i18n.i18nPaths": "src\\locales,src\\locales\\lang,public\\resource\\tinymce\\langs", "vetur.format.scriptInitialIndent": true,
"[properties]": { "vetur.format.styleInitialIndent": true,
"editor.defaultFormatter": "foxundermoon.shell-format" "vetur.validation.script": false
},
"[nginx]": {
"editor.defaultFormatter": "teclado.vscode-nginx-format"
}
} }
...@@ -21,13 +21,15 @@ function createConfig(params: CreateConfigParams) { ...@@ -21,13 +21,15 @@ function createConfig(params: CreateConfigParams) {
try { try {
const windowConf = `window.${configName}` const windowConf = `window.${configName}`
// Ensure that the variable will not be modified // Ensure that the variable will not be modified
const configStr = `${windowConf}=${JSON.stringify(config)}; let configStr = `${windowConf}=${JSON.stringify(config)};`
configStr += `
Object.freeze(${windowConf}); Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", { Object.defineProperty(window, "${configName}", {
configurable: false, configurable: false,
writable: false, writable: false,
}); });
`.replace(/\s/g, '') `.replace(/\s/g, '')
fs.mkdirp(getRootPath(OUTPUT_DIR)) fs.mkdirp(getRootPath(OUTPUT_DIR))
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr) writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr)
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
import { createStyleImportPlugin } from 'vite-plugin-style-import' import { createStyleImportPlugin } from 'vite-plugin-style-import'
export function configStyleImportPlugin(_isBuild: boolean) { export function configStyleImportPlugin(_isBuild: boolean) {
// if (!isBuild) { if (!_isBuild) {
// return []; return []
// } }
const styleImportPlugin = createStyleImportPlugin({ const styleImportPlugin = createStyleImportPlugin({
libs: [ libs: [
{ {
...@@ -64,6 +64,8 @@ export function configStyleImportPlugin(_isBuild: boolean) { ...@@ -64,6 +64,8 @@ export function configStyleImportPlugin(_isBuild: boolean) {
'layout-footer': 'layout', 'layout-footer': 'layout',
'layout-header': 'layout', 'layout-header': 'layout',
'month-picker': 'date-picker', 'month-picker': 'date-picker',
'range-picker': 'date-picker',
'image-preview-group': 'image',
} }
return ignoreList.includes(name) return ignoreList.includes(name)
......
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const scopes = fs
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name.replace(/s$/, ''))
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
.toString()
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replace(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '')
/** @type {import('cz-git').UserConfig} */
module.exports = { module.exports = {
ignores: [(commit) => commit.includes('init')], ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'], extends: ['@commitlint/config-conventional'],
...@@ -30,4 +50,58 @@ module.exports = { ...@@ -30,4 +50,58 @@ module.exports = {
], ],
], ],
}, },
prompt: {
/** @use `yarn commit :f` */
alias: {
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
b: 'build: bump dependencies',
c: 'chore: update config',
},
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
defaultScope: scopeComplete,
scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: false,
allowCustomIssuePrefixs: false,
// English
typesAppend: [
{ value: 'wip', name: 'wip: work in process' },
{ value: 'workflow', name: 'workflow: workflow improvements' },
{ value: 'types', name: 'types: type definition file changes' },
],
// 中英文对照版
// messages: {
// type: '选择你要提交的类型 :',
// scope: '选择一个提交范围 (可选):',
// customScope: '请输入自定义的提交范围 :',
// subject: '填写简短精炼的变更描述 :\n',
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
// customFooterPrefixs: '输入自定义issue前缀 :',
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
// confirmCommit: '是否提交或修改commit ?',
// },
// types: [
// { value: 'feat', name: 'feat: 新增功能' },
// { value: 'fix', name: 'fix: 修复缺陷' },
// { value: 'docs', name: 'docs: 文档变更' },
// { value: 'style', name: 'style: 代码格式' },
// { value: 'refactor', name: 'refactor: 代码重构' },
// { value: 'perf', name: 'perf: 性能优化' },
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
// { value: 'revert', name: 'revert: 回滚 commit' },
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
// { value: 'wip', name: 'wip: 正在开发中' },
// { value: 'workflow', name: 'workflow: 工作流程改进' },
// { value: 'types', name: 'types: 类型定义文件修改' },
// ],
// emptyScopesAlias: 'empty: 不填写',
// customScopesAlias: 'custom: 自定义',
},
} }
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
}, },
"config": { "config": {
"commitizen": { "commitizen": {
"path": "./node_modules/cz-conventional-changelog" "path": "node_modules/cz-git"
} }
}, },
"dependencies": { "dependencies": {
...@@ -127,6 +127,8 @@ ...@@ -127,6 +127,8 @@
"conventional-changelog-cli": "^2.2.2", "conventional-changelog-cli": "^2.2.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"cz-git": "^1.3.12",
"czg": "^1.3.12",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"eslint": "^8.26.0", "eslint": "^8.26.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
......
...@@ -48,6 +48,8 @@ specifiers: ...@@ -48,6 +48,8 @@ specifiers:
cross-env: ^7.0.3 cross-env: ^7.0.3
crypto-js: ^4.1.1 crypto-js: ^4.1.1
cz-conventional-changelog: ^3.3.0 cz-conventional-changelog: ^3.3.0
cz-git: ^1.3.12
czg: ^1.3.12
dayjs: ^1.11.6 dayjs: ^1.11.6
dotenv: ^16.0.3 dotenv: ^16.0.3
echarts: ^5.4.0 echarts: ^5.4.0
...@@ -195,6 +197,8 @@ devDependencies: ...@@ -195,6 +197,8 @@ devDependencies:
conventional-changelog-cli: 2.2.2 conventional-changelog-cli: 2.2.2
cross-env: 7.0.3 cross-env: 7.0.3
cz-conventional-changelog: 3.3.0 cz-conventional-changelog: 3.3.0
cz-git: 1.3.12
czg: 1.3.12
dotenv: 16.0.3 dotenv: 16.0.3
eslint: 8.26.0 eslint: 8.26.0
eslint-config-prettier: 8.5.0_eslint@8.26.0 eslint-config-prettier: 8.5.0_eslint@8.26.0
...@@ -4011,6 +4015,15 @@ packages: ...@@ -4011,6 +4015,15 @@ packages:
- '@swc/wasm' - '@swc/wasm'
dev: true dev: true
/cz-git/1.3.12:
resolution: {integrity: sha512-grTgbgjsRpvkYNjm8kmpvHHw/LkkS3SlQOOwgamhRZo7c5qQfts1QZdkJ6RP0K61BcxyH5siMz8qC/MDoW/tBA==}
dev: true
/czg/1.3.12:
resolution: {integrity: sha512-p+Jm5AVatbaMeo2lRyuj5xm9iuxe/A6s9WA/Jfu+g3FWYHkG+StsNXJw1u8i1qqfmbu2hj9rXgF0lTltwHHKSA==}
hasBin: true
dev: true
/d/1.0.1: /d/1.0.1:
resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
dependencies: dependencies:
......
...@@ -4,14 +4,4 @@ ...@@ -4,14 +4,4 @@
* For LGPL see License.txt in the project root for license information. * For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/ * For commercial licenses see https://www.tiny.cloud/
*/ */
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;} .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
body{-webkit-text-size-adjust: none;}
body img{max-width: 96vw;}
body table img{max-width: 95%;}
body{font-family: sans-serif;}
table{border-collapse: collapse;}
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
...@@ -4,14 +4,4 @@ ...@@ -4,14 +4,4 @@
* For LGPL see License.txt in the project root for license information. * For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/ * For commercial licenses see https://www.tiny.cloud/
*/ */
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;} .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
body{-webkit-text-size-adjust: none;}
body img{max-width: 96vw;}
body table img{max-width: 95%;}
body{font-family: sans-serif;}
table{border-collapse: collapse;}
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
"description": "身份认证", "description": "身份认证",
"interfaces": [ "interfaces": [
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "登录", "description": "登录",
"name": "login", "name": "login",
"method": "post", "method": "post",
...@@ -91,9 +89,7 @@ ...@@ -91,9 +89,7 @@
"parameters": [] "parameters": []
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "注册", "description": "注册",
"name": "register", "name": "register",
"method": "post", "method": "post",
...@@ -409,9 +405,7 @@ ...@@ -409,9 +405,7 @@
"parameters": [] "parameters": []
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "JSON 传参", "description": "JSON 传参",
"name": "json", "name": "json",
"method": "post", "method": "post",
...@@ -554,10 +548,7 @@ ...@@ -554,10 +548,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'ASC'", "'DESC'"],
"'ASC'",
"'DESC'"
],
"typeProperties": [] "typeProperties": []
} }
} }
...@@ -617,9 +608,7 @@ ...@@ -617,9 +608,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["multipart/form-data"],
"multipart/form-data"
],
"description": "文件上传", "description": "文件上传",
"name": "upload", "name": "upload",
"method": "post", "method": "post",
...@@ -662,9 +651,7 @@ ...@@ -662,9 +651,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "Base64 图片上传", "description": "Base64 图片上传",
"name": "uploadBase64Image", "name": "uploadBase64Image",
"method": "post", "method": "post",
...@@ -713,9 +700,7 @@ ...@@ -713,9 +700,7 @@
"description": "权限管理", "description": "权限管理",
"interfaces": [ "interfaces": [
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "新增", "description": "新增",
"name": "add", "name": "add",
"method": "post", "method": "post",
...@@ -901,11 +886,7 @@ ...@@ -901,11 +886,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'DIR'", "'MENU'", "'BUTTON'"],
"'DIR'",
"'MENU'",
"'BUTTON'"
],
"typeProperties": [] "typeProperties": []
} }
}, },
...@@ -927,9 +908,7 @@ ...@@ -927,9 +908,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "删除", "description": "删除",
"name": "deleted", "name": "deleted",
"method": "post", "method": "post",
...@@ -1014,9 +993,7 @@ ...@@ -1014,9 +993,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "修改", "description": "修改",
"name": "modify", "name": "modify",
"method": "post", "method": "post",
...@@ -1217,11 +1194,7 @@ ...@@ -1217,11 +1194,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'DIR'", "'MENU'", "'BUTTON'"],
"'DIR'",
"'MENU'",
"'BUTTON'"
],
"typeProperties": [] "typeProperties": []
} }
}, },
...@@ -1333,10 +1306,7 @@ ...@@ -1333,10 +1306,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'ASC'", "'DESC'"],
"'ASC'",
"'DESC'"
],
"typeProperties": [] "typeProperties": []
} }
} }
...@@ -1401,9 +1371,7 @@ ...@@ -1401,9 +1371,7 @@
"description": "角色管理", "description": "角色管理",
"interfaces": [ "interfaces": [
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "新增", "description": "新增",
"name": "add", "name": "add",
"method": "post", "method": "post",
...@@ -1461,9 +1429,7 @@ ...@@ -1461,9 +1429,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "分配权限", "description": "分配权限",
"name": "assign", "name": "assign",
"method": "post", "method": "post",
...@@ -1521,9 +1487,7 @@ ...@@ -1521,9 +1487,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "删除", "description": "删除",
"name": "deleted", "name": "deleted",
"method": "post", "method": "post",
...@@ -1608,9 +1572,7 @@ ...@@ -1608,9 +1572,7 @@
] ]
}, },
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "修改", "description": "修改",
"name": "modify", "name": "modify",
"method": "post", "method": "post",
...@@ -1773,10 +1735,7 @@ ...@@ -1773,10 +1735,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'ASC'", "'DESC'"],
"'ASC'",
"'DESC'"
],
"typeProperties": [] "typeProperties": []
} }
} }
...@@ -1825,9 +1784,7 @@ ...@@ -1825,9 +1784,7 @@
"description": "用户管理", "description": "用户管理",
"interfaces": [ "interfaces": [
{ {
"consumes": [ "consumes": ["application/json"],
"application/json"
],
"description": "分配角色", "description": "分配角色",
"name": "assign", "name": "assign",
"method": "post", "method": "post",
...@@ -1975,10 +1932,7 @@ ...@@ -1975,10 +1932,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'ASC'", "'DESC'"],
"'ASC'",
"'DESC'"
],
"typeProperties": [] "typeProperties": []
} }
} }
...@@ -2299,10 +2253,7 @@ ...@@ -2299,10 +2253,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'ASC'", "'DESC'"],
"'ASC'",
"'DESC'"
],
"typeProperties": [] "typeProperties": []
}, },
"name": "sortOrder", "name": "sortOrder",
...@@ -2574,11 +2525,7 @@ ...@@ -2574,11 +2525,7 @@
"isDefsType": false, "isDefsType": false,
"templateIndex": -1, "templateIndex": -1,
"compileTemplateKeyword": "#/definitions/", "compileTemplateKeyword": "#/definitions/",
"enum": [ "enum": ["'DIR'", "'MENU'", "'BUTTON'"],
"'DIR'",
"'MENU'",
"'BUTTON'"
],
"typeProperties": [] "typeProperties": []
}, },
"name": "type", "name": "type",
......
...@@ -10,18 +10,18 @@ ...@@ -10,18 +10,18 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { Button } from 'ant-design-vue'
export default defineComponent({ export default defineComponent({
name: 'AButton', name: 'AButton',
extends: Button,
inheritAttrs: false, inheritAttrs: false,
}) })
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, unref } from 'vue' import { computed, unref } from 'vue'
import { Button } from 'ant-design-vue'
import Icon from '/@/components/Icon/src/Icon.vue' import Icon from '/@/components/Icon/src/Icon.vue'
import { buttonProps } from './props' import { buttonProps } from './props'
import { useAttrs } from '/@/hooks/core/useAttrs' import { useAttrs } from '/@/hooks/core/useAttrs'
const props = defineProps(buttonProps) const props = defineProps(buttonProps)
// get component class // get component class
const attrs = useAttrs({ excludeDefaultKeys: false }) const attrs = useAttrs({ excludeDefaultKeys: false })
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
}, },
] ]
}) })
// get inherit binding value // get inherit binding value
const getBindValue = computed(() => ({ ...unref(attrs), ...props })) const getBindValue = computed(() => ({ ...unref(attrs), ...props }))
</script> </script>
const validColors = ['error', 'warning', 'success', ''] as const
type ButtonColorType = typeof validColors[number]
export const buttonProps = { export const buttonProps = {
color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) }, color: {
type: String as PropType<ButtonColorType>,
validator: (v) => validColors.includes(v),
default: '',
},
loading: { type: Boolean }, loading: { type: Boolean },
disabled: { type: Boolean }, disabled: { type: Boolean },
/** /**
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from 'vue' import type { PropType } from 'vue'
import { ref } from 'vue' import { ref } from 'vue'
import { isNil } from 'lodash-es'
// component // component
import { Skeleton } from 'ant-design-vue' import { Skeleton } from 'ant-design-vue'
import { CollapseTransition } from '/@/components/Transition' import { CollapseTransition } from '/@/components/Transition'
...@@ -33,7 +34,6 @@ ...@@ -33,7 +34,6 @@
// hook // hook
import { useTimeoutFn } from '/@/hooks/core/useTimeout' import { useTimeoutFn } from '/@/hooks/core/useTimeout'
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign'
const props = defineProps({ const props = defineProps({
title: { type: String, default: '' }, title: { type: String, default: '' },
loading: { type: Boolean }, loading: { type: Boolean },
...@@ -58,25 +58,24 @@ ...@@ -58,25 +58,24 @@
*/ */
lazyTime: { type: Number, default: 0 }, lazyTime: { type: Number, default: 0 },
}) })
const show = ref(true) const show = ref(true)
const { prefixCls } = useDesign('collapse-container') const { prefixCls } = useDesign('collapse-container')
/** /**
* @description: Handling development events * @description: Handling development events
*/ */
function handleExpand() { function handleExpand(val: boolean) {
show.value = !show.value show.value = isNil(val) ? !show.value : val
if (props.triggerWindowResize) { if (props.triggerWindowResize) {
// 200 milliseconds here is because the expansion has animation, // 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200) useTimeoutFn(triggerWindowResize, 200)
} }
} }
defineExpose({
handleExpand,
})
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-collapse-container'; @prefix-cls: ~'@{namespace}-collapse-container';
.@{prefix-cls} { .@{prefix-cls} {
background-color: @component-background; background-color: @component-background;
border-radius: 2px; border-radius: 2px;
......
<script lang="tsx"> <script lang="tsx">
import type { ContextMenuItem, ItemContentProps, Axis } from './typing' import type { ContextMenuItem, ItemContentProps, Axis } from './typing'
import type { FunctionalComponent, CSSProperties } from 'vue' import type { FunctionalComponent, CSSProperties, PropType } from 'vue'
import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue' import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue'
import Icon from '/@/components/Icon' import Icon from '/@/components/Icon'
import { Menu, Divider } from 'ant-design-vue' import { Menu, Divider } from 'ant-design-vue'
const prefixCls = 'context-menu' const prefixCls = 'context-menu'
const props = { const props = {
width: { type: Number, default: 156 }, width: { type: Number, default: 156 },
customEvent: { type: Object as PropType<Event>, default: null }, customEvent: { type: Object as PropType<Event>, default: null },
...@@ -27,7 +25,6 @@ ...@@ -27,7 +25,6 @@
}, },
}, },
} }
const ItemContent: FunctionalComponent<ItemContentProps> = (props) => { const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
const { item } = props const { item } = props
return ( return (
...@@ -37,21 +34,18 @@ ...@@ -37,21 +34,18 @@
</span> </span>
) )
} }
export default defineComponent({ export default defineComponent({
name: 'ContextMenu', name: 'ContextMenu',
props, props,
setup(props) { setup(props) {
const wrapRef = ref(null) const wrapRef = ref(null)
const showRef = ref(false) const showRef = ref(false)
const getStyle = computed((): CSSProperties => { const getStyle = computed((): CSSProperties => {
const { axis, items, styles, width } = props const { axis, items, styles, width } = props
const { x, y } = axis || { x: 0, y: 0 } const { x, y } = axis || { x: 0, y: 0 }
const menuHeight = (items || []).length * 40 const menuHeight = (items || []).length * 40
const menuWidth = width const menuWidth = width
const body = document.body const body = document.body
const left = body.clientWidth < x + menuWidth ? x - menuWidth : x const left = body.clientWidth < x + menuWidth ? x - menuWidth : x
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y const top = body.clientHeight < y + menuHeight ? y - menuHeight : y
return { return {
...@@ -63,16 +57,13 @@ ...@@ -63,16 +57,13 @@
zIndex: 9999, zIndex: 9999,
} }
}) })
onMounted(() => { onMounted(() => {
nextTick(() => (showRef.value = true)) nextTick(() => (showRef.value = true))
}) })
onUnmounted(() => { onUnmounted(() => {
const el = unref(wrapRef) const el = unref(wrapRef)
el && document.body.removeChild(el) el && document.body.removeChild(el)
}) })
function handleAction(item: ContextMenuItem, e: MouseEvent) { function handleAction(item: ContextMenuItem, e: MouseEvent) {
const { handler, disabled } = item const { handler, disabled } = item
if (disabled) { if (disabled) {
...@@ -83,17 +74,15 @@ ...@@ -83,17 +74,15 @@
e?.preventDefault() e?.preventDefault()
handler?.() handler?.()
} }
function renderMenuItem(items: ContextMenuItem[]) { function renderMenuItem(items: ContextMenuItem[]) {
return items.map((item) => { const visibleItems = items.filter((item) => !item.hidden)
return visibleItems.map((item) => {
const { disabled, label, children, divider = false } = item const { disabled, label, children, divider = false } = item
const contentProps = { const contentProps = {
item, item,
handler: handleAction, handler: handleAction,
showIcon: props.showIcon, showIcon: props.showIcon,
} }
if (!children || children.length === 0) { if (!children || children.length === 0) {
return ( return (
<> <>
...@@ -105,7 +94,6 @@ ...@@ -105,7 +94,6 @@
) )
} }
if (!unref(showRef)) return null if (!unref(showRef)) return null
return ( return (
<Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}> <Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
{{ {{
...@@ -134,11 +122,8 @@ ...@@ -134,11 +122,8 @@
</script> </script>
<style lang="less"> <style lang="less">
@default-height: 42px !important; @default-height: 42px !important;
@small-height: 36px !important; @small-height: 36px !important;
@large-height: 36px !important; @large-height: 36px !important;
.item-style() { .item-style() {
li { li {
display: inline-block; display: inline-block;
...@@ -191,7 +176,6 @@ ...@@ -191,7 +176,6 @@
.ant-divider { .ant-divider {
margin: 0; margin: 0;
} }
.item-style(); .item-style();
} }
......
...@@ -6,6 +6,7 @@ export interface Axis { ...@@ -6,6 +6,7 @@ export interface Axis {
export interface ContextMenuItem { export interface ContextMenuItem {
label: string label: string
icon?: string icon?: string
hidden?: boolean
disabled?: boolean disabled?: boolean
handler?: Fn handler?: Fn
divider?: boolean divider?: boolean
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import type { ButtonProps } from '/@/components/Button' import type { ButtonProps } from '/@/components/Button'
import Icon from '/@/components/Icon' import Icon from '/@/components/Icon'
const props = { const props = {
width: { type: [String, Number], default: '200px' }, width: { type: [String, Number], default: '200px' },
value: { type: String }, value: { type: String },
...@@ -41,7 +40,6 @@ ...@@ -41,7 +40,6 @@
btnText: { type: String, default: '' }, btnText: { type: String, default: '' },
uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> }, uploadApi: { type: Function as PropType<({ file: Blob, name: string }) => Promise<void>> },
} }
export default defineComponent({ export default defineComponent({
name: 'CropperAvatar', name: 'CropperAvatar',
components: { CopperModal, Icon }, components: { CopperModal, Icon },
...@@ -53,38 +51,28 @@ ...@@ -53,38 +51,28 @@
const [register, { openModal, closeModal }] = useModal() const [register, { openModal, closeModal }] = useModal()
const { createMessage } = useMessage() const { createMessage } = useMessage()
const { t } = useI18n() const { t } = useI18n()
const getClass = computed(() => [prefixCls]) const getClass = computed(() => [prefixCls])
const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px') const getWidth = computed(() => `${props.width}`.replace(/px/, '') + 'px')
const getIconWidth = computed(() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px') const getIconWidth = computed(() => parseInt(`${props.width}`.replace(/px/, '')) / 2 + 'px')
const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) })) const getStyle = computed((): CSSProperties => ({ width: unref(getWidth) }))
const getImageWrapperStyle = computed( const getImageWrapperStyle = computed(
(): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }), (): CSSProperties => ({ width: unref(getWidth), height: unref(getWidth) }),
) )
watchEffect(() => { watchEffect(() => {
sourceValue.value = props.value || '' sourceValue.value = props.value || ''
}) })
watch( watch(
() => sourceValue.value, () => sourceValue.value,
(v: string) => { (v: string) => {
emit('update:value', v) emit('update:value', v)
}, },
) )
function handleUploadSuccess({ source, data }) {
function handleUploadSuccess({ source }) {
sourceValue.value = source sourceValue.value = source
emit('change', source) emit('change', { source, data })
createMessage.success(t('component.cropper.uploadSuccess')) createMessage.success(t('component.cropper.uploadSuccess'))
} }
expose({ openModal: openModal.bind(null, true), closeModal }) expose({ openModal: openModal.bind(null, true), closeModal })
return { return {
t, t,
prefixCls, prefixCls,
...@@ -103,7 +91,6 @@ ...@@ -103,7 +91,6 @@
<style lang="less" scoped> <style lang="less" scoped>
@prefix-cls: ~'@{namespace}-cropper-avatar'; @prefix-cls: ~'@{namespace}-cropper-avatar';
.@{prefix-cls} { .@{prefix-cls} {
display: inline-block; display: inline-block;
text-align: center; text-align: center;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index' import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index'
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import type { CollapseContainerOptions } from '/@/components/Container/index' import type { CollapseContainerOptions } from '/@/components/Container/index'
import { defineComponent, computed, ref, unref } from 'vue' import { defineComponent, computed, ref, unref, toRefs } from 'vue'
import { get } from 'lodash-es' import { get } from 'lodash-es'
import { Descriptions } from 'ant-design-vue' import { Descriptions } from 'ant-design-vue'
import { CollapseContainer } from '/@/components/Container/index' import { CollapseContainer } from '/@/components/Container/index'
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
import { isFunction } from '/@/utils/is' import { isFunction } from '/@/utils/is'
import { getSlot } from '/@/utils/helper/tsxHelper' import { getSlot } from '/@/utils/helper/tsxHelper'
import { useAttrs } from '/@/hooks/core/useAttrs' import { useAttrs } from '/@/hooks/core/useAttrs'
const props = { const props = {
useCollapse: { type: Boolean, default: true }, useCollapse: { type: Boolean, default: true },
title: { type: String, default: '' }, title: { type: String, default: '' },
...@@ -37,17 +36,14 @@ ...@@ -37,17 +36,14 @@
}, },
data: { type: Object }, data: { type: Object },
} }
export default defineComponent({ export default defineComponent({
name: 'Description', name: 'Description',
props, props,
emits: ['register'], emits: ['register'],
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const propsRef = ref<Partial<DescriptionProps> | null>(null) const propsRef = ref<Partial<DescriptionProps> | null>(null)
const { prefixCls } = useDesign('description') const { prefixCls } = useDesign('description')
const attrs = useAttrs() const attrs = useAttrs()
// Custom title component: get title // Custom title component: get title
const getMergeProps = computed(() => { const getMergeProps = computed(() => {
return { return {
...@@ -55,7 +51,6 @@ ...@@ -55,7 +51,6 @@
...(unref(propsRef) as Recordable), ...(unref(propsRef) as Recordable),
} as DescriptionProps } as DescriptionProps
}) })
const getProps = computed(() => { const getProps = computed(() => {
const opt = { const opt = {
...unref(getMergeProps), ...unref(getMergeProps),
...@@ -63,12 +58,10 @@ ...@@ -63,12 +58,10 @@
} }
return opt as DescriptionProps return opt as DescriptionProps
}) })
/** /**
* @description: Whether to setting title * @description: Whether to setting title
*/ */
const useWrapper = computed(() => !!unref(getMergeProps).title) const useWrapper = computed(() => !!unref(getMergeProps).title)
/** /**
* @description: Get configuration Collapse * @description: Get configuration Collapse
*/ */
...@@ -79,11 +72,9 @@ ...@@ -79,11 +72,9 @@
...unref(getProps).collapseOptions, ...unref(getProps).collapseOptions,
} }
}) })
const getDescriptionsProps = computed(() => { const getDescriptionsProps = computed(() => {
return { ...unref(attrs), ...unref(getProps) } as DescriptionsProps return { ...unref(attrs), ...unref(getProps) } as DescriptionsProps
}) })
/** /**
* @description:设置desc * @description:设置desc
*/ */
...@@ -91,39 +82,36 @@ ...@@ -91,39 +82,36 @@
// Keep the last setDrawerProps // Keep the last setDrawerProps
propsRef.value = { ...(unref(propsRef) as Recordable), ...descProps } as Recordable propsRef.value = { ...(unref(propsRef) as Recordable), ...descProps } as Recordable
} }
// Prevent line breaks // Prevent line breaks
function renderLabel({ label, labelMinWidth, labelStyle }: DescItem) { function renderLabel({ label, labelMinWidth, labelStyle }: DescItem) {
if (!labelStyle && !labelMinWidth) { if (!labelStyle && !labelMinWidth) {
return label return label
} }
const labelStyles: CSSProperties = { const labelStyles: CSSProperties = {
...labelStyle, ...labelStyle,
minWidth: `${labelMinWidth}px `, minWidth: `${labelMinWidth}px `,
} }
return <div style={labelStyles}>{label}</div> return <div style={labelStyles}>{label}</div>
} }
function renderItem() { function renderItem() {
const { schema, data } = unref(getProps) const { schema, data } = unref(getProps)
return unref(schema) return unref(schema)
.map((item) => { .map((item) => {
const { render, field, span, show, contentMinWidth } = item const { render, field, span, show, contentMinWidth } = item
if (show && isFunction(show) && !show(data)) { if (show && isFunction(show) && !show(data)) {
return null return null
} }
const getContent = () => { const getContent = () => {
const _data = unref(getProps)?.data const _data = unref(getProps)?.data
if (!_data) { if (!_data) {
return null return null
} }
const getField = get(_data, field) const getField = get(_data, field)
if (getField && !toRefs(_data).hasOwnProperty(field)) {
return isFunction(render) ? render('', _data) : ''
}
return isFunction(render) ? render(getField, _data) : getField ?? '' return isFunction(render) ? render(getField, _data) : getField ?? ''
} }
const width = contentMinWidth const width = contentMinWidth
return ( return (
<Descriptions.Item label={renderLabel(item)} key={field} span={span}> <Descriptions.Item label={renderLabel(item)} key={field} span={span}>
...@@ -141,7 +129,6 @@ ...@@ -141,7 +129,6 @@
}) })
.filter((item) => !!item) .filter((item) => !!item)
} }
const renderDesc = () => { const renderDesc = () => {
return ( return (
<Descriptions class={`${prefixCls}`} {...(unref(getDescriptionsProps) as any)}> <Descriptions class={`${prefixCls}`} {...(unref(getDescriptionsProps) as any)}>
...@@ -149,17 +136,14 @@ ...@@ -149,17 +136,14 @@
</Descriptions> </Descriptions>
) )
} }
const renderContainer = () => { const renderContainer = () => {
const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div> const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div>
// Reduce the dom level // Reduce the dom level
if (!props.useCollapse) { if (!props.useCollapse) {
return content return content
} }
const { canExpand, helpMessage } = unref(getCollapseOptions) const { canExpand, helpMessage } = unref(getCollapseOptions)
const { title } = unref(getMergeProps) const { title } = unref(getMergeProps)
return ( return (
<CollapseContainer title={title} canExpan={canExpand} helpMessage={helpMessage}> <CollapseContainer title={title} canExpan={canExpand} helpMessage={helpMessage}>
{{ {{
...@@ -169,11 +153,9 @@ ...@@ -169,11 +153,9 @@
</CollapseContainer> </CollapseContainer>
) )
} }
const methods: DescInstance = { const methods: DescInstance = {
setDescProps, setDescProps,
} }
emit('register', methods) emit('register', methods)
return () => (unref(useWrapper) ? renderContainer() : renderDesc()) return () => (unref(useWrapper) ? renderContainer() : renderDesc())
}, },
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
const heightStr = `${props.height}` const heightStr = `${props.height}`
return { return {
height: heightStr, height: heightStr,
lineHeight: heightStr, lineHeight: `calc(${heightStr} - 1px)`,
} }
}) })
......
...@@ -6,6 +6,28 @@ const { utils, writeFile } = xlsx ...@@ -6,6 +6,28 @@ const { utils, writeFile } = xlsx
const DEF_FILE_NAME = 'excel-list.xlsx' const DEF_FILE_NAME = 'excel-list.xlsx'
/**
* @param data source data
* @param worksheet worksheet object
* @param min min width
*/
function setColumnWidth(data, worksheet, min = 3) {
const obj = {}
worksheet['!cols'] = []
data.forEach((item) => {
Object.keys(item).forEach((key) => {
const cur = item[key]
const length = cur.length
obj[key] = Math.max(min, length)
})
})
Object.keys(obj).forEach((key) => {
worksheet['!cols'].push({
wch: obj[key],
})
})
}
export function jsonToSheetXlsx<T = any>({ export function jsonToSheetXlsx<T = any>({
data, data,
header, header,
...@@ -20,7 +42,7 @@ export function jsonToSheetXlsx<T = any>({ ...@@ -20,7 +42,7 @@ export function jsonToSheetXlsx<T = any>({
} }
const worksheet = utils.json_to_sheet(arrData, json2sheetOpts) const worksheet = utils.json_to_sheet(arrData, json2sheetOpts)
setColumnWidth(arrData, worksheet)
/* add worksheet to workbook */ /* add worksheet to workbook */
const workbook: WorkBook = { const workbook: WorkBook = {
SheetNames: [filename], SheetNames: [filename],
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
import { defineComponent, ref, unref } from 'vue' import { defineComponent, ref, unref } from 'vue'
import * as XLSX from 'xlsx' import * as XLSX from 'xlsx'
import { dateUtil } from '/@/utils/dateUtil' import { dateUtil } from '/@/utils/dateUtil'
import type { ExcelData } from './typing' import type { ExcelData } from './typing'
export default defineComponent({ export default defineComponent({
name: 'ImportExcel', name: 'ImportExcel',
...@@ -25,12 +24,16 @@ ...@@ -25,12 +24,16 @@
type: Number, type: Number,
default: 8, default: 8,
}, },
// 是否直接返回选中文件
isReturnFile: {
type: Boolean,
default: false,
},
}, },
emits: ['success', 'error'], emits: ['success', 'error'],
setup(props, { emit }) { setup(props, { emit }) {
const inputRef = ref<HTMLInputElement | null>(null) const inputRef = ref<HTMLInputElement | null>(null)
const loadingRef = ref<Boolean>(false) const loadingRef = ref<Boolean>(false)
/** /**
* @description: 第一行作为头部 * @description: 第一行作为头部
*/ */
...@@ -39,7 +42,6 @@ ...@@ -39,7 +42,6 @@
const headers: string[] = [] const headers: string[] = []
// A3:B7=>{s:{c:0, r:2}, e:{c:1, r:6}} // A3:B7=>{s:{c:0, r:2}, e:{c:1, r:6}}
const range = XLSX.utils.decode_range(sheet['!ref']) const range = XLSX.utils.decode_range(sheet['!ref'])
const R = range.s.r const R = range.s.r
/* start in the first row */ /* start in the first row */
for (let C = range.s.c; C <= range.e.c; ++C) { for (let C = range.s.c; C <= range.e.c; ++C) {
...@@ -52,7 +54,6 @@ ...@@ -52,7 +54,6 @@
} }
return headers return headers
} }
/** /**
* @description: 获得excel数据 * @description: 获得excel数据
*/ */
...@@ -79,7 +80,6 @@ ...@@ -79,7 +80,6 @@
} }
return row return row
}) })
excelData.push({ excelData.push({
header, header,
results, results,
...@@ -90,7 +90,6 @@ ...@@ -90,7 +90,6 @@
} }
return excelData return excelData
} }
/** /**
* @description: 读取excel数据 * @description: 读取excel数据
*/ */
...@@ -117,7 +116,6 @@ ...@@ -117,7 +116,6 @@
reader.readAsArrayBuffer(rawFile) reader.readAsArrayBuffer(rawFile)
}) })
} }
async function upload(rawFile: File) { async function upload(rawFile: File) {
const inputRefDom = unref(inputRef) const inputRefDom = unref(inputRef)
if (inputRefDom) { if (inputRefDom) {
...@@ -126,7 +124,6 @@ ...@@ -126,7 +124,6 @@
} }
await readerData(rawFile) await readerData(rawFile)
} }
/** /**
* @description: 触发选择文件管理器 * @description: 触发选择文件管理器
*/ */
...@@ -134,9 +131,12 @@ ...@@ -134,9 +131,12 @@
const files = e && (e.target as HTMLInputElement).files const files = e && (e.target as HTMLInputElement).files
const rawFile = files && files[0] // only setting files[0] const rawFile = files && files[0] // only setting files[0]
if (!rawFile) return if (!rawFile) return
if (props.isReturnFile) {
emit('success', rawFile)
return
}
upload(rawFile) upload(rawFile)
} }
/** /**
* @description: 点击上传按钮 * @description: 点击上传按钮
*/ */
...@@ -144,7 +144,6 @@ ...@@ -144,7 +144,6 @@
const inputRefDom = unref(inputRef) const inputRefDom = unref(inputRef)
inputRefDom && inputRefDom.click() inputRefDom && inputRefDom.click()
} }
return { handleUpload, handleInputClick, inputRef } return { handleUpload, handleInputClick, inputRef }
}, },
}) })
......
...@@ -12,5 +12,6 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue' ...@@ -12,5 +12,6 @@ export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'
export { default as ApiTree } from './src/components/ApiTree.vue' export { default as ApiTree } from './src/components/ApiTree.vue'
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue' export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'
export { default as ApiCascader } from './src/components/ApiCascader.vue' export { default as ApiCascader } from './src/components/ApiCascader.vue'
export { default as ApiTransfer } from './src/components/ApiTransfer.vue'
export { BasicForm } export { BasicForm }
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<slot name="formHeader"></slot> <slot name="formHeader"></slot>
<template v-for="schema in getSchema" :key="schema.field"> <template v-for="schema in getSchema" :key="schema.field">
<FormItem <FormItem
:isAdvanced="fieldsIsAdvancedMap[schema.field]"
:tableAction="tableAction" :tableAction="tableAction"
:formActionType="formActionType" :formActionType="formActionType"
:schema="schema" :schema="schema"
...@@ -40,18 +41,14 @@ ...@@ -40,18 +41,14 @@
import type { FormActionType, FormProps, FormSchema } from './types/form' import type { FormActionType, FormProps, FormSchema } from './types/form'
import type { AdvanceState } from './types/hooks' import type { AdvanceState } from './types/hooks'
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue' import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue'
import { Form, Row } from 'ant-design-vue' import { Form, Row } from 'ant-design-vue'
import FormItem from './components/FormItem.vue' import FormItem from './components/FormItem.vue'
import FormAction from './components/FormAction.vue' import FormAction from './components/FormAction.vue'
import { dateItemType } from './helper' import { dateItemType } from './helper'
import { dateUtil } from '/@/utils/dateUtil' import { dateUtil } from '/@/utils/dateUtil'
// import { cloneDeep } from 'lodash-es'; // import { cloneDeep } from 'lodash-es';
import { deepMerge } from '/@/utils' import { deepMerge } from '/@/utils'
import { useFormValues } from './hooks/useFormValues' import { useFormValues } from './hooks/useFormValues'
import useAdvanced from './hooks/useAdvanced' import useAdvanced from './hooks/useAdvanced'
import { useFormEvents } from './hooks/useFormEvents' import { useFormEvents } from './hooks/useFormEvents'
...@@ -59,39 +56,33 @@ ...@@ -59,39 +56,33 @@
import { useAutoFocus } from './hooks/useAutoFocus' import { useAutoFocus } from './hooks/useAutoFocus'
import { useModalContext } from '/@/components/Modal' import { useModalContext } from '/@/components/Modal'
import { useDebounceFn } from '@vueuse/core' import { useDebounceFn } from '@vueuse/core'
import { basicProps } from './props' import { basicProps } from './props'
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign'
import { cloneDeep } from 'lodash-es'
export default defineComponent({ export default defineComponent({
name: 'BasicForm', name: 'BasicForm',
components: { FormItem, Form, Row, FormAction }, components: { FormItem, Form, Row, FormAction },
props: basicProps, props: basicProps,
emits: ['advanced-change', 'reset', 'submit', 'register'], emits: ['advanced-change', 'reset', 'submit', 'register', 'field-value-change'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const formModel = reactive<Recordable>({}) const formModel = reactive<Recordable>({})
const modalFn = useModalContext() const modalFn = useModalContext()
const advanceState = reactive<AdvanceState>({ const advanceState = reactive<AdvanceState>({
isAdvanced: true, isAdvanced: true,
hideAdvanceBtn: false, hideAdvanceBtn: false,
isLoad: false, isLoad: false,
actionSpan: 6, actionSpan: 6,
}) })
const defaultValueRef = ref<Recordable>({}) const defaultValueRef = ref<Recordable>({})
const isInitedDefaultRef = ref(false) const isInitedDefaultRef = ref(false)
const propsRef = ref<Partial<FormProps>>({}) const propsRef = ref<Partial<FormProps>>({})
const schemaRef = ref<Nullable<FormSchema[]>>(null) const schemaRef = ref<Nullable<FormSchema[]>>(null)
const formElRef = ref<Nullable<FormActionType>>(null) const formElRef = ref<Nullable<FormActionType>>(null)
const { prefixCls } = useDesign('basic-form') const { prefixCls } = useDesign('basic-form')
// Get the basic configuration of the form // Get the basic configuration of the form
const getProps = computed((): FormProps => { const getProps = computed((): FormProps => {
return { ...props, ...unref(propsRef) } as FormProps return { ...props, ...unref(propsRef) } as FormProps
}) })
const getFormClass = computed(() => { const getFormClass = computed(() => {
return [ return [
prefixCls, prefixCls,
...@@ -100,7 +91,6 @@ ...@@ -100,7 +91,6 @@
}, },
] ]
}) })
// Get uniform row style and Row configuration for the entire form // Get uniform row style and Row configuration for the entire form
const getRow = computed((): Recordable => { const getRow = computed((): Recordable => {
const { baseRowStyle = {}, rowProps } = unref(getProps) const { baseRowStyle = {}, rowProps } = unref(getProps)
...@@ -109,15 +99,13 @@ ...@@ -109,15 +99,13 @@
...rowProps, ...rowProps,
} }
}) })
const getBindValue = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable)) const getBindValue = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable))
const getSchema = computed((): FormSchema[] => { const getSchema = computed((): FormSchema[] => {
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any) const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any)
for (const schema of schemas) { for (const schema of schemas) {
const { defaultValue, component } = schema const { defaultValue, component, isHandleDateDefaultValue = true } = schema
// handle date type // handle date type
if (defaultValue && dateItemType.includes(component)) { if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) {
if (!Array.isArray(defaultValue)) { if (!Array.isArray(defaultValue)) {
schema.defaultValue = dateUtil(defaultValue) schema.defaultValue = dateUtil(defaultValue)
} else { } else {
...@@ -130,13 +118,12 @@ ...@@ -130,13 +118,12 @@
} }
} }
if (unref(getProps).showAdvancedButton) { if (unref(getProps).showAdvancedButton) {
return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[] return cloneDeep(schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[])
} else { } else {
return schemas as FormSchema[] return cloneDeep(schemas as FormSchema[])
} }
}) })
const { handleToggleAdvanced, fieldsIsAdvancedMap } = useAdvanced({
const { handleToggleAdvanced } = useAdvanced({
advanceState, advanceState,
emit, emit,
getProps, getProps,
...@@ -144,21 +131,18 @@ ...@@ -144,21 +131,18 @@
formModel, formModel,
defaultValueRef, defaultValueRef,
}) })
const { handleFormValues, initDefault } = useFormValues({ const { handleFormValues, initDefault } = useFormValues({
getProps, getProps,
defaultValueRef, defaultValueRef,
getSchema, getSchema,
formModel, formModel,
}) })
useAutoFocus({ useAutoFocus({
getSchema, getSchema,
getProps, getProps,
isInitedDefault: isInitedDefaultRef, isInitedDefault: isInitedDefaultRef,
formElRef: formElRef as Ref<FormActionType>, formElRef: formElRef as Ref<FormActionType>,
}) })
const { const {
handleSubmit, handleSubmit,
setFieldsValue, setFieldsValue,
...@@ -169,7 +153,7 @@ ...@@ -169,7 +153,7 @@
updateSchema, updateSchema,
resetSchema, resetSchema,
appendSchemaByField, appendSchemaByField,
removeSchemaByFiled, removeSchemaByField,
resetFields, resetFields,
scrollToField, scrollToField,
} = useFormEvents({ } = useFormEvents({
...@@ -182,12 +166,10 @@ ...@@ -182,12 +166,10 @@
schemaRef: schemaRef as Ref<FormSchema[]>, schemaRef: schemaRef as Ref<FormSchema[]>,
handleFormValues, handleFormValues,
}) })
createFormContext({ createFormContext({
resetAction: resetFields, resetAction: resetFields,
submitAction: handleSubmit, submitAction: handleSubmit,
}) })
watch( watch(
() => unref(getProps).model, () => unref(getProps).model,
() => { () => {
...@@ -199,14 +181,12 @@ ...@@ -199,14 +181,12 @@
immediate: true, immediate: true,
}, },
) )
watch( watch(
() => unref(getProps).schemas, () => unref(getProps).schemas,
(schemas) => { (schemas) => {
resetSchema(schemas ?? []) resetSchema(schemas ?? [])
}, },
) )
watch( watch(
() => getSchema.value, () => getSchema.value,
(schema) => { (schema) => {
...@@ -223,7 +203,6 @@ ...@@ -223,7 +203,6 @@
} }
}, },
) )
watch( watch(
() => formModel, () => formModel,
useDebounceFn(() => { useDebounceFn(() => {
...@@ -231,19 +210,17 @@ ...@@ -231,19 +210,17 @@
}, 300), }, 300),
{ deep: true }, { deep: true },
) )
async function setProps(formProps: Partial<FormProps>): Promise<void> { async function setProps(formProps: Partial<FormProps>): Promise<void> {
propsRef.value = deepMerge(unref(propsRef) || {}, formProps) propsRef.value = deepMerge(unref(propsRef) || {}, formProps)
} }
function setFormModel(key: string, value: any) { function setFormModel(key: string, value: any) {
formModel[key] = value formModel[key] = value
const { validateTrigger } = unref(getBindValue) const { validateTrigger } = unref(getBindValue)
if (!validateTrigger || validateTrigger === 'change') { if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {}) validateFields([key]).catch((_) => {})
} }
emit('field-value-change', key, value)
} }
function handleEnterPress(e: KeyboardEvent) { function handleEnterPress(e: KeyboardEvent) {
const { autoSubmitOnEnter } = unref(getProps) const { autoSubmitOnEnter } = unref(getProps)
if (!autoSubmitOnEnter) return if (!autoSubmitOnEnter) return
...@@ -254,7 +231,6 @@ ...@@ -254,7 +231,6 @@
} }
} }
} }
const formActionType: Partial<FormActionType> = { const formActionType: Partial<FormActionType> = {
getFieldsValue, getFieldsValue,
setFieldsValue, setFieldsValue,
...@@ -262,7 +238,7 @@ ...@@ -262,7 +238,7 @@
updateSchema, updateSchema,
resetSchema, resetSchema,
setProps, setProps,
removeSchemaByFiled, removeSchemaByField,
appendSchemaByField, appendSchemaByField,
clearValidate, clearValidate,
validateFields, validateFields,
...@@ -270,12 +246,10 @@ ...@@ -270,12 +246,10 @@
submit: handleSubmit, submit: handleSubmit,
scrollToField: scrollToField, scrollToField: scrollToField,
} }
onMounted(() => { onMounted(() => {
initDefault() initDefault()
emit('register', formActionType) emit('register', formActionType)
}) })
return { return {
getBindValue, getBindValue,
handleToggleAdvanced, handleToggleAdvanced,
...@@ -291,6 +265,7 @@ ...@@ -291,6 +265,7 @@
setFormModel, setFormModel,
getFormClass, getFormClass,
getFormActionBindProps: computed((): Recordable => ({ ...getProps.value, ...advanceState })), getFormActionBindProps: computed((): Recordable => ({ ...getProps.value, ...advanceState })),
fieldsIsAdvancedMap,
...formActionType, ...formActionType,
} }
}, },
...@@ -298,7 +273,6 @@ ...@@ -298,7 +273,6 @@
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-basic-form'; @prefix-cls: ~'@{namespace}-basic-form';
.@{prefix-cls} { .@{prefix-cls} {
.ant-form-item { .ant-form-item {
&-label label::after { &-label label::after {
......
...@@ -27,6 +27,7 @@ import ApiSelect from './components/ApiSelect.vue' ...@@ -27,6 +27,7 @@ import ApiSelect from './components/ApiSelect.vue'
import ApiTree from './components/ApiTree.vue' import ApiTree from './components/ApiTree.vue'
import ApiTreeSelect from './components/ApiTreeSelect.vue' import ApiTreeSelect from './components/ApiTreeSelect.vue'
import ApiCascader from './components/ApiCascader.vue' import ApiCascader from './components/ApiCascader.vue'
import ApiTransfer from './components/ApiTransfer.vue'
import { BasicUpload } from '/@/components/Upload' import { BasicUpload } from '/@/components/Upload'
import { StrengthMeter } from '/@/components/StrengthMeter' import { StrengthMeter } from '/@/components/StrengthMeter'
import { IconPicker } from '/@/components/Icon' import { IconPicker } from '/@/components/Icon'
...@@ -57,6 +58,7 @@ componentMap.set('ApiCascader', ApiCascader) ...@@ -57,6 +58,7 @@ componentMap.set('ApiCascader', ApiCascader)
componentMap.set('Cascader', Cascader) componentMap.set('Cascader', Cascader)
componentMap.set('Slider', Slider) componentMap.set('Slider', Slider)
componentMap.set('Rate', Rate) componentMap.set('Rate', Rate)
componentMap.set('ApiTransfer', ApiTransfer)
componentMap.set('DatePicker', DatePicker) componentMap.set('DatePicker', DatePicker)
componentMap.set('MonthPicker', DatePicker.MonthPicker) componentMap.set('MonthPicker', DatePicker.MonthPicker)
......
...@@ -30,9 +30,7 @@ ...@@ -30,9 +30,7 @@
import { LoadingOutlined } from '@ant-design/icons-vue' import { LoadingOutlined } from '@ant-design/icons-vue'
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes'
type OptionsItem = { label: string; value: string; disabled?: boolean } type OptionsItem = { label: string; value: string; disabled?: boolean }
export default defineComponent({ export default defineComponent({
name: 'ApiSelect', name: 'ApiSelect',
components: { components: {
...@@ -59,7 +57,7 @@ ...@@ -59,7 +57,7 @@
immediate: propTypes.bool.def(true), immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false), alwaysLoad: propTypes.bool.def(false),
}, },
emits: ['options-change', 'change'], emits: ['options-change', 'change', 'update:value'],
setup(props, { emit }) { setup(props, { emit }) {
const options = ref<OptionsItem[]>([]) const options = ref<OptionsItem[]>([])
const loading = ref(false) const loading = ref(false)
...@@ -67,13 +65,10 @@ ...@@ -67,13 +65,10 @@
const emitData = ref<any[]>([]) const emitData = ref<any[]>([])
const attrs = useAttrs() const attrs = useAttrs()
const { t } = useI18n() const { t } = useI18n()
// Embedded in the form, just use the hook binding to perform form verification // Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props, 'value', 'change', emitData) const [state] = useRuleFormItem(props, 'value', 'change', emitData)
const getOptions = computed(() => { const getOptions = computed(() => {
const { labelField, valueField, numberToString } = props const { labelField, valueField, numberToString } = props
return unref(options).reduce((prev, next: Recordable) => { return unref(options).reduce((prev, next: Recordable) => {
if (next) { if (next) {
const value = next[valueField] const value = next[valueField]
...@@ -86,11 +81,15 @@ ...@@ -86,11 +81,15 @@
return prev return prev
}, [] as OptionsItem[]) }, [] as OptionsItem[])
}) })
watchEffect(() => { watchEffect(() => {
props.immediate && !props.alwaysLoad && fetch() props.immediate && !props.alwaysLoad && fetch()
}) })
watch(
() => state.value,
(v) => {
emit('update:value', v)
},
)
watch( watch(
() => props.params, () => props.params,
() => { () => {
...@@ -98,7 +97,6 @@ ...@@ -98,7 +97,6 @@
}, },
{ deep: true }, { deep: true },
) )
async function fetch() { async function fetch() {
const api = props.api const api = props.api
if (!api || !isFunction(api)) return if (!api || !isFunction(api)) return
...@@ -121,7 +119,6 @@ ...@@ -121,7 +119,6 @@
loading.value = false loading.value = false
} }
} }
async function handleFetch(visible) { async function handleFetch(visible) {
if (visible) { if (visible) {
if (props.alwaysLoad) { if (props.alwaysLoad) {
...@@ -132,15 +129,12 @@ ...@@ -132,15 +129,12 @@
} }
} }
} }
function emitChange() { function emitChange() {
emit('options-change', unref(getOptions)) emit('options-change', unref(getOptions))
} }
function handleChange(_, ...args) { function handleChange(_, ...args) {
emitData.value = args emitData.value = args
} }
return { state, attrs, getOptions, loading, t, handleFetch, handleChange } return { state, attrs, getOptions, loading, t, handleFetch, handleChange }
}, },
}) })
......
<template>
<Transfer
:data-source="getdataSource"
:filter-option="filterOption"
:render="(item) => item.title"
:showSelectAll="showSelectAll"
:selectedKeys="selectedKeys"
:targetKeys="getTargetKeys"
:showSearch="showSearch"
@change="handleChange"
/>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, unref, watchEffect } from 'vue'
import { Transfer } from 'ant-design-vue'
import { isFunction } from '/@/utils/is'
import { get, omit } from 'lodash-es'
import { propTypes } from '/@/utils/propTypes'
import { useI18n } from '/@/hooks/web/useI18n'
import { TransferDirection, TransferItem } from 'ant-design-vue/lib/transfer'
export default defineComponent({
name: 'ApiTransfer',
components: { Transfer },
props: {
value: { type: Array as PropType<Array<string>> },
api: {
type: Function as PropType<(arg?: Recordable) => Promise<TransferItem[]>>,
default: null,
},
params: { type: Object },
dataSource: { type: Array as PropType<Array<TransferItem>> },
immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
afterFetch: { type: Function as PropType<Fn> },
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('title'),
valueField: propTypes.string.def('key'),
showSearch: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
filterOption: {
type: Function as PropType<(inputValue: string, item: TransferItem) => boolean>,
},
selectedKeys: { type: Array as PropType<Array<string>> },
showSelectAll: { type: Boolean, default: false },
targetKeys: { type: Array as PropType<Array<string>> },
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const _dataSource = ref<TransferItem[]>([])
const _targetKeys = ref<string[]>([])
const { t } = useI18n()
const getAttrs = computed(() => {
return {
...(!props.api ? { dataSource: unref(_dataSource) } : {}),
...attrs,
}
})
const getdataSource = computed(() => {
const { labelField, valueField } = props
return unref(_dataSource).reduce((prev, next: Recordable) => {
if (next) {
prev.push({
...omit(next, [labelField, valueField]),
title: next[labelField],
key: next[valueField],
})
}
return prev
}, [] as TransferItem[])
})
const getTargetKeys = computed<string[]>(() => {
if (unref(_targetKeys).length > 0) {
return unref(_targetKeys)
}
if (Array.isArray(props.value)) {
return props.value
}
return []
})
function handleChange(keys: string[], direction: TransferDirection, moveKeys: string[]) {
_targetKeys.value = keys
console.log(direction)
console.log(moveKeys)
emit('change', keys)
}
watchEffect(() => {
props.immediate && !props.alwaysLoad && fetch()
})
watch(
() => props.params,
() => {
fetch()
},
{ deep: true },
)
async function fetch() {
const api = props.api
if (!api || !isFunction(api)) {
if (Array.isArray(props.dataSource)) {
_dataSource.value = props.dataSource
}
return
}
_dataSource.value = []
try {
const res = await api(props.params)
if (Array.isArray(res)) {
_dataSource.value = res
emitChange()
return
}
if (props.resultField) {
_dataSource.value = get(res, props.resultField) || []
}
emitChange()
} catch (error) {
console.warn(error)
} finally {
}
}
function emitChange() {
emit('options-change', unref(getdataSource))
}
return { getTargetKeys, getdataSource, t, getAttrs, handleChange }
},
})
</script>
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
params: { type: Object }, params: { type: Object },
immediate: { type: Boolean, default: true }, immediate: { type: Boolean, default: true },
resultField: propTypes.string.def(''), resultField: propTypes.string.def(''),
afterFetch: { type: Function as PropType<Fn> },
}, },
emits: ['options-change', 'change'], emits: ['options-change', 'change'],
setup(props, { attrs, emit }) { setup(props, { attrs, emit }) {
...@@ -36,11 +37,9 @@ ...@@ -36,11 +37,9 @@
...attrs, ...attrs,
} }
}) })
function handleChange(...args) { function handleChange(...args) {
emit('change', ...args) emit('change', ...args)
} }
watch( watch(
() => props.params, () => props.params,
() => { () => {
...@@ -48,20 +47,17 @@ ...@@ -48,20 +47,17 @@
}, },
{ deep: true }, { deep: true },
) )
watch( watch(
() => props.immediate, () => props.immediate,
(v) => { (v) => {
v && !isFirstLoaded.value && fetch() v && !isFirstLoaded.value && fetch()
}, },
) )
onMounted(() => { onMounted(() => {
props.immediate && fetch() props.immediate && fetch()
}) })
async function fetch() { async function fetch() {
const { api } = props const { api, afterFetch } = props
if (!api || !isFunction(api)) return if (!api || !isFunction(api)) return
loading.value = true loading.value = true
treeData.value = [] treeData.value = []
...@@ -71,6 +67,9 @@ ...@@ -71,6 +67,9 @@
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
if (afterFetch && isFunction(afterFetch)) {
result = afterFetch(result)
}
loading.value = false loading.value = false
if (!result) return if (!result) return
if (!isArray(result)) { if (!isArray(result)) {
......
import type { ColEx } from '../types' import type { ColEx } from '../types'
import type { AdvanceState } from '../types/hooks' import type { AdvanceState } from '../types/hooks'
import type { ComputedRef, Ref } from 'vue' import { ComputedRef, getCurrentInstance, Ref, shallowReactive } from 'vue'
import type { FormProps, FormSchema } from '../types/form' import type { FormProps, FormSchema } from '../types/form'
import { computed, unref, watch } from 'vue' import { computed, unref, watch } from 'vue'
import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is' import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'
...@@ -19,6 +19,8 @@ interface UseAdvancedContext { ...@@ -19,6 +19,8 @@ interface UseAdvancedContext {
} }
export default function ({ advanceState, emit, getProps, getSchema, formModel, defaultValueRef }: UseAdvancedContext) { export default function ({ advanceState, emit, getProps, getSchema, formModel, defaultValueRef }: UseAdvancedContext) {
const vm = getCurrentInstance()
const { realWidthRef, screenEnum, screenRef } = useBreakpoint() const { realWidthRef, screenEnum, screenRef } = useBreakpoint()
const getEmptySpan = computed((): number => { const getEmptySpan = computed((): number => {
...@@ -104,6 +106,8 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d ...@@ -104,6 +106,8 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d
} }
} }
const fieldsIsAdvancedMap = shallowReactive({})
function updateAdvanced() { function updateAdvanced() {
let itemColSum = 0 let itemColSum = 0
let realItemColSum = 0 let realItemColSum = 0
...@@ -136,10 +140,13 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d ...@@ -136,10 +140,13 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d
if (isAdvanced) { if (isAdvanced) {
realItemColSum = itemColSum realItemColSum = itemColSum
} }
schema.isAdvanced = isAdvanced fieldsIsAdvancedMap[schema.field] = isAdvanced
} }
} }
// 确保页面发送更新
vm?.proxy?.$forceUpdate()
advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpan) advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpan)
getAdvanced(unref(getProps).actionColOptions || { span: BASIC_COL_LEN }, itemColSum, true) getAdvanced(unref(getProps).actionColOptions || { span: BASIC_COL_LEN }, itemColSum, true)
...@@ -151,5 +158,5 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d ...@@ -151,5 +158,5 @@ export default function ({ advanceState, emit, getProps, getSchema, formModel, d
advanceState.isAdvanced = !advanceState.isAdvanced advanceState.isAdvanced = !advanceState.isAdvanced
} }
return { handleToggleAdvanced } return { handleToggleAdvanced, fieldsIsAdvancedMap }
} }
...@@ -79,8 +79,8 @@ export function useForm(props?: Props): UseFormReturnType { ...@@ -79,8 +79,8 @@ export function useForm(props?: Props): UseFormReturnType {
}) })
}, },
removeSchemaByFiled: async (field: string | string[]) => { removeSchemaByField: async (field: string | string[]) => {
unref(formRef)?.removeSchemaByFiled(field) unref(formRef)?.removeSchemaByField(field)
}, },
// TODO promisify // TODO promisify
......
...@@ -2,7 +2,7 @@ import type { ComputedRef, Ref } from 'vue' ...@@ -2,7 +2,7 @@ import type { ComputedRef, Ref } from 'vue'
import type { FormProps, FormSchema, FormActionType } from '../types/form' import type { FormProps, FormSchema, FormActionType } from '../types/form'
import type { NamePath } from 'ant-design-vue/lib/form/interface' import type { NamePath } from 'ant-design-vue/lib/form/interface'
import { unref, toRaw, nextTick } from 'vue' import { unref, toRaw, nextTick } from 'vue'
import { isArray, isFunction, isNullOrUnDef, isObject, isString } from '/@/utils/is' import { isArray, isFunction, isObject, isString, isDef, isNullOrUnDef } from '/@/utils/is'
import { deepMerge } from '/@/utils' import { deepMerge } from '/@/utils'
import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper' import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper'
import { dateUtil } from '/@/utils/dateUtil' import { dateUtil } from '/@/utils/dateUtil'
...@@ -39,7 +39,8 @@ export function useFormEvents({ ...@@ -39,7 +39,8 @@ export function useFormEvents({
Object.keys(formModel).forEach((key) => { Object.keys(formModel).forEach((key) => {
const schema = unref(getSchema).find((item) => item.field === key) const schema = unref(getSchema).find((item) => item.field === key)
const isInput = schema?.component && defaultValueComponents.includes(schema.component) const isInput = schema?.component && defaultValueComponents.includes(schema.component)
formModel[key] = isInput ? defaultValueRef.value[key] || '' : defaultValueRef.value[key] const defaultValue = cloneDeep(defaultValueRef.value[key])
formModel[key] = isInput ? defaultValue || '' : defaultValue
}) })
nextTick(() => clearValidate()) nextTick(() => clearValidate())
...@@ -55,6 +56,10 @@ export function useFormEvents({ ...@@ -55,6 +56,10 @@ export function useFormEvents({
.map((item) => item.field) .map((item) => item.field)
.filter(Boolean) .filter(Boolean)
// key 支持 a.b.c 的嵌套写法
const delimiter = '.'
const nestKeyArray = fields.filter((item) => item.indexOf(delimiter) >= 0)
const validKeys: string[] = [] const validKeys: string[] = []
Object.keys(values).forEach((key) => { Object.keys(values).forEach((key) => {
const schema = unref(getSchema).find((item) => item.field === key) const schema = unref(getSchema).find((item) => item.field === key)
...@@ -85,6 +90,21 @@ export function useFormEvents({ ...@@ -85,6 +90,21 @@ export function useFormEvents({
formModel[key] = value formModel[key] = value
} }
validKeys.push(key) 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] = cloneDeep(defaultValueRef.value[nestKey])
}
}
})
} }
}) })
validateFields(validKeys).catch((_) => {}) validateFields(validKeys).catch((_) => {})
...@@ -92,7 +112,7 @@ export function useFormEvents({ ...@@ -92,7 +112,7 @@ export function useFormEvents({
/** /**
* @description: Delete based on field name * @description: Delete based on field name
*/ */
async function removeSchemaByFiled(fields: string | string[]): Promise<void> { async function removeSchemaByField(fields: string | string[]): Promise<void> {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)) const schemaList: FormSchema[] = cloneDeep(unref(getSchema))
if (!fields) { if (!fields) {
return return
...@@ -103,7 +123,7 @@ export function useFormEvents({ ...@@ -103,7 +123,7 @@ export function useFormEvents({
fieldList = [fields] fieldList = [fields]
} }
for (const field of fieldList) { for (const field of fieldList) {
_removeSchemaByFiled(field, schemaList) _removeSchemaByFeild(field, schemaList)
} }
schemaRef.value = schemaList schemaRef.value = schemaList
} }
...@@ -111,7 +131,7 @@ export function useFormEvents({ ...@@ -111,7 +131,7 @@ export function useFormEvents({
/** /**
* @description: Delete based on field name * @description: Delete based on field name
*/ */
function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void { function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void {
if (isString(field)) { if (isString(field)) {
const index = schemaList.findIndex((schema) => schema.field === field) const index = schemaList.findIndex((schema) => schema.field === field)
if (index !== -1) { if (index !== -1) {
...@@ -206,12 +226,14 @@ export function useFormEvents({ ...@@ -206,12 +226,14 @@ export function useFormEvents({
} }
const obj: Recordable = {} const obj: Recordable = {}
const currentFieldsValue = getFieldsValue()
schemas.forEach((item) => { schemas.forEach((item) => {
if ( if (
item.component != 'Divider' && item.component != 'Divider' &&
Reflect.has(item, 'field') && Reflect.has(item, 'field') &&
item.field && item.field &&
!isNullOrUnDef(item.defaultValue) !isNullOrUnDef(item.defaultValue) &&
!(item.field in currentFieldsValue)
) { ) {
obj[item.field] = item.defaultValue obj[item.field] = item.defaultValue
} }
...@@ -280,7 +302,7 @@ export function useFormEvents({ ...@@ -280,7 +302,7 @@ export function useFormEvents({
updateSchema, updateSchema,
resetSchema, resetSchema,
appendSchemaByField, appendSchemaByField,
removeSchemaByFiled, removeSchemaByField,
resetFields, resetFields,
setFieldsValue, setFieldsValue,
scrollToField, scrollToField,
......
...@@ -3,7 +3,7 @@ import { dateUtil } from '/@/utils/dateUtil' ...@@ -3,7 +3,7 @@ import { dateUtil } from '/@/utils/dateUtil'
import { unref } from 'vue' import { unref } from 'vue'
import type { Ref, ComputedRef } from 'vue' import type { Ref, ComputedRef } from 'vue'
import type { FormProps, FormSchema } from '../types/form' import type { FormProps, FormSchema } from '../types/form'
import { set } from 'lodash-es' import { cloneDeep, set } from 'lodash-es'
interface UseFormValuesContext { interface UseFormValuesContext {
defaultValueRef: Ref<any> defaultValueRef: Ref<any>
...@@ -92,14 +92,21 @@ export function useFormValues({ defaultValueRef, getSchema, formModel, getProps ...@@ -92,14 +92,21 @@ export function useFormValues({ defaultValueRef, getSchema, formModel, getProps
} }
for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) { for (const [field, [startTimeKey, endTimeKey], format = 'YYYY-MM-DD'] of fieldMapToTime) {
if (!field || !startTimeKey || !endTimeKey || !values[field]) { if (!field || !startTimeKey || !endTimeKey) {
continue
}
// If the value to be converted is empty, remove the field
if (!values[field]) {
Reflect.deleteProperty(values, field)
continue continue
} }
const [startTime, endTime]: string[] = values[field] const [startTime, endTime]: string[] = values[field]
values[startTimeKey] = dateUtil(startTime).format(format) const [startTimeFormat, endTimeFormat] = Array.isArray(format) ? format : [format, format]
values[endTimeKey] = dateUtil(endTime).format(format)
values[startTimeKey] = dateUtil(startTime).format(startTimeFormat)
values[endTimeKey] = dateUtil(endTime).format(endTimeFormat)
Reflect.deleteProperty(values, field) Reflect.deleteProperty(values, field)
} }
...@@ -113,10 +120,13 @@ export function useFormValues({ defaultValueRef, getSchema, formModel, getProps ...@@ -113,10 +120,13 @@ export function useFormValues({ defaultValueRef, getSchema, formModel, getProps
const { defaultValue } = item const { defaultValue } = item
if (!isNullOrUnDef(defaultValue)) { if (!isNullOrUnDef(defaultValue)) {
obj[item.field] = defaultValue obj[item.field] = defaultValue
if (formModel[item.field] === undefined) {
formModel[item.field] = defaultValue formModel[item.field] = defaultValue
} }
}
}) })
defaultValueRef.value = obj defaultValueRef.value = cloneDeep(obj)
} }
return { handleFormValues, initDefault } return { handleFormValues, initDefault }
......
...@@ -7,7 +7,7 @@ import type { TableActionType } from '/@/components/Table/src/types/table' ...@@ -7,7 +7,7 @@ import type { TableActionType } from '/@/components/Table/src/types/table'
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import type { RowProps } from 'ant-design-vue/lib/grid/Row' import type { RowProps } from 'ant-design-vue/lib/grid/Row'
export type FieldMapToTime = [string, [string, string], string?][] export type FieldMapToTime = [string, [string, string], (string | [string, string])?][]
export type Rule = RuleObject & { export type Rule = RuleObject & {
trigger?: 'blur' | 'change' | ['change', 'blur'] trigger?: 'blur' | 'change' | ['change', 'blur']
...@@ -33,7 +33,7 @@ export interface FormActionType { ...@@ -33,7 +33,7 @@ export interface FormActionType {
updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void> updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>
resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void> resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>
setProps: (formProps: Partial<FormProps>) => Promise<void> setProps: (formProps: Partial<FormProps>) => Promise<void>
removeSchemaByFiled: (field: string | string[]) => Promise<void> removeSchemaByField: (field: string | string[]) => Promise<void>
appendSchemaByField: ( appendSchemaByField: (
schema: FormSchema, schema: FormSchema,
prefixField: string | undefined, prefixField: string | undefined,
...@@ -49,6 +49,7 @@ export type RegisterFn = (formInstance: FormActionType) => void ...@@ -49,6 +49,7 @@ export type RegisterFn = (formInstance: FormActionType) => void
export type UseFormReturnType = [RegisterFn, FormActionType] export type UseFormReturnType = [RegisterFn, FormActionType]
export interface FormProps { export interface FormProps {
name?: string
layout?: 'vertical' | 'inline' | 'horizontal' layout?: 'vertical' | 'inline' | 'horizontal'
// Form value // Form value
model?: Recordable model?: Recordable
...@@ -171,6 +172,10 @@ export interface FormSchema { ...@@ -171,6 +172,10 @@ export interface FormSchema {
// 默认值 // 默认值
defaultValue?: any defaultValue?: any
// 是否自动处理与时间相关组件的默认值
isHandleDateDefaultValue?: boolean
isAdvanced?: boolean isAdvanced?: boolean
// Matching details components // Matching details components
......
...@@ -114,3 +114,4 @@ export type ComponentType = ...@@ -114,3 +114,4 @@ export type ComponentType =
| 'Slider' | 'Slider'
| 'Rate' | 'Rate'
| 'Divider' | 'Divider'
| 'ApiTransfer'
...@@ -10,9 +10,8 @@ ...@@ -10,9 +10,8 @@
import { useModalContext } from '../../Modal' import { useModalContext } from '../../Modal'
import { useRootSetting } from '/@/hooks/setting/useRootSetting' import { useRootSetting } from '/@/hooks/setting/useRootSetting'
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated' import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'
import { getTheme } from './getTheme'
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined
export default defineComponent({ export default defineComponent({
inheritAttrs: false, inheritAttrs: false,
props: { props: {
...@@ -24,28 +23,25 @@ ...@@ -24,28 +23,25 @@
const wrapRef = ref<ElRef>(null) const wrapRef = ref<ElRef>(null)
const vditorRef = ref(null) as Ref<Nullable<Vditor>> const vditorRef = ref(null) as Ref<Nullable<Vditor>>
const initedRef = ref(false) const initedRef = ref(false)
const modalFn = useModalContext() const modalFn = useModalContext()
const { getLocale } = useLocale() const { getLocale } = useLocale()
const { getDarkMode } = useRootSetting() const { getDarkMode } = useRootSetting()
const valueRef = ref(props.value || '') const valueRef = ref(props.value || '')
watch( watch(
[() => getDarkMode.value, () => initedRef.value], [() => getDarkMode.value, () => initedRef.value],
([val, inited]) => { ([val, inited]) => {
if (!inited) { if (!inited) {
return return
} }
const theme = val === 'dark' ? 'dark' : 'classic' instance
instance.getVditor()?.setTheme(theme) .getVditor()
?.setTheme(getTheme(val) as any, getTheme(val, 'content'), getTheme(val, 'code'))
}, },
{ {
immediate: true, immediate: true,
flush: 'post', flush: 'post',
}, },
) )
watch( watch(
() => props.value, () => props.value,
(v) => { (v) => {
...@@ -55,7 +51,6 @@ ...@@ -55,7 +51,6 @@
valueRef.value = v valueRef.value = v
}, },
) )
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => { const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
let lang: Lang let lang: Lang
switch (unref(getLocale)) { switch (unref(getLocale)) {
...@@ -78,13 +73,22 @@ ...@@ -78,13 +73,22 @@
if (!wrapEl) return if (!wrapEl) return
const bindValue = { ...attrs, ...props } const bindValue = { ...attrs, ...props }
const insEditor = new Vditor(wrapEl, { const insEditor = new Vditor(wrapEl, {
theme: getDarkMode.value === 'dark' ? 'dark' : 'classic', // 设置外观主题
theme: getTheme(getDarkMode.value) as any,
lang: unref(getCurrentLang), lang: unref(getCurrentLang),
mode: 'sv', mode: 'sv',
fullscreen: { fullscreen: {
index: 520, index: 520,
}, },
preview: { preview: {
theme: {
// 设置内容主题
current: getTheme(getDarkMode.value, 'content'),
},
hljs: {
// 设置代码块主题
style: getTheme(getDarkMode.value, 'code'),
},
actions: [], actions: [],
}, },
input: (v) => { input: (v) => {
...@@ -110,11 +114,9 @@ ...@@ -110,11 +114,9 @@
}, },
}) })
} }
const instance = { const instance = {
getVditor: (): Vditor => vditorRef.value!, getVditor: (): Vditor => vditorRef.value!,
} }
function destroy() { function destroy() {
const vditorInstance = unref(vditorRef) const vditorInstance = unref(vditorRef)
if (!vditorInstance) return if (!vditorInstance) return
...@@ -124,9 +126,7 @@ ...@@ -124,9 +126,7 @@
vditorRef.value = null vditorRef.value = null
initedRef.value = false initedRef.value = false
} }
onMountedOrActivated(init) onMountedOrActivated(init)
onBeforeUnmount(destroy) onBeforeUnmount(destroy)
onDeactivated(destroy) onDeactivated(destroy)
return { return {
......
<template> <template>
<!-- eslint-disable vue/no-v-html --> <div ref="viewerRef" id="markdownViewer" :class="$props.class"></div>
<div v-html="getHtmlData" :class="$props.class" class="markdown-viewer"></div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineProps } from 'vue' import { defineProps, onBeforeUnmount, onDeactivated, Ref, ref, unref, watch } from 'vue'
import showdown from 'showdown' import VditorPreview from 'vditor/dist/method.min'
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'
const converter = new showdown.Converter() import { useRootSetting } from '/@/hooks/setting/useRootSetting'
converter.setOption('tables', true) import { getTheme } from './getTheme'
const props = defineProps({ const props = defineProps({
value: { type: String }, value: { type: String },
class: { type: String }, class: { type: String },
}) })
const getHtmlData = computed(() => converter.makeHtml(props.value || '')) const viewerRef = ref<ElRef>(null)
</script> const vditorPreviewRef = ref(null) as Ref<Nullable<VditorPreview>>
const { getDarkMode } = useRootSetting()
<style scoped> function init() {
.markdown-viewer { const viewerEl = unref(viewerRef) as HTMLElement
width: 100%; vditorPreviewRef.value = VditorPreview.preview(viewerEl, props.value, {
mode: getTheme(getDarkMode.value, 'content'),
theme: {
// 设置内容主题
current: getTheme(getDarkMode.value, 'content'),
},
hljs: {
// 设置代码块主题
style: getTheme(getDarkMode.value, 'code'),
},
})
}
watch(
() => getDarkMode.value,
(val) => {
VditorPreview.setContentTheme(getTheme(val, 'content'))
VditorPreview.setCodeTheme(getTheme(val, 'code'))
init()
},
)
watch(
() => props.value,
(v, oldValue) => {
v !== oldValue && init()
},
)
function destroy() {
const vditorInstance = unref(vditorPreviewRef)
if (!vditorInstance) return
try {
vditorInstance?.destroy?.()
} catch (error) {}
vditorPreviewRef.value = null
} }
</style> onMountedOrActivated(init)
onBeforeUnmount(destroy)
onDeactivated(destroy)
</script>
/**
* 获取主题类型 深色浅色模式 对应的值
* @param darkModeVal 深色模式值
* @param themeMode 主题类型——外观(默认), 内容, 代码块
*/
export const getTheme = (
darkModeVal: 'light' | 'dark' | string,
themeMode: 'default' | 'content' | 'code' = 'default',
) => {
const isDark = darkModeVal === 'dark'
switch (themeMode) {
case 'default':
return isDark ? 'dark' : 'classic'
case 'content':
return isDark ? 'dark' : 'light'
case 'code':
return isDark ? 'dracula' : 'github'
}
}
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
&-title { &-title {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
line-height: 16px;
.base-title { .base-title {
cursor: move !important; cursor: move !important;
...@@ -111,19 +110,16 @@ ...@@ -111,19 +110,16 @@
.ant-modal-confirm .ant-modal-body { .ant-modal-confirm .ant-modal-body {
padding: 24px !important; padding: 24px !important;
} }
@media screen and (max-height: 600px) { @media screen and (max-height: 600px) {
.ant-modal { .ant-modal {
top: 60px; top: 60px;
} }
} }
@media screen and (max-height: 540px) { @media screen and (max-height: 540px) {
.ant-modal { .ant-modal {
top: 30px; top: 30px;
} }
} }
@media screen and (max-height: 480px) { @media screen and (max-height: 480px) {
.ant-modal { .ant-modal {
top: 10px; top: 10px;
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
</span> </span>
<SimpleMenuTag :item="item" :collapseParent="!!collapse && !!parent" /> <SimpleMenuTag :item="item" :collapseParent="!!collapse && !!parent" />
</template> </template>
<template v-for="childrenItem in item.children || []" :key="childrenItem.path"> <template v-for="childrenItem in item.children || []" :key="childrenItem.paramPath || childrenItem.path">
<SimpleSubMenu v-bind="$props" :item="childrenItem" :parent="false" /> <SimpleSubMenu v-bind="$props" :item="childrenItem" :parent="false" />
</template> </template>
</SubMenu> </SubMenu>
...@@ -37,17 +37,14 @@ ...@@ -37,17 +37,14 @@
<script lang="ts"> <script lang="ts">
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { Menu } from '/@/router/types' import type { Menu } from '/@/router/types'
import { defineComponent, computed } from 'vue' import { defineComponent, computed } from 'vue'
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign'
import Icon from '/@/components/Icon/index' import Icon from '/@/components/Icon/index'
import MenuItem from './components/MenuItem.vue' import MenuItem from './components/MenuItem.vue'
import SubMenu from './components/SubMenuItem.vue' import SubMenu from './components/SubMenuItem.vue'
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes'
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent' import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'
export default defineComponent({ export default defineComponent({
name: 'SimpleSubMenu', name: 'SimpleSubMenu',
components: { components: {
...@@ -69,7 +66,6 @@ ...@@ -69,7 +66,6 @@
setup(props) { setup(props) {
const { t } = useI18n() const { t } = useI18n()
const { prefixCls } = useDesign('simple-menu') const { prefixCls } = useDesign('simple-menu')
const getShowMenu = computed(() => !props.item?.meta?.hideMenu) const getShowMenu = computed(() => !props.item?.meta?.hideMenu)
const getIcon = computed(() => props.item?.icon) const getIcon = computed(() => props.item?.icon)
const getI18nName = computed(() => t(props.item?.name)) const getI18nName = computed(() => t(props.item?.name))
...@@ -83,7 +79,6 @@ ...@@ -83,7 +79,6 @@
}, },
] ]
}) })
function menuHasChildren(menuTreeItem: Menu): boolean { function menuHasChildren(menuTreeItem: Menu): boolean {
return ( return (
!menuTreeItem.meta?.hideChildrenInMenu && !menuTreeItem.meta?.hideChildrenInMenu &&
...@@ -92,7 +87,6 @@ ...@@ -92,7 +87,6 @@
menuTreeItem.children.length > 0 menuTreeItem.children.length > 0
) )
} }
return { return {
prefixCls, prefixCls,
menuHasChildren, menuHasChildren,
......
...@@ -28,6 +28,10 @@ ...@@ -28,6 +28,10 @@
<template #headerCell="{ column }"> <template #headerCell="{ column }">
<HeaderCell :column="column" /> <HeaderCell :column="column" />
</template> </template>
<!-- 增加对antdv3.x兼容 -->
<template #bodyCell="data">
<slot name="bodyCell" v-bind="data || {}"></slot>
</template>
<!-- <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">--> <!-- <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">-->
<!-- <HeaderCell :column="column" />--> <!-- <HeaderCell :column="column" />-->
<!-- </template>--> <!-- </template>-->
...@@ -36,14 +40,12 @@ ...@@ -36,14 +40,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import type { BasicTableProps, TableActionType, SizeType, ColumnChangeParam } from './types/table' import type { BasicTableProps, TableActionType, SizeType, ColumnChangeParam } from './types/table'
import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue' import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue'
import { Table } from 'ant-design-vue' import { Table } from 'ant-design-vue'
import { BasicForm, useForm } from '/@/components/Form/index' import { BasicForm, useForm } from '/@/components/Form/index'
import { PageWrapperFixedHeightKey } from '/@/components/Page' import { PageWrapperFixedHeightKey } from '/@/components/Page'
import HeaderCell from './components/HeaderCell.vue' import HeaderCell from './components/HeaderCell.vue'
import { InnerHandlers } from './types/table' import { InnerHandlers } from './types/table'
import { usePagination } from './hooks/usePagination' import { usePagination } from './hooks/usePagination'
import { useColumns } from './hooks/useColumns' import { useColumns } from './hooks/useColumns'
import { useDataSource } from './hooks/useDataSource' import { useDataSource } from './hooks/useDataSource'
...@@ -59,12 +61,10 @@ ...@@ -59,12 +61,10 @@
import { useTableFooter } from './hooks/useTableFooter' import { useTableFooter } from './hooks/useTableFooter'
import { useTableForm } from './hooks/useTableForm' import { useTableForm } from './hooks/useTableForm'
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign'
import { omit } from 'lodash-es' import { omit } from 'lodash-es'
import { basicProps } from './props' import { basicProps } from './props'
import { isFunction } from '/@/utils/is' import { isFunction } from '/@/utils/is'
import { warn } from '/@/utils/log' import { warn } from '/@/utils/log'
export default defineComponent({ export default defineComponent({
components: { components: {
Table, Table,
...@@ -93,18 +93,14 @@ ...@@ -93,18 +93,14 @@
setup(props, { attrs, emit, slots, expose }) { setup(props, { attrs, emit, slots, expose }) {
const tableElRef = ref(null) const tableElRef = ref(null)
const tableData = ref<Recordable[]>([]) const tableData = ref<Recordable[]>([])
const wrapRef = ref(null) const wrapRef = ref(null)
const formRef = ref(null) const formRef = ref(null)
const innerPropsRef = ref<Partial<BasicTableProps>>() const innerPropsRef = ref<Partial<BasicTableProps>>()
const { prefixCls } = useDesign('basic-table') const { prefixCls } = useDesign('basic-table')
const [registerForm, formActions] = useForm() const [registerForm, formActions] = useForm()
const getProps = computed(() => { const getProps = computed(() => {
return { ...props, ...unref(innerPropsRef) } as BasicTableProps return { ...props, ...unref(innerPropsRef) } as BasicTableProps
}) })
const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false) const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false)
watchEffect(() => { watchEffect(() => {
unref(isFixedHeightPage) && unref(isFixedHeightPage) &&
...@@ -113,21 +109,19 @@ ...@@ -113,21 +109,19 @@
"'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)", "'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)",
) )
}) })
const { getLoading, setLoading } = useLoading(getProps) const { getLoading, setLoading } = useLoading(getProps)
const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } = const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } =
usePagination(getProps) usePagination(getProps)
const { const {
getRowSelection, getRowSelection,
getRowSelectionRef, getRowSelectionRef,
getSelectRows, getSelectRows,
setSelectedRows,
clearSelectedRowKeys, clearSelectedRowKeys,
getSelectRowKeys, getSelectRowKeys,
deleteSelectRowByKey, deleteSelectRowByKey,
setSelectedRowKeys, setSelectedRowKeys,
} = useRowSelection(getProps, tableData, emit) } = useRowSelection(getProps, tableData, emit)
const { const {
handleTableChange: onTableChange, handleTableChange: onTableChange,
getDataSourceRef, getDataSourceRef,
...@@ -155,7 +149,6 @@ ...@@ -155,7 +149,6 @@
}, },
emit, emit,
) )
function handleTableChange(...args) { function handleTableChange(...args) {
onTableChange.call(undefined, ...args) onTableChange.call(undefined, ...args)
emit('change', ...args) emit('change', ...args)
...@@ -163,10 +156,8 @@ ...@@ -163,10 +156,8 @@
const { onChange } = unref(getProps) const { onChange } = unref(getProps)
onChange && isFunction(onChange) && onChange.call(undefined, ...args) onChange && isFunction(onChange) && onChange.call(undefined, ...args)
} }
const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } = const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } =
useColumns(getProps, getPaginationInfo) useColumns(getProps, getPaginationInfo)
const { getScrollRef, redoHeight } = useTableScroll( const { getScrollRef, redoHeight } = useTableScroll(
getProps, getProps,
tableElRef, tableElRef,
...@@ -176,9 +167,7 @@ ...@@ -176,9 +167,7 @@
wrapRef, wrapRef,
formRef, formRef,
) )
const { scrollTo } = useTableScrollTo(tableElRef, getDataSourceRef) const { scrollTo } = useTableScrollTo(tableElRef, getDataSourceRef)
const { customRow } = useCustomRow(getProps, { const { customRow } = useCustomRow(getProps, {
setSelectedRowKeys, setSelectedRowKeys,
getSelectRowKeys, getSelectRowKeys,
...@@ -186,11 +175,8 @@ ...@@ -186,11 +175,8 @@
getAutoCreateKey, getAutoCreateKey,
emit, emit,
}) })
const { getRowClassName } = useTableStyle(getProps, prefixCls) const { getRowClassName } = useTableStyle(getProps, prefixCls)
const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand(getProps, tableData, emit) const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand(getProps, tableData, emit)
const handlers: InnerHandlers = { const handlers: InnerHandlers = {
onColumnsChange: (data: ColumnChangeParam[]) => { onColumnsChange: (data: ColumnChangeParam[]) => {
emit('columns-change', data) emit('columns-change', data)
...@@ -198,18 +184,14 @@ ...@@ -198,18 +184,14 @@
unref(getProps).onColumnsChange?.(data) unref(getProps).onColumnsChange?.(data)
}, },
} }
const { getHeaderProps } = useTableHeader(getProps, slots, handlers) const { getHeaderProps } = useTableHeader(getProps, slots, handlers)
const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef) const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef)
const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm( const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm(
getProps, getProps,
slots, slots,
fetch, fetch,
getLoading, getLoading,
) )
const getBindValues = computed(() => { const getBindValues = computed(() => {
const dataSource = unref(getDataSourceRef) const dataSource = unref(getDataSourceRef)
let propsData: Recordable = { let propsData: Recordable = {
...@@ -228,14 +210,12 @@ ...@@ -228,14 +210,12 @@
footer: unref(getFooterProps), footer: unref(getFooterProps),
...unref(getExpandOption), ...unref(getExpandOption),
} }
if (slots.expandedRowRender) { // if (slots.expandedRowRender) {
propsData = omit(propsData, 'scroll') // propsData = omit(propsData, 'scroll');
} // }
propsData = omit(propsData, ['class', 'onChange']) propsData = omit(propsData, ['class', 'onChange'])
return propsData return propsData
}) })
const getWrapperClass = computed(() => { const getWrapperClass = computed(() => {
const values = unref(getBindValues) const values = unref(getBindValues)
return [ return [
...@@ -247,7 +227,6 @@ ...@@ -247,7 +227,6 @@
}, },
] ]
}) })
const getEmptyDataIsShowTable = computed(() => { const getEmptyDataIsShowTable = computed(() => {
const { emptyDataIsShowTable, useSearchForm } = unref(getProps) const { emptyDataIsShowTable, useSearchForm } = unref(getProps)
if (emptyDataIsShowTable || !useSearchForm) { if (emptyDataIsShowTable || !useSearchForm) {
...@@ -255,11 +234,9 @@ ...@@ -255,11 +234,9 @@
} }
return !!unref(getDataSourceRef).length return !!unref(getDataSourceRef).length
}) })
function setProps(props: Partial<BasicTableProps>) { function setProps(props: Partial<BasicTableProps>) {
innerPropsRef.value = { ...unref(innerPropsRef), ...props } innerPropsRef.value = { ...unref(innerPropsRef), ...props }
} }
const tableAction: TableActionType = { const tableAction: TableActionType = {
reload, reload,
getSelectRows, getSelectRows,
...@@ -287,6 +264,7 @@ ...@@ -287,6 +264,7 @@
updateTableData, updateTableData,
setShowPagination, setShowPagination,
getShowPagination, getShowPagination,
setSelectedRows,
setCacheColumnsByField, setCacheColumnsByField,
expandAll, expandAll,
expandRows, expandRows,
...@@ -297,11 +275,8 @@ ...@@ -297,11 +275,8 @@
}, },
} }
createTableContext({ ...tableAction, wrapRef, getBindValues }) createTableContext({ ...tableAction, wrapRef, getBindValues })
expose(tableAction) expose(tableAction)
emit('register', tableAction, formActions) emit('register', tableAction, formActions)
return { return {
formRef, formRef,
tableElRef, tableElRef,
...@@ -326,7 +301,6 @@ ...@@ -326,7 +301,6 @@
</script> </script>
<style lang="less"> <style lang="less">
@border-color: #cecece4d; @border-color: #cecece4d;
@prefix-cls: ~'@{namespace}-basic-table'; @prefix-cls: ~'@{namespace}-basic-table';
[data-theme='dark'] { [data-theme='dark'] {
...@@ -335,7 +309,6 @@ ...@@ -335,7 +309,6 @@
background-color: #262626; background-color: #262626;
} }
} }
.@{prefix-cls} { .@{prefix-cls} {
max-width: 100%; max-width: 100%;
height: 100%; height: 100%;
...@@ -350,6 +323,7 @@ ...@@ -350,6 +323,7 @@
padding: 16px; padding: 16px;
.ant-form { .ant-form {
width: 100%;
padding: 12px 10px 6px; padding: 12px 10px 6px;
margin-bottom: 16px; margin-bottom: 16px;
background-color: @component-background; background-color: @component-background;
...@@ -387,7 +361,6 @@ ...@@ -387,7 +361,6 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
//.ant-table-tbody > tr.ant-table-row-selected td { //.ant-table-tbody > tr.ant-table-row-selected td {
//background-color: fade(@primary-color, 8%) !important; //background-color: fade(@primary-color, 8%) !important;
//} //}
......
import type { Component } from 'vue' import type { Component } from 'vue'
import { Input, Select, Checkbox, InputNumber, Switch, DatePicker, TimePicker } from 'ant-design-vue' import {
Input,
Select,
Checkbox,
InputNumber,
Switch,
DatePicker,
TimePicker,
AutoComplete,
Radio,
} from 'ant-design-vue'
import type { ComponentType } from './types/componentType' import type { ComponentType } from './types/componentType'
import { ApiSelect, ApiTreeSelect } from '/@/components/Form' import { ApiSelect, ApiTreeSelect, RadioButtonGroup, ApiRadioGroup } from '/@/components/Form'
const componentMap = new Map<ComponentType, Component>() const componentMap = new Map<ComponentType, Component>()
...@@ -9,11 +19,15 @@ componentMap.set('Input', Input) ...@@ -9,11 +19,15 @@ componentMap.set('Input', Input)
componentMap.set('InputNumber', InputNumber) componentMap.set('InputNumber', InputNumber)
componentMap.set('Select', Select) componentMap.set('Select', Select)
componentMap.set('ApiSelect', ApiSelect) componentMap.set('ApiSelect', ApiSelect)
componentMap.set('AutoComplete', AutoComplete)
componentMap.set('ApiTreeSelect', ApiTreeSelect) componentMap.set('ApiTreeSelect', ApiTreeSelect)
componentMap.set('Switch', Switch) componentMap.set('Switch', Switch)
componentMap.set('Checkbox', Checkbox) componentMap.set('Checkbox', Checkbox)
componentMap.set('DatePicker', DatePicker) componentMap.set('DatePicker', DatePicker)
componentMap.set('TimePicker', TimePicker) componentMap.set('TimePicker', TimePicker)
componentMap.set('RadioGroup', Radio.Group)
componentMap.set('RadioButtonGroup', RadioButtonGroup)
componentMap.set('ApiRadioGroup', ApiRadioGroup)
export function add(compName: ComponentType, component: Component) { export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component) componentMap.set(compName, component)
......
...@@ -7,7 +7,7 @@ const { t } = useI18n() ...@@ -7,7 +7,7 @@ const { t } = useI18n()
* @description: 生成placeholder * @description: 生成placeholder
*/ */
export function createPlaceholderMessage(component: ComponentType) { export function createPlaceholderMessage(component: ComponentType) {
if (component.includes('Input')) { if (component.includes('Input') || component.includes('AutoComplete')) {
return t('common.inputText') return t('common.inputText')
} }
if (component.includes('Picker')) { if (component.includes('Picker')) {
......
...@@ -98,6 +98,7 @@ ...@@ -98,6 +98,7 @@
import type { BasicColumn, ColumnChangeParam } from '../../types/table' import type { BasicColumn, ColumnChangeParam } from '../../types/table'
import { defineComponent, ref, reactive, toRefs, watchEffect, nextTick, unref, computed } from 'vue' import { defineComponent, ref, reactive, toRefs, watchEffect, nextTick, unref, computed } from 'vue'
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue' import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue'
import type { CheckboxChangeEvent } from 'ant-design-vue/lib/checkbox/interface'
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue' import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue'
import { Icon } from '/@/components/Icon' import { Icon } from '/@/components/Icon'
import { ScrollContainer } from '/@/components/Container' import { ScrollContainer } from '/@/components/Container'
...@@ -110,20 +111,17 @@ ...@@ -110,20 +111,17 @@
import { cloneDeep, omit } from 'lodash-es' import { cloneDeep, omit } from 'lodash-es'
import Sortablejs from 'sortablejs' import Sortablejs from 'sortablejs'
import type Sortable from 'sortablejs' import type Sortable from 'sortablejs'
interface State { interface State {
checkAll: boolean checkAll: boolean
isInit?: boolean isInit?: boolean
checkedList: string[] checkedList: string[]
defaultCheckList: string[] defaultCheckList: string[]
} }
interface Options { interface Options {
label: string label: string
value: string value: string
fixed?: boolean | 'left' | 'right' fixed?: boolean | 'left' | 'right'
} }
export default defineComponent({ export default defineComponent({
name: 'ColumnSetting', name: 'ColumnSetting',
components: { components: {
...@@ -138,49 +136,39 @@ ...@@ -138,49 +136,39 @@
Icon, Icon,
}, },
emits: ['columns-change'], emits: ['columns-change'],
setup(_, { emit, attrs }) { setup(_, { emit, attrs }) {
const { t } = useI18n() const { t } = useI18n()
const table = useTableContext() const table = useTableContext()
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys') const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys')
let inited = false let inited = false
const cachePlainOptions = ref<Options[]>([]) const cachePlainOptions = ref<Options[]>([])
const plainOptions = ref<Options[] | any>([]) const plainOptions = ref<Options[] | any>([])
const plainSortOptions = ref<Options[]>([]) const plainSortOptions = ref<Options[]>([])
const columnListRef = ref<ComponentRef>(null) const columnListRef = ref<ComponentRef>(null)
const state = reactive<State>({ const state = reactive<State>({
checkAll: true, checkAll: true,
checkedList: [], checkedList: [],
defaultCheckList: [], defaultCheckList: [],
}) })
const checkIndex = ref(false) const checkIndex = ref(false)
const checkSelect = ref(false) const checkSelect = ref(false)
const { prefixCls } = useDesign('basic-column-setting') const { prefixCls } = useDesign('basic-column-setting')
const getValues = computed(() => { const getValues = computed(() => {
return unref(table?.getBindValues) || {} return unref(table?.getBindValues) || {}
}) })
watchEffect(() => { watchEffect(() => {
setTimeout(() => {
const columns = table.getColumns() const columns = table.getColumns()
if (columns.length && !state.isInit) { if (columns.length && !state.isInit) {
init() init()
} }
}, 0)
}) })
watchEffect(() => { watchEffect(() => {
const values = unref(getValues) const values = unref(getValues)
checkIndex.value = !!values.showIndexColumn checkIndex.value = !!values.showIndexColumn
checkSelect.value = !!values.rowSelection checkSelect.value = !!values.rowSelection
}) })
function getColumns() { function getColumns() {
const ret: Options[] = [] const ret: Options[] = []
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => { table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
...@@ -192,12 +180,10 @@ ...@@ -192,12 +180,10 @@
}) })
return ret return ret
} }
function init() { function init() {
const columns = getColumns() const columns = getColumns()
const checkList = table const checkList = table
.getColumns({ ignoreAction: true }) .getColumns({ ignoreAction: true, ignoreIndex: true })
.map((item) => { .map((item) => {
if (item.defaultHidden) { if (item.defaultHidden) {
return '' return ''
...@@ -205,7 +191,6 @@ ...@@ -205,7 +191,6 @@
return item.dataIndex || item.title return item.dataIndex || item.title
}) })
.filter(Boolean) as string[] .filter(Boolean) as string[]
if (!plainOptions.value.length) { if (!plainOptions.value.length) {
plainOptions.value = columns plainOptions.value = columns
plainSortOptions.value = columns plainSortOptions.value = columns
...@@ -215,7 +200,6 @@ ...@@ -215,7 +200,6 @@
// const fixedColumns = columns.filter((item) => // const fixedColumns = columns.filter((item) =>
// Reflect.has(item, 'fixed') // Reflect.has(item, 'fixed')
// ) as BasicColumn[]; // ) as BasicColumn[];
unref(plainOptions).forEach((item: BasicColumn) => { unref(plainOptions).forEach((item: BasicColumn) => {
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex) const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex)
if (findItem) { if (findItem) {
...@@ -226,9 +210,8 @@ ...@@ -226,9 +210,8 @@
state.isInit = true state.isInit = true
state.checkedList = checkList state.checkedList = checkList
} }
// checkAll change // checkAll change
function onCheckAllChange(e: ChangeEvent) { function onCheckAllChange(e: CheckboxChangeEvent) {
const checkList = plainOptions.value.map((item) => item.value) const checkList = plainOptions.value.map((item) => item.value)
if (e.target.checked) { if (e.target.checked) {
state.checkedList = checkList state.checkedList = checkList
...@@ -238,14 +221,12 @@ ...@@ -238,14 +221,12 @@
setColumns([]) setColumns([])
} }
} }
const indeterminate = computed(() => { const indeterminate = computed(() => {
const len = plainOptions.value.length const len = plainOptions.value.length
let checkedLen = state.checkedList.length let checkedLen = state.checkedList.length
unref(checkIndex) && checkedLen-- // unref(checkIndex) && checkedLen--;
return checkedLen > 0 && checkedLen < len return checkedLen > 0 && checkedLen < len
}) })
// Trigger when check/uncheck a column // Trigger when check/uncheck a column
function onChange(checkedList: string[]) { function onChange(checkedList: string[]) {
const len = plainSortOptions.value.length const len = plainSortOptions.value.length
...@@ -256,7 +237,6 @@ ...@@ -256,7 +237,6 @@
}) })
setColumns(checkedList) setColumns(checkedList)
} }
let sortable: Sortable let sortable: Sortable
let sortableOrder: string[] = [] let sortableOrder: string[] = []
// reset columns // reset columns
...@@ -268,7 +248,6 @@ ...@@ -268,7 +248,6 @@
setColumns(table.getCacheColumns()) setColumns(table.getCacheColumns())
sortable.sort(sortableOrder) sortable.sort(sortableOrder)
} }
// Open the pop-up window for drag and drop initialization // Open the pop-up window for drag and drop initialization
function handleVisibleChange() { function handleVisibleChange() {
if (inited) return if (inited) return
...@@ -290,7 +269,6 @@ ...@@ -290,7 +269,6 @@
} }
// Sort column // Sort column
const columns = cloneDeep(plainSortOptions.value) const columns = cloneDeep(plainSortOptions.value)
if (oldIndex > newIndex) { if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]) columns.splice(newIndex, 0, columns[oldIndex])
columns.splice(oldIndex + 1, 1) columns.splice(oldIndex + 1, 1)
...@@ -298,9 +276,12 @@ ...@@ -298,9 +276,12 @@
columns.splice(newIndex + 1, 0, columns[oldIndex]) columns.splice(newIndex + 1, 0, columns[oldIndex])
columns.splice(oldIndex, 1) columns.splice(oldIndex, 1)
} }
plainSortOptions.value = columns plainSortOptions.value = columns
setColumns(columns) setColumns(
columns
.map((col: Options) => col.value)
.filter((value: string) => state.checkedList.includes(value)),
)
}, },
}) })
// 记录原始order 序列 // 记录原始order 序列
...@@ -308,24 +289,20 @@ ...@@ -308,24 +289,20 @@
inited = true inited = true
}) })
} }
// Control whether the serial number column is displayed // Control whether the serial number column is displayed
function handleIndexCheckChange(e: ChangeEvent) { function handleIndexCheckChange(e: CheckboxChangeEvent) {
table.setProps({ table.setProps({
showIndexColumn: e.target.checked, showIndexColumn: e.target.checked,
}) })
} }
// Control whether the check box is displayed // Control whether the check box is displayed
function handleSelectCheckChange(e: ChangeEvent) { function handleSelectCheckChange(e: CheckboxChangeEvent) {
table.setProps({ table.setProps({
rowSelection: e.target.checked ? defaultRowSelection : undefined, rowSelection: e.target.checked ? defaultRowSelection : undefined,
}) })
} }
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') { function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
if (!state.checkedList.includes(item.dataIndex as string)) return if (!state.checkedList.includes(item.dataIndex as string)) return
const columns = getColumns() as BasicColumn[] const columns = getColumns() as BasicColumn[]
const isFixed = item.fixed === fixed ? false : fixed const isFixed = item.fixed === fixed ? false : fixed
const index = columns.findIndex((col) => col.dataIndex === item.dataIndex) const index = columns.findIndex((col) => col.dataIndex === item.dataIndex)
...@@ -333,14 +310,12 @@ ...@@ -333,14 +310,12 @@
columns[index].fixed = isFixed columns[index].fixed = isFixed
} }
item.fixed = isFixed item.fixed = isFixed
if (isFixed && !item.width) { if (isFixed && !item.width) {
item.width = 100 item.width = 100
} }
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed }) table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed })
setColumns(columns) setColumns(columns)
} }
function setColumns(columns: BasicColumn[] | string[]) { function setColumns(columns: BasicColumn[] | string[]) {
table.setColumns(columns) table.setColumns(columns)
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => { const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
...@@ -351,14 +326,11 @@ ...@@ -351,14 +326,11 @@
) !== -1 ) !== -1
return { dataIndex: col.value, fixed: col.fixed, visible } return { dataIndex: col.value, fixed: col.fixed, visible }
}) })
emit('columns-change', data) emit('columns-change', data)
} }
function getPopupContainer() { function getPopupContainer() {
return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer() return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer()
} }
return { return {
t, t,
...toRefs(state), ...toRefs(state),
...@@ -388,7 +360,6 @@ ...@@ -388,7 +360,6 @@
margin: 0 5px; margin: 0 5px;
cursor: move; cursor: move;
} }
.@{prefix-cls} { .@{prefix-cls} {
&__popover-title { &__popover-title {
position: relative; position: relative;
......
import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table' import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table'
import type { PaginationProps } from '../types/pagination' import type { PaginationProps } from '../types/pagination'
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
import { computed, Ref, ref, toRaw, unref, watch } from 'vue' import { computed, Ref, ref, reactive, toRaw, unref, watch } from 'vue'
import { renderEditCell } from '../components/editable' import { renderEditCell } from '../components/editable'
import { usePermission } from '/@/hooks/web/usePermission' import { usePermission } from '/@/hooks/web/usePermission'
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
...@@ -167,7 +167,7 @@ export function useColumns( ...@@ -167,7 +167,7 @@ export function useColumns(
if ((edit || editRow) && !isDefaultAction) { if ((edit || editRow) && !isDefaultAction) {
column.customRender = renderEditCell(column) column.customRender = renderEditCell(column)
} }
return column return reactive(column)
}) })
}) })
...@@ -293,7 +293,7 @@ export function formatCell(text: string, format: CellFormat, record: Recordable, ...@@ -293,7 +293,7 @@ export function formatCell(text: string, format: CellFormat, record: Recordable,
try { try {
// date type // date type
const DATE_FORMAT_PREFIX = 'date|' const DATE_FORMAT_PREFIX = 'date|'
if (isString(format) && format.startsWith(DATE_FORMAT_PREFIX)) { if (isString(format) && format.startsWith(DATE_FORMAT_PREFIX) && text) {
const dateFormat = format.replace(DATE_FORMAT_PREFIX, '') const dateFormat = format.replace(DATE_FORMAT_PREFIX, '')
if (!dateFormat) { if (!dateFormat) {
......
...@@ -76,6 +76,9 @@ export function useTable(tableProps?: Props): [ ...@@ -76,6 +76,9 @@ export function useTable(tableProps?: Props): [
redoHeight: () => { redoHeight: () => {
getTableInstance().redoHeight() getTableInstance().redoHeight()
}, },
setSelectedRows: (rows: Recordable[]) => {
return toRaw(getTableInstance().setSelectedRows(rows))
},
setLoading: (loading: boolean) => { setLoading: (loading: boolean) => {
getTableInstance().setLoading(loading) getTableInstance().setLoading(loading)
}, },
......
...@@ -36,14 +36,13 @@ export function useTableFooter( ...@@ -36,14 +36,13 @@ export function useTableFooter(
nextTick(() => { nextTick(() => {
const tableEl = unref(tableElRef) const tableEl = unref(tableElRef)
if (!tableEl) return if (!tableEl) return
const bodyDomList = tableEl.$el.querySelectorAll('.ant-table-body') const bodyDom = tableEl.$el.querySelector('.ant-table-content')
const bodyDom = bodyDomList[0]
useEventListener({ useEventListener({
el: bodyDom, el: bodyDom,
name: 'scroll', name: 'scroll',
listener: () => { listener: () => {
const footerBodyDom = tableEl.$el.querySelector( const footerBodyDom = tableEl.$el.querySelector(
'.ant-table-footer .ant-table-body', '.ant-table-footer .ant-table-content',
) as HTMLDivElement ) as HTMLDivElement
if (!footerBodyDom || !bodyDom) return if (!footerBodyDom || !bodyDom) return
footerBodyDom.scrollLeft = bodyDom.scrollLeft footerBodyDom.scrollLeft = bodyDom.scrollLeft
......
...@@ -87,7 +87,7 @@ export function useTableScroll( ...@@ -87,7 +87,7 @@ export function useTableScroll(
bodyEl!.style.height = 'unset' bodyEl!.style.height = 'unset'
if (!unref(getCanResize) || tableData.length === 0) return if (!unref(getCanResize) || !unref(tableData) || tableData.length === 0) return
await nextTick() await nextTick()
// Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight // Add a delay to get the correct bottomIncludeBody paginationHeight footerHeight headerHeight
...@@ -186,7 +186,7 @@ export function useTableScroll( ...@@ -186,7 +186,7 @@ export function useTableScroll(
const columns = unref(columnsRef).filter((item) => !item.defaultHidden) const columns = unref(columnsRef).filter((item) => !item.defaultHidden)
columns.forEach((item) => { columns.forEach((item) => {
width += Number.parseInt(item.width as string) || 0 width += Number.parseFloat(item.width as string) || 0
}) })
const unsetWidthColumns = columns.filter((item) => !Reflect.has(item, 'width')) const unsetWidthColumns = columns.filter((item) => !Reflect.has(item, 'width'))
......
...@@ -3,8 +3,12 @@ export type ComponentType = ...@@ -3,8 +3,12 @@ export type ComponentType =
| 'InputNumber' | 'InputNumber'
| 'Select' | 'Select'
| 'ApiSelect' | 'ApiSelect'
| 'AutoComplete'
| 'ApiTreeSelect' | 'ApiTreeSelect'
| 'Checkbox' | 'Checkbox'
| 'Switch' | 'Switch'
| 'DatePicker' | 'DatePicker'
| 'TimePicker' | 'TimePicker'
| 'RadioGroup'
| 'RadioButtonGroup'
| 'ApiRadioGroup'
...@@ -7,9 +7,12 @@ interface PaginationRenderProps { ...@@ -7,9 +7,12 @@ interface PaginationRenderProps {
originalElement: any originalElement: any
} }
type PaginationPositon = 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight'
export declare class PaginationConfig extends Pagination { export declare class PaginationConfig extends Pagination {
position?: 'top' | 'bottom' | 'both' position?: PaginationPositon[]
} }
export interface PaginationProps { export interface PaginationProps {
/** /**
* total number of data items * total number of data items
...@@ -96,4 +99,11 @@ export interface PaginationProps { ...@@ -96,4 +99,11 @@ export interface PaginationProps {
* @type Function * @type Function
*/ */
itemRender?: (props: PaginationRenderProps) => VNodeChild | JSX.Element itemRender?: (props: PaginationRenderProps) => VNodeChild | JSX.Element
/**
* specify the position of Pagination
* @default ['bottomRight']
* @type string[]
*/
position?: PaginationPositon[]
} }
...@@ -84,10 +84,11 @@ export type SizeType = 'default' | 'middle' | 'small' | 'large' ...@@ -84,10 +84,11 @@ export type SizeType = 'default' | 'middle' | 'small' | 'large'
export interface TableActionType { export interface TableActionType {
reload: (opt?: FetchParams) => Promise<void> reload: (opt?: FetchParams) => Promise<void>
setSelectedRows: (rows: Recordable[]) => void
getSelectRows: <T = Recordable>() => T[] getSelectRows: <T = Recordable>() => T[]
clearSelectedRowKeys: () => void clearSelectedRowKeys: () => void
expandAll: () => void expandAll: () => void
expandRows: (keys: string[]) => void expandRows: (keys: string[] | number[]) => void
collapseAll: () => void collapseAll: () => void
scrollTo: (pos: string) => void // pos: id | "top" | "bottom" scrollTo: (pos: string) => void // pos: id | "top" | "bottom"
getSelectRowKeys: () => string[] getSelectRowKeys: () => string[]
...@@ -456,6 +457,8 @@ export interface BasicColumn extends ColumnProps<Recordable> { ...@@ -456,6 +457,8 @@ export interface BasicColumn extends ColumnProps<Recordable> {
column: BasicColumn column: BasicColumn
index: number index: number
}) => VNodeChild | JSX.Element }) => VNodeChild | JSX.Element
// 动态 Disabled
editDynamicDisabled?: boolean | ((record: Recordable) => boolean)
} }
export type ColumnChangeParam = { export type ColumnChangeParam = {
......
import BasicTree from './src/Tree.vue' import BasicTree from './src/BasicTree.vue'
import './style' import './style'
export { BasicTree } export { BasicTree }
export type { ContextMenuItem } from '/@/hooks/web/useContextMenu' export type { ContextMenuItem } from '/@/hooks/web/useContextMenu'
export * from './src/tree' export * from './src/types/tree'
...@@ -37,12 +37,9 @@ ...@@ -37,12 +37,9 @@
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { useDebounceFn } from '@vueuse/core' import { useDebounceFn } from '@vueuse/core'
import { createBEM } from '/@/utils/bem' import { createBEM } from '/@/utils/bem'
import { ToolbarEnum } from './tree' import { ToolbarEnum } from '../types/tree'
const searchValue = ref('') const searchValue = ref('')
const [bem] = createBEM('tree-header') const [bem] = createBEM('tree-header')
const props = defineProps({ const props = defineProps({
helpMessage: { helpMessage: {
type: [String, Array] as PropType<string | string[]>, type: [String, Array] as PropType<string | string[]>,
...@@ -78,10 +75,8 @@ ...@@ -78,10 +75,8 @@
}, },
} as const) } as const)
const emit = defineEmits(['strictly-change', 'search']) const emit = defineEmits(['strictly-change', 'search'])
const slots = useSlots() const slots = useSlots()
const { t } = useI18n() const { t } = useI18n()
const getInputSearchCls = computed(() => { const getInputSearchCls = computed(() => {
const titleExists = slots.headerTitle || props.title const titleExists = slots.headerTitle || props.title
return [ return [
...@@ -92,7 +87,6 @@ ...@@ -92,7 +87,6 @@
}, },
] ]
}) })
const toolbarList = computed(() => { const toolbarList = computed(() => {
const { checkable } = props const { checkable } = props
const defaultToolbarList = [ const defaultToolbarList = [
...@@ -103,7 +97,6 @@ ...@@ -103,7 +97,6 @@
divider: checkable, divider: checkable,
}, },
] ]
return checkable return checkable
? [ ? [
{ label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL }, { label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
...@@ -114,14 +107,10 @@ ...@@ -114,14 +107,10 @@
}, },
...defaultToolbarList, ...defaultToolbarList,
{ label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY }, { label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY },
{ { label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY },
label: t('component.tree.checkUnStrictly'),
value: ToolbarEnum.CHECK_UN_STRICTLY,
},
] ]
: defaultToolbarList : defaultToolbarList
}) })
function handleMenuClick(e: { key: ToolbarEnum }) { function handleMenuClick(e: { key: ToolbarEnum }) {
const { key } = e const { key } = e
switch (key) { switch (key) {
...@@ -145,20 +134,16 @@ ...@@ -145,20 +134,16 @@
break break
} }
} }
function emitChange(value?: string): void { function emitChange(value?: string): void {
emit('search', value) emit('search', value)
} }
const debounceEmitChange = useDebounceFn(emitChange, 200) const debounceEmitChange = useDebounceFn(emitChange, 200)
watch( watch(
() => searchValue.value, () => searchValue.value,
(v) => { (v) => {
debounceEmitChange(v) debounceEmitChange(v)
}, },
) )
watch( watch(
() => props.searchText, () => props.searchText,
(v) => { (v) => {
......
import type { InsertNodeParams, KeyType, FieldNames } from './tree' import type { InsertNodeParams, KeyType, FieldNames, TreeItem } from '../types/tree'
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'
...@@ -176,6 +176,23 @@ export function useTree(treeDataRef: Ref<TreeDataItem[]>, getFieldNames: Compute ...@@ -176,6 +176,23 @@ export function useTree(treeDataRef: Ref<TreeDataItem[]>, 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 { return {
deleteNodeByKey, deleteNodeByKey,
insertNodeByKey, insertNodeByKey,
...@@ -185,5 +202,6 @@ export function useTree(treeDataRef: Ref<TreeDataItem[]>, getFieldNames: Compute ...@@ -185,5 +202,6 @@ export function useTree(treeDataRef: Ref<TreeDataItem[]>, getFieldNames: Compute
getAllKeys, getAllKeys,
getChildrenKeys, getChildrenKeys,
getEnabledKeys, getEnabledKeys,
getSelectedNode,
} }
} }
...@@ -126,6 +126,11 @@ export const treeProps = buildProps({ ...@@ -126,6 +126,11 @@ export const treeProps = buildProps({
checkOnSearch: Boolean, checkOnSearch: Boolean,
// 搜索完成自动select所有结果 // 搜索完成自动select所有结果
selectedOnSearch: Boolean, selectedOnSearch: Boolean,
loading: {
type: Boolean,
default: false,
},
treeWrapperClassName: String,
}) })
export type TreeProps = ExtractPropTypes<typeof treeProps> export type TreeProps = ExtractPropTypes<typeof treeProps>
...@@ -133,6 +138,7 @@ export type TreeProps = ExtractPropTypes<typeof treeProps> ...@@ -133,6 +138,7 @@ export type TreeProps = ExtractPropTypes<typeof treeProps>
export interface ContextMenuItem { export interface ContextMenuItem {
label: string label: string
icon?: string icon?: string
hidden?: boolean
disabled?: boolean disabled?: boolean
handler?: Fn handler?: Fn
divider?: boolean divider?: boolean
...@@ -177,4 +183,5 @@ export interface TreeActionType { ...@@ -177,4 +183,5 @@ export interface TreeActionType {
updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void
setSearchValue: (value: string) => void setSearchValue: (value: string) => void
getSearchValue: () => string getSearchValue: () => string
getSelectedNode: (key: KeyType, treeList?: TreeItem[], selectNode?: TreeItem | null) => TreeItem | null
} }
@import './pagination.less'; @import url('./pagination.less');
@import './input.less'; @import url('./input.less');
@import './btn.less'; @import url('./btn.less');
.ant-image-preview-root { .ant-image-preview-root {
img { img {
...@@ -57,3 +57,11 @@ span.anticon:not(.app-iconify) { ...@@ -57,3 +57,11 @@ span.anticon:not(.app-iconify) {
border-top: 0 !important; border-top: 0 !important;
border-left: 0 !important; border-left: 0 !important;
} }
.ant-form-item-control-input-content {
> div {
> div {
max-width: 100%;
}
}
}
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
// input // input
.ant-input { .ant-input {
&-number { &-number,
&-number-group-wrapper {
min-width: 110px; min-width: 110px;
width: 100% !important;
max-width: 100%;
} }
} }
......
@import 'transition/index.less'; @import url('transition/index.less');
@import 'var/index.less'; @import url('var/index.less');
@import 'public.less'; @import url('public.less');
@import 'ant/index.less'; @import url('ant/index.less');
@import './theme.less'; @import url('./theme.less');
input:-webkit-autofill { input:-webkit-autofill {
box-shadow: 0 0 0 1000px white inset !important; box-shadow: 0 0 0 1000px white inset !important;
...@@ -21,8 +21,8 @@ html, ...@@ -21,8 +21,8 @@ html,
body { body {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: visible !important; overflow: visible;
overflow-x: hidden !important; overflow-x: hidden;
&.color-weak { &.color-weak {
filter: invert(80%); filter: invert(80%);
...@@ -40,10 +40,5 @@ button, ...@@ -40,10 +40,5 @@ button,
div, div,
svg, svg,
span { span {
outline: none !important; outline: none;
}
// ================ vben ================
.vben-default-layout-main {
margin-left: 0 !important;
} }
.fade-transition {
&-enter-active,
&-leave-active {
transition: opacity 0.2s ease-in-out;
}
&-enter-from,
&-leave-to {
opacity: 0;
}
}
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity 0.2s ease-in-out; transition: opacity 0.2s ease-in-out;
......
@import './base.less'; @import url('./base.less');
@import './fade.less'; @import url('./fade.less');
@import './scale.less'; @import url('./scale.less');
@import './slide.less'; @import url('./slide.less');
@import './scroll.less'; @import url('./scroll.less');
@import './zoom.less'; @import url('./zoom.less');
.collapse-transition { .collapse-transition {
transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out; transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out;
......
@import (reference) '../color.less'; @import (reference) '../color.less';
@import 'easing'; @import url('easing');
@import 'breakpoint'; @import url('breakpoint');
@namespace: vben; @namespace: vben;
......
...@@ -30,14 +30,18 @@ export enum SessionTimeoutProcessingEnum { ...@@ -30,14 +30,18 @@ export enum SessionTimeoutProcessingEnum {
*/ */
export enum PermissionModeEnum { export enum PermissionModeEnum {
// role // role
// 角色权限
ROLE = 'ROLE', ROLE = 'ROLE',
// black // black
// 后端
BACK = 'BACK', BACK = 'BACK',
// route mapping // route mapping
// 路由映射
ROUTE_MAPPING = 'ROUTE_MAPPING', ROUTE_MAPPING = 'ROUTE_MAPPING',
} }
// Route switching animation // Route switching animation
// 路由切换动画
export enum RouterTransitionEnum { export enum RouterTransitionEnum {
ZOOM_FADE = 'zoom-fade', ZOOM_FADE = 'zoom-fade',
ZOOM_OUT = 'zoom-out', ZOOM_OUT = 'zoom-out',
......
/** /**
* @description: Request result set * @description: Request result set
*/ */
export enum ResultEnum {} export enum ResultEnum {
SUCCESS = 0,
ERROR = -1,
TIMEOUT = 401,
TYPE = 'success',
}
/** /**
* @description: request method * @description: request method
......
...@@ -31,7 +31,6 @@ export function createContext<T>(context: any, key: InjectionKey<T> = Symbol(), ...@@ -31,7 +31,6 @@ export function createContext<T>(context: any, key: InjectionKey<T> = Symbol(),
} }
export function useContext<T>(key: InjectionKey<T>, native?: boolean): T export function useContext<T>(key: InjectionKey<T>, native?: boolean): T
export function useContext<T>(key: InjectionKey<T>, defaultValue?: any, native?: boolean): T
export function useContext<T>(key: InjectionKey<T> = Symbol(), defaultValue?: any): ShallowUnwrap<T> { export function useContext<T>(key: InjectionKey<T> = Symbol(), defaultValue?: any): ShallowUnwrap<T> {
return inject(key, defaultValue || {}) return inject(key, defaultValue || {})
......
...@@ -93,8 +93,8 @@ export function useMenuSetting() { ...@@ -93,8 +93,8 @@ export function useMenuSetting() {
}) })
const getMiniWidthNumber = computed(() => { const getMiniWidthNumber = computed(() => {
const { collapsedShowTitle } = appStore.getMenuSetting const { collapsedShowTitle, siderHidden } = appStore.getMenuSetting
return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH return siderHidden ? 0 : collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH
}) })
const getCalcContentWidth = computed(() => { const getCalcContentWidth = computed(() => {
......
...@@ -8,9 +8,11 @@ import { useEventListener } from '/@/hooks/event/useEventListener' ...@@ -8,9 +8,11 @@ import { useEventListener } from '/@/hooks/event/useEventListener'
import { useBreakpoint } from '/@/hooks/event/useBreakpoint' import { useBreakpoint } from '/@/hooks/event/useBreakpoint'
import echarts from '/@/utils/lib/echarts' import echarts from '/@/utils/lib/echarts'
import { useRootSetting } from '/@/hooks/setting/useRootSetting' import { useRootSetting } from '/@/hooks/setting/useRootSetting'
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'
export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' | 'default' = 'default') { export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' | 'default' = 'default') {
const { getDarkMode: getSysDarkMode } = useRootSetting() const { getDarkMode: getSysDarkMode } = useRootSetting()
const { getCollapsed } = useMenuSetting()
const getDarkMode = computed(() => { const getDarkMode = computed(() => {
return theme === 'default' ? getSysDarkMode.value : theme return theme === 'default' ? getSysDarkMode.value : theme
...@@ -76,7 +78,12 @@ export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' | ...@@ -76,7 +78,12 @@ export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' |
} }
function resize() { function resize() {
chartInstance?.resize() chartInstance?.resize({
animation: {
duration: 300,
easing: 'quadraticIn',
},
})
} }
watch( watch(
...@@ -90,6 +97,12 @@ export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' | ...@@ -90,6 +97,12 @@ export function useECharts(elRef: Ref<HTMLDivElement>, theme: 'light' | 'dark' |
}, },
) )
watch(getCollapsed, (_) => {
useTimeoutFn(() => {
resizeFn()
}, 300)
})
tryOnUnmounted(() => { tryOnUnmounted(() => {
if (!chartInstance) return if (!chartInstance) return
removeResizeFn() removeResizeFn()
......
import type { RouteLocationRaw, Router } from 'vue-router' import type { RouteLocationRaw, Router } from 'vue-router'
import { PageEnum } from '/@/enums/pageEnum' import { PageEnum } from '/@/enums/pageEnum'
import { isString } from '/@/utils/is'
import { unref } from 'vue' import { unref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { REDIRECT_NAME } from '/@/router/constant' import { REDIRECT_NAME } from '/@/router/constant'
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum } export type PathAsPageEnum<T> = T extends { path: string } ? T & { path: PageEnum } : T
export type RouteLocationRawEx = PathAsPageEnum<RouteLocationRaw>
function handleError(e: Error) { function handleError(e: Error) {
console.error(e) console.error(e)
} }
// page switch /**
* page switch
*/
export function useGo(_router?: Router) { export function useGo(_router?: Router) {
let router const { push, replace } = _router || useRouter()
if (!_router) { function go(opt: RouteLocationRawEx = PageEnum.BASE_HOME, isReplace = false) {
router = useRouter()
}
const { push, replace } = _router || router
function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
if (!opt) { if (!opt) {
return return
} }
if (isString(opt)) {
isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError) isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError)
} else {
const o = opt as RouteLocationRaw
isReplace ? replace(o).catch(handleError) : push(o).catch(handleError)
}
} }
return go return go
} }
...@@ -38,7 +31,7 @@ export function useGo(_router?: Router) { ...@@ -38,7 +31,7 @@ export function useGo(_router?: Router) {
* @description: redo current page * @description: redo current page
*/ */
export const useRedo = (_router?: Router) => { export const useRedo = (_router?: Router) => {
const { push, currentRoute } = _router || useRouter() const { replace, currentRoute } = _router || useRouter()
const { query, params = {}, name, fullPath } = unref(currentRoute.value) const { query, params = {}, name, fullPath } = unref(currentRoute.value)
function redo(): Promise<boolean> { function redo(): Promise<boolean> {
return new Promise((resolve) => { return new Promise((resolve) => {
...@@ -53,7 +46,7 @@ export const useRedo = (_router?: Router) => { ...@@ -53,7 +46,7 @@ export const useRedo = (_router?: Router) => {
params['_redirect_type'] = 'path' params['_redirect_type'] = 'path'
params['path'] = fullPath params['path'] = fullPath
} }
push({ name: REDIRECT_NAME, params, query }).then(() => resolve(true)) replace({ name: REDIRECT_NAME, params, query }).then(() => resolve(true))
}) })
} }
return redo return redo
......
...@@ -39,6 +39,7 @@ export function usePermission() { ...@@ -39,6 +39,7 @@ export function usePermission() {
/** /**
* Reset and regain authority resource information * Reset and regain authority resource information
* 重置和重新获得权限资源信息
* @param id * @param id
*/ */
async function resume() { async function resume() {
......
...@@ -11,20 +11,26 @@ ...@@ -11,20 +11,26 @@
import { Tooltip } from 'ant-design-vue' import { Tooltip } from 'ant-design-vue'
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { useFullscreen } from '@vueuse/core' import { useFullscreen } from '@vueuse/core'
import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue' import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue'
export default defineComponent({ export default defineComponent({
name: 'FullScreen', name: 'FullScreen',
components: { FullscreenExitOutlined, FullscreenOutlined, Tooltip }, components: { FullscreenExitOutlined, FullscreenOutlined, Tooltip },
setup() { setup() {
const { t } = useI18n() const { t } = useI18n()
const { toggle, isFullscreen } = useFullscreen() const { toggle, isFullscreen } = useFullscreen()
// 重新检查全屏状态
isFullscreen.value = !!(
document.fullscreenElement ||
// @ts-ignore
document.webkitFullscreenElement ||
// @ts-ignore
document.mozFullScreenElement ||
// @ts-ignore
document.msFullscreenElement
)
const getTitle = computed(() => { const getTitle = computed(() => {
return unref(isFullscreen) ? t('layout.header.tooltipExitFull') : t('layout.header.tooltipEntryFull') return unref(isFullscreen) ? t('layout.header.tooltipExitFull') : t('layout.header.tooltipEntryFull')
}) })
return { return {
getTitle, getTitle,
isFullscreen, isFullscreen,
......
...@@ -33,25 +33,19 @@ ...@@ -33,25 +33,19 @@
<script lang="ts"> <script lang="ts">
// components // components
import { Dropdown, Menu } from 'ant-design-vue' import { Dropdown, Menu } from 'ant-design-vue'
import type { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'
import { defineComponent, computed } from 'vue' import { defineComponent, computed } from 'vue'
import { DOC_URL } from '/@/settings/siteSetting' import { DOC_URL } from '/@/settings/siteSetting'
import { useUserStore } from '/@/store/modules/user' import { useUserStore } from '/@/store/modules/user'
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting' import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { useDesign } from '/@/hooks/web/useDesign' import { useDesign } from '/@/hooks/web/useDesign'
import { useModal } from '/@/components/Modal' import { useModal } from '/@/components/Modal'
import headerImg from '/@/assets/images/header.jpg' import headerImg from '/@/assets/images/header.jpg'
import { propTypes } from '/@/utils/propTypes' import { propTypes } from '/@/utils/propTypes'
import { openWindow } from '/@/utils' import { openWindow } from '/@/utils'
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent' import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'
type MenuEvent = 'logout' | 'doc' | 'lock' type MenuEvent = 'logout' | 'doc' | 'lock'
export default defineComponent({ export default defineComponent({
name: 'UserDropdown', name: 'UserDropdown',
components: { components: {
...@@ -69,30 +63,24 @@ ...@@ -69,30 +63,24 @@
const { t } = useI18n() const { t } = useI18n()
const { getShowDoc, getUseLockPage } = useHeaderSetting() const { getShowDoc, getUseLockPage } = useHeaderSetting()
const userStore = useUserStore() const userStore = useUserStore()
const getUserInfo = computed(() => { const getUserInfo = computed(() => {
const { realName = '', avatar, desc } = userStore.getUserInfo || {} const { realName = '', avatar, desc } = userStore.getUserInfo || {}
return { realName, avatar: avatar || headerImg, desc } return { realName, avatar: avatar || headerImg, desc }
}) })
const [register, { openModal }] = useModal() const [register, { openModal }] = useModal()
function handleLock() { function handleLock() {
openModal(true) openModal(true)
} }
// login out // login out
function handleLoginOut() { function handleLoginOut() {
userStore.confirmLoginOut() userStore.confirmLoginOut()
} }
// open doc // open doc
function openDoc() { function openDoc() {
openWindow(DOC_URL) openWindow(DOC_URL)
} }
function handleMenuClick(e: MenuInfo) {
function handleMenuClick(e: { key: MenuEvent }) { switch (e.key as MenuEvent) {
switch (e.key) {
case 'logout': case 'logout':
handleLoginOut() handleLoginOut()
break break
...@@ -104,7 +92,6 @@ ...@@ -104,7 +92,6 @@
break break
} }
} }
return { return {
prefixCls, prefixCls,
t, t,
...@@ -119,7 +106,6 @@ ...@@ -119,7 +106,6 @@
</script> </script>
<style lang="less"> <style lang="less">
@prefix-cls: ~'@{namespace}-header-user-dropdown'; @prefix-cls: ~'@{namespace}-header-user-dropdown';
.@{prefix-cls} { .@{prefix-cls} {
height: @header-height; height: @header-height;
padding: 0 0 0 10px; padding: 0 0 0 10px;
...@@ -153,11 +139,9 @@ ...@@ -153,11 +139,9 @@
&:hover { &:hover {
background-color: @header-light-bg-hover-color; background-color: @header-light-bg-hover-color;
} }
.@{prefix-cls}__name { .@{prefix-cls}__name {
color: @text-color-base; color: @text-color-base;
} }
.@{prefix-cls}__desc { .@{prefix-cls}__desc {
color: @header-light-desc-color; color: @header-light-desc-color;
} }
......
import type { Ref } from 'vue' import type { Ref } from 'vue'
import { computed, unref, onMounted, nextTick, ref } from 'vue' import { computed, unref, onMounted, nextTick } from 'vue'
import { TriggerEnum } from '/@/enums/menuEnum' import { TriggerEnum } from '/@/enums/menuEnum'
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting' import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'
import { useDebounceFn } from '@vueuse/core' import { useDebounceFn } from '@vueuse/core'
import { useAppStore } from '/@/store/modules/app'
/** /**
* Handle related operations of menu events * Handle related operations of menu events
*/ */
export function useSiderEvent() { export function useSiderEvent() {
const brokenRef = ref(false) const appStore = useAppStore()
const { getMiniWidthNumber } = useMenuSetting() const { getMiniWidthNumber } = useMenuSetting()
const getCollapsedWidth = computed(() => { const getCollapsedWidth = computed(() => {
return unref(brokenRef) ? 0 : unref(getMiniWidthNumber) return unref(getMiniWidthNumber)
}) })
function onBreakpointChange(broken: boolean) { function onBreakpointChange(broken: boolean) {
brokenRef.value = broken appStore.setProjectConfig({
menuSetting: {
siderHidden: broken,
},
})
} }
return { getCollapsedWidth, onBreakpointChange } return { getCollapsedWidth, onBreakpointChange }
......
...@@ -181,6 +181,25 @@ html[data-theme='light'] { ...@@ -181,6 +181,25 @@ html[data-theme='light'] {
} }
} }
.ant-tabs-dropdown-menu {
&-title-content {
display: flex;
align-items: center;
.@{prefix-cls} {
&-content__info {
width: auto;
margin-left: 0;
line-height: 28px;
}
}
}
&-item-remove {
margin-left: auto;
}
}
.multiple-tabs__dropdown { .multiple-tabs__dropdown {
.ant-dropdown-content { .ant-dropdown-content {
width: 172px; width: 172px;
......
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
<keep-alive v-if="openCache" :include="getCaches"> <keep-alive v-if="openCache" :include="getCaches">
<component :is="Component" :key="route.fullPath" /> <component :is="Component" :key="route.fullPath" />
</keep-alive> </keep-alive>
<component v-else :is="Component" :key="route.fullPath" /> <div v-else :key="route.name">
<component :is="Component" :key="route.fullPath" />
</div>
</transition> </transition>
</template> </template>
</RouterView> </RouterView>
...@@ -26,37 +28,27 @@ ...@@ -26,37 +28,27 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, unref } from 'vue' import { computed, defineComponent, unref } from 'vue'
import FrameLayout from '/@/layouts/iframe/index.vue' import FrameLayout from '/@/layouts/iframe/index.vue'
import { useRootSetting } from '/@/hooks/setting/useRootSetting' import { useRootSetting } from '/@/hooks/setting/useRootSetting'
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting' import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting' import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'
import { getTransitionName } from './transition' import { getTransitionName } from './transition'
import { useMultipleTabStore } from '/@/store/modules/multipleTab' import { useMultipleTabStore } from '/@/store/modules/multipleTab'
export default defineComponent({ export default defineComponent({
name: 'PageLayout', name: 'PageLayout',
components: { FrameLayout }, components: { FrameLayout },
setup() { setup() {
const { getShowMultipleTab } = useMultipleTabSetting() const { getShowMultipleTab } = useMultipleTabSetting()
const tabStore = useMultipleTabStore() const tabStore = useMultipleTabStore()
const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting() const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting()
const { getBasicTransition, getEnableTransition } = useTransitionSetting() const { getBasicTransition, getEnableTransition } = useTransitionSetting()
const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab)) const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab))
const getCaches = computed((): string[] => { const getCaches = computed((): string[] => {
if (!unref(getOpenKeepAlive)) { if (!unref(getOpenKeepAlive)) {
return [] return []
} }
return tabStore.getCachedTabList return tabStore.getCachedTabList
}) })
return { return {
getTransitionName, getTransitionName,
openCache, openCache,
......
...@@ -3,7 +3,7 @@ export default { ...@@ -3,7 +3,7 @@ export default {
header: { header: {
// user dropdown // user dropdown
dropdownItemDoc: 'Document', dropdownItemDoc: 'Document',
dropdownItemLoginOut: 'Login Out', dropdownItemLoginOut: 'Log Out',
tooltipErrorLog: 'Error log', tooltipErrorLog: 'Error log',
tooltipLock: 'Lock screen', tooltipLock: 'Lock screen',
......
...@@ -111,6 +111,7 @@ export default { ...@@ -111,6 +111,7 @@ export default {
dynamicForm: 'Dynamic', dynamicForm: 'Dynamic',
customerForm: 'Custom', customerForm: 'Custom',
appendForm: 'Append', appendForm: 'Append',
tabsForm: 'TabsForm',
}, },
iframe: { iframe: {
frame: 'External', frame: 'External',
......
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
api: { api: {
operationFailed: 'Operation failed', operationFailed: 'Operation failed',
errorTip: 'Error Tip', errorTip: 'Error Tip',
successTip: 'Success Tip',
errorMessage: 'The operation failed, the system is abnormal!', errorMessage: 'The operation failed, the system is abnormal!',
timeoutMessage: 'Login timed out, please log in again!', timeoutMessage: 'Login timed out, please log in again!',
apiTimeoutMessage: 'The interface request timed out, please refresh the page and try again!', apiTimeoutMessage: 'The interface request timed out, please refresh the page and try again!',
...@@ -85,7 +86,7 @@ export default { ...@@ -85,7 +86,7 @@ export default {
loginSuccessDesc: 'Welcome back', loginSuccessDesc: 'Welcome back',
// placeholder // placeholder
accountPlaceholder: 'Please input account', accountPlaceholder: 'Please input username',
passwordPlaceholder: 'Please input password', passwordPlaceholder: 'Please input password',
smsPlaceholder: 'Please input sms code', smsPlaceholder: 'Please input sms code',
mobilePlaceholder: 'Please input mobile', mobilePlaceholder: 'Please input mobile',
......
export default {
lang: {
shortWeekDays: ['一', '二', '三', '四', '五', '六', '日'],
shortMonths: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
},
}
...@@ -107,6 +107,7 @@ export default { ...@@ -107,6 +107,7 @@ export default {
dynamicForm: '动态表单', dynamicForm: '动态表单',
customerForm: '自定义组件', customerForm: '自定义组件',
appendForm: '表单增删示例', appendForm: '表单增删示例',
tabsForm: '标签页+多级field',
}, },
iframe: { iframe: {
frame: '外部页面', frame: '外部页面',
...@@ -162,13 +163,9 @@ export default { ...@@ -162,13 +163,9 @@ export default {
}, },
example: { example: {
example: '示例页', example: '示例页',
api: 'API 示例', api: 'API 示例',
hideLayout: '测试 - 隐藏布局', hideLayout: '测试 - 隐藏布局',
fullScreen: '测试 - 自动全屏', fullScreen: '测试 - 自动全屏',
mapMars3d: 'Mars3d', mapMars3d: 'Mars3d',
}, },
system: { system: {
......
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
api: { api: {
operationFailed: '操作失败', operationFailed: '操作失败',
errorTip: '错误提示', errorTip: '错误提示',
successTip: '成功提示',
errorMessage: '操作失败,系统异常!', errorMessage: '操作失败,系统异常!',
timeoutMessage: '登录超时,请重新登录!', timeoutMessage: '登录超时,请重新登录!',
apiTimeoutMessage: '接口请求超时,请刷新页面重试!', apiTimeoutMessage: '接口请求超时,请刷新页面重试!',
...@@ -21,11 +22,7 @@ export default { ...@@ -21,11 +22,7 @@ export default {
errMsg504: '网络超时!', errMsg504: '网络超时!',
errMsg505: 'http版本不支持该请求!', errMsg505: 'http版本不支持该请求!',
}, },
app: { app: { logoutTip: '温馨提醒', logoutMessage: '是否确认退出系统?', menuLoading: '菜单加载中...' },
logoutTip: '温馨提醒',
logoutMessage: '是否确认退出系统?',
menuLoading: '菜单加载中...',
},
errorLog: { errorLog: {
tableTitle: '错误日志列表', tableTitle: '错误日志列表',
tableColumnType: '类型', tableColumnType: '类型',
......
...@@ -16,6 +16,12 @@ import { setupI18n } from '/@/locales/setupI18n' ...@@ -16,6 +16,12 @@ import { setupI18n } from '/@/locales/setupI18n'
import { setupAppConfig } from '/@/config/app' import { setupAppConfig } from '/@/config/app'
import { registerGlobComp } from '/@/components/registerGlobComp' import { registerGlobComp } from '/@/components/registerGlobComp'
import { isDevMode } from './utils/env'
if (isDevMode()) {
import('ant-design-vue/es/style')
}
async function bootstrap() { async function bootstrap() {
const app = createApp(App) const app = createApp(App)
...@@ -23,28 +29,37 @@ async function bootstrap() { ...@@ -23,28 +29,37 @@ async function bootstrap() {
setupAppConfig() setupAppConfig()
// Configure store // Configure store
// 配置 store
setupStore(app) setupStore(app)
// Initialize internal system configuration // Initialize internal system configuration
// 初始化内部系统配置
initAppConfigStore() initAppConfigStore()
// Register global components // Register global components
// 注册全局组件
registerGlobComp(app) registerGlobComp(app)
// Multilingual configuration // Multilingual configuration
// 多语言配置
// Asynchronous case: language files may be obtained from the server side // Asynchronous case: language files may be obtained from the server side
// 异步案例:语言文件可能从服务器端获取
await setupI18n(app) await setupI18n(app)
// Configure routing // Configure routing
// 配置路由
setupRouter(app) setupRouter(app)
// router-guard // router-guard
// 路由守卫
setupRouterGuard(router) setupRouterGuard(router)
// Register global directive // Register global directive
// 注册全局指令
setupGlobDirectives(app) setupGlobDirectives(app)
// Configure global error handling // Configure global error handling
// 配置全局错误处理
setupErrorHandle(app) setupErrorHandle(app)
// https://next.router.vuejs.org/api/#isready // https://next.router.vuejs.org/api/#isready
......
...@@ -18,7 +18,7 @@ export const getParentLayout = (_name?: string) => { ...@@ -18,7 +18,7 @@ export const getParentLayout = (_name?: string) => {
return () => return () =>
new Promise((resolve) => { new Promise((resolve) => {
resolve({ resolve({
name: PARENT_LAYOUT_NAME, name: _name || PARENT_LAYOUT_NAME,
}) })
}) })
} }
...@@ -11,14 +11,18 @@ export function getAllParentPath<T = Recordable>(treeData: T[], path: string) { ...@@ -11,14 +11,18 @@ export function getAllParentPath<T = Recordable>(treeData: T[], path: string) {
return (menuList || []).map((item) => item.path) return (menuList || []).map((item) => item.path)
} }
// 路径处理
function joinParentPath(menus: Menu[], parentPath = '') { function joinParentPath(menus: Menu[], parentPath = '') {
for (let index = 0; index < menus.length; index++) { for (let index = 0; index < menus.length; index++) {
const menu = menus[index] const menu = menus[index]
// https://next.router.vuejs.org/guide/essentials/nested-routes.html // https://next.router.vuejs.org/guide/essentials/nested-routes.html
// Note that nested paths that start with / will be treated as a root path. // Note that nested paths that start with / will be treated as a root path.
// 请注意,以 / 开头的嵌套路径将被视为根路径。
// This allows you to leverage the component nesting without having to use a nested URL. // This allows you to leverage the component nesting without having to use a nested URL.
// 这允许你利用组件嵌套,而无需使用嵌套 URL。
if (!(menu.path.startsWith('/') || isUrl(menu.path))) { if (!(menu.path.startsWith('/') || isUrl(menu.path))) {
// path doesn't start with /, nor is it a url, join parent path // path doesn't start with /, nor is it a url, join parent path
// 路径不以 / 开头,也不是 url,加入父路径
menu.path = `${parentPath}/${menu.path}` menu.path = `${parentPath}/${menu.path}`
} }
if (menu?.children?.length) { if (menu?.children?.length) {
...@@ -37,14 +41,18 @@ export function transformMenuModule(menuModule: MenuModule): Menu { ...@@ -37,14 +41,18 @@ export function transformMenuModule(menuModule: MenuModule): Menu {
return menuList[0] return menuList[0]
} }
// 将路由转换成菜单
export function transformRouteToMenu(routeModList: AppRouteModule[], routerMapping = false) { export function transformRouteToMenu(routeModList: AppRouteModule[], routerMapping = false) {
// 借助 lodash 深拷贝
const cloneRouteModList = cloneDeep(routeModList) const cloneRouteModList = cloneDeep(routeModList)
const routeList: AppRouteRecordRaw[] = [] const routeList: AppRouteRecordRaw[] = []
// 对路由项进行修改
cloneRouteModList.forEach((item) => { cloneRouteModList.forEach((item) => {
if (routerMapping && item.meta.hideChildrenInMenu && typeof item.redirect === 'string') { if (routerMapping && item.meta.hideChildrenInMenu && typeof item.redirect === 'string') {
item.path = item.redirect item.path = item.redirect
} }
if (item.meta?.single) { if (item.meta?.single) {
const realItem = item?.children?.[0] const realItem = item?.children?.[0]
realItem && routeList.push(realItem) realItem && routeList.push(realItem)
...@@ -52,6 +60,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi ...@@ -52,6 +60,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
routeList.push(item) routeList.push(item)
} }
}) })
// 提取树指定结构
const list = treeMap(routeList, { const list = treeMap(routeList, {
conversion: (node: AppRouteRecordRaw) => { conversion: (node: AppRouteRecordRaw) => {
const { meta: { title, hideMenu = false } = {} } = node const { meta: { title, hideMenu = false } = {} } = node
...@@ -66,6 +75,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi ...@@ -66,6 +75,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
} }
}, },
}) })
// 路径处理
joinParentPath(list) joinParentPath(list)
return cloneDeep(list) return cloneDeep(list)
} }
...@@ -74,6 +84,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi ...@@ -74,6 +84,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
* config menu with given params * config menu with given params
*/ */
const menuParamRegex = /(?::)([\s\S]+?)((?=\/)|$)/g const menuParamRegex = /(?::)([\s\S]+?)((?=\/)|$)/g
export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) { export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) {
const { path, paramPath } = toRaw(menu) const { path, paramPath } = toRaw(menu)
let realPath = paramPath ? paramPath : path let realPath = paramPath ? paramPath : path
......
...@@ -65,6 +65,7 @@ function dynamicImport(dynamicViewsModules: Record<string, () => Promise<Recorda ...@@ -65,6 +65,7 @@ function dynamicImport(dynamicViewsModules: Record<string, () => Promise<Recorda
} }
// Turn background objects into routing objects // Turn background objects into routing objects
// 将背景对象变成路由对象
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] { export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
routeList.forEach((route) => { routeList.forEach((route) => {
const component = route.component as string const component = route.component as string
...@@ -91,35 +92,46 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul ...@@ -91,35 +92,46 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul
/** /**
* Convert multi-level routing to level 2 routing * Convert multi-level routing to level 2 routing
* 将多级路由转换为 2 级路由
*/ */
export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) { export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) {
const modules: AppRouteModule[] = cloneDeep(routeModules) const modules: AppRouteModule[] = cloneDeep(routeModules)
for (let index = 0; index < modules.length; index++) { for (let index = 0; index < modules.length; index++) {
const routeModule = modules[index] const routeModule = modules[index]
// 判断级别是否 多级 路由
if (!isMultipleRoute(routeModule)) { if (!isMultipleRoute(routeModule)) {
// 声明终止当前循环, 即跳过此次循环,进行下一轮
continue continue
} }
// 路由等级提升
promoteRouteLevel(routeModule) promoteRouteLevel(routeModule)
} }
return modules return modules
} }
// Routing level upgrade // Routing level upgrade
// 路由等级提升
function promoteRouteLevel(routeModule: AppRouteModule) { function promoteRouteLevel(routeModule: AppRouteModule) {
// Use vue-router to splice menus // Use vue-router to splice menus
// 使用vue-router拼接菜单
// createRouter 创建一个可以被 Vue 应用程序使用的路由实例
let router: Router | null = createRouter({ let router: Router | null = createRouter({
routes: [routeModule as unknown as RouteRecordNormalized], routes: [routeModule as unknown as RouteRecordNormalized],
history: createWebHashHistory(), history: createWebHashHistory(),
}) })
// getRoutes: 获取所有 路由记录的完整列表。
const routes = router.getRoutes() const routes = router.getRoutes()
// 将所有子路由添加到二级路由
addToChildren(routes, routeModule.children || [], routeModule) addToChildren(routes, routeModule.children || [], routeModule)
router = null router = null
// omit lodash的函数 对传入的item对象的children进行删除
routeModule.children = routeModule.children?.map((item) => omit(item, 'children')) routeModule.children = routeModule.children?.map((item) => omit(item, 'children'))
} }
// Add all sub-routes to the secondary route // Add all sub-routes to the secondary route
// 将所有子路由添加到二级路由
function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecordRaw[], routeModule: AppRouteModule) { function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecordRaw[], routeModule: AppRouteModule) {
for (let index = 0; index < children.length; index++) { for (let index = 0; index < children.length; index++) {
const child = children[index] const child = children[index]
...@@ -138,7 +150,9 @@ function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecord ...@@ -138,7 +150,9 @@ function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecord
} }
// Determine whether the level exceeds 2 levels // Determine whether the level exceeds 2 levels
// 判断级别是否超过2级
function isMultipleRoute(routeModule: AppRouteModule) { function isMultipleRoute(routeModule: AppRouteModule) {
// Reflect.has 与 in 操作符 相同, 用于检查一个对象(包括它原型链上)是否拥有某个属性
if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) { if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) {
return false return false
} }
......
...@@ -14,9 +14,13 @@ const getRouteNames = (array: any[]) => ...@@ -14,9 +14,13 @@ const getRouteNames = (array: any[]) =>
getRouteNames(basicRoutes) getRouteNames(basicRoutes)
// app router // app router
// 创建一个可以被 Vue 应用程序使用的路由实例
export const router = createRouter({ export const router = createRouter({
// 创建一个 hash 历史记录。
history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH), history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH),
// 应该添加到路由的初始路由列表。
routes: basicRoutes as unknown as RouteRecordRaw[], routes: basicRoutes as unknown as RouteRecordRaw[],
// 是否应该禁止尾部斜杠。默认为假
strict: true, strict: true,
scrollBehavior: () => ({ left: 0, top: 0 }), scrollBehavior: () => ({ left: 0, top: 0 }),
}) })
...@@ -32,6 +36,7 @@ export function resetRouter() { ...@@ -32,6 +36,7 @@ export function resetRouter() {
} }
// config router // config router
// 配置路由器
export function setupRouter(app: App<Element>) { export function setupRouter(app: App<Element>) {
app.use(router) app.use(router)
} }
...@@ -2,6 +2,7 @@ import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types' ...@@ -2,6 +2,7 @@ import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types'
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic' import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic'
import { mainOutRoutes } from './mainOut'
import { PageEnum } from '/@/enums/pageEnum' import { PageEnum } from '/@/enums/pageEnum'
import { t } from '/@/hooks/web/useI18n' import { t } from '/@/hooks/web/useI18n'
...@@ -9,6 +10,7 @@ const modules = import.meta.glob('./modules/**/*.ts', { eager: true }) ...@@ -9,6 +10,7 @@ const modules = import.meta.glob('./modules/**/*.ts', { eager: true })
const routeModuleList: AppRouteModule[] = [] const routeModuleList: AppRouteModule[] = []
// 加入到路由集合中
Object.keys(modules).forEach((key) => { Object.keys(modules).forEach((key) => {
const mod = (modules as Recordable)[key].default || {} const mod = (modules as Recordable)[key].default || {}
const modList = Array.isArray(mod) ? [...mod] : [mod] const modList = Array.isArray(mod) ? [...mod] : [mod]
...@@ -17,6 +19,7 @@ Object.keys(modules).forEach((key) => { ...@@ -17,6 +19,7 @@ Object.keys(modules).forEach((key) => {
export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList] export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList]
// 根路由
export const RootRoute: AppRouteRecordRaw = { export const RootRoute: AppRouteRecordRaw = {
path: '/', path: '/',
name: 'Root', name: 'Root',
...@@ -36,4 +39,5 @@ export const LoginRoute: AppRouteRecordRaw = { ...@@ -36,4 +39,5 @@ export const LoginRoute: AppRouteRecordRaw = {
} }
// Basic routing without permission // Basic routing without permission
export const basicRoutes = [LoginRoute, RootRoute, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE] // 未经许可的基本路由
export const basicRoutes = [LoginRoute, RootRoute, ...mainOutRoutes, REDIRECT_ROUTE, PAGE_NOT_FOUND_ROUTE]
/**
The routing of this file will not show the layout.
It is an independent new page.
the contents of the file still need to log in to access
*/
import type { AppRouteModule } from '/@/router/types'
// test
// http:ip:port/main-out
export const mainOutRoutes: AppRouteModule[] = [
{
path: '/main-out',
name: 'MainOut',
component: () => import('/@/views/demo/main-out/index.vue'),
meta: {
title: 'MainOut',
ignoreAuth: true,
},
},
]
export const mainOutRouteNames = mainOutRoutes.map((item) => item.name)
...@@ -3,7 +3,7 @@ import type { AppRouteModule } from '/@/router/types' ...@@ -3,7 +3,7 @@ import type { AppRouteModule } from '/@/router/types'
import { LAYOUT } from '/@/router/constant' import { LAYOUT } from '/@/router/constant'
import { t } from '/@/hooks/web/useI18n' import { t } from '/@/hooks/web/useI18n'
const dashboard: AppRouteModule = { const about: AppRouteModule = {
path: '/about', path: '/about',
name: 'About', name: 'About',
component: LAYOUT, component: LAYOUT,
...@@ -28,4 +28,4 @@ const dashboard: AppRouteModule = { ...@@ -28,4 +28,4 @@ const dashboard: AppRouteModule = {
], ],
} }
export default dashboard export default about
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论