diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index 270ebb8c1c629ae621bb4798aee73e909f52d64c..0000000000000000000000000000000000000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,6 +0,0 @@ -echo Start running commit-msg hook... - -# Check whether the git commit information is standardized -pnpm exec commitlint --edit "$1" - -echo Run commit-msg hook done. diff --git a/.husky/post-merge b/.husky/post-merge deleted file mode 100644 index 83fa775d585b8d08eca0600d1e479e74ed41ebfc..0000000000000000000000000000000000000000 --- a/.husky/post-merge +++ /dev/null @@ -1,3 +0,0 @@ -# 每次 git pull 之后, 安装依赖 - -pnpm install diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 5dccee288f3efc02a868643d736ff0e94c821dcf..0000000000000000000000000000000000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,7 +0,0 @@ -# update `.vscode/vben-admin.code-workspace` file -pnpm vsh code-workspace --auto-commit - -# Format and submit code according to lintstagedrc.js configuration -pnpm exec lint-staged - -echo Run pre-commit hook done. diff --git a/.image/common/ai-feature.png b/.image/common/ai-feature.png index 552ed59b424610bf8438003a1c839e197bda7eed..e659dd1a28a6ae5f6cad0483a36189034ea4fe3f 100644 Binary files a/.image/common/ai-feature.png and b/.image/common/ai-feature.png differ diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs deleted file mode 100644 index 2a5a5a1ae950e42e65467e293d4ee7c0de33c53c..0000000000000000000000000000000000000000 --- a/.lintstagedrc.mjs +++ /dev/null @@ -1,20 +0,0 @@ -export default { - '*.md': ['prettier --cache --ignore-unknown --write'], - '*.vue': [ - 'prettier --write', - 'eslint --cache --fix', - 'stylelint --fix --allow-empty-input', - ], - '*.{js,jsx,ts,tsx}': [ - 'prettier --cache --ignore-unknown --write', - 'eslint --cache --fix', - ], - '*.{scss,less,styl,html,vue,css}': [ - 'prettier --cache --ignore-unknown --write', - 'stylelint --fix --allow-empty-input', - ], - 'package.json': ['prettier --cache --write'], - '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [ - 'prettier --cache --write --parser json', - ], -}; diff --git a/.npmrc b/.npmrc index f4a1ad483b0de3ccae7f1cfa7506ed2140d6596e..21147aff25fd35e02d642c56f4aa01831e5ea8db 100644 --- a/.npmrc +++ b/.npmrc @@ -1,5 +1,5 @@ registry = "https://registry.npmmirror.com" -public-hoist-pattern[]=husky +public-hoist-pattern[]=lefthook public-hoist-pattern[]=eslint public-hoist-pattern[]=prettier public-hoist-pattern[]=prettier-plugin-tailwindcss diff --git a/.vscode/settings.json b/.vscode/settings.json index 46d853ffe84abbd833b1908565338dea3043e335..7f3c7a4f486e16fac24aee8619e9a57d5fdf8c9c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -219,7 +219,7 @@ "*.env": "$(capture).env.*", "README.md": "README*,CHANGELOG*,LICENSE,CNAME", "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", - "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json", + "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml", "tailwind.config.mjs": "postcss.*" }, "commentTranslate.hover.enabled": false, diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 2b73ff29675951d9add0b9b45fec90cf30a83af8..44f867d22883dd86b96bd79f48aea87838943093 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -259,6 +259,7 @@ setupVbenVxeTable({ // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 // vxeUI.formats.add + // add by 星语:数量格式化,例如说:金额 vxeUI.formats.add('formatAmount', { cellFormatMethod({ cellValue }, digits = 2) { if (cellValue === null || cellValue === undefined) { diff --git a/apps/web-antd/src/api/bpm/category/index.ts b/apps/web-antd/src/api/bpm/category/index.ts index 1519c758c01e2e1fcf4cc44ac564c2377b6f1afc..0a558f6daf687fc6e36f9918c1f61ac40c21387a 100644 --- a/apps/web-antd/src/api/bpm/category/index.ts +++ b/apps/web-antd/src/api/bpm/category/index.ts @@ -15,6 +15,7 @@ export namespace BpmCategoryApi { } /** 模型分类信息 */ + // TODO @jason:这个应该非 api 的,可以考虑抽到页面里哈。 export interface ModelCategoryInfo { id: number; name: string; diff --git a/apps/web-antd/src/api/bpm/model/index.ts b/apps/web-antd/src/api/bpm/model/index.ts index 06c444e044c8cc84661cf4c0fa8ad2f581d3b4c5..72ae8e9f94640eef5a3cc3d8462e3dd6dbc90980 100644 --- a/apps/web-antd/src/api/bpm/model/index.ts +++ b/apps/web-antd/src/api/bpm/model/index.ts @@ -2,6 +2,7 @@ import { requestClient } from '#/api/request'; export namespace BpmModelApi { /** 用户信息 TODO 这个是不是可以抽取出来定义在公共模块 */ + // TODO @芋艿:一起看看。 export interface UserInfo { id: number; nickname: string; @@ -9,6 +10,7 @@ export namespace BpmModelApi { deptId?: number; deptName?: string; } + /** 流程定义 VO */ export interface ProcessDefinitionVO { id: string; diff --git a/apps/web-antd/src/api/infra/demo/demo01/index.ts b/apps/web-antd/src/api/infra/demo/demo01/index.ts index b03a2aef041c32823039cd9a71933e3b818ad8a5..5a940a61db045de58d26dff8309c554c6f1f2b85 100644 --- a/apps/web-antd/src/api/infra/demo/demo01/index.ts +++ b/apps/web-antd/src/api/infra/demo/demo01/index.ts @@ -1,3 +1,5 @@ +import type { Dayjs } from 'dayjs'; + import type { PageParam, PageResult } from '@vben/request'; import { requestClient } from '#/api/request'; @@ -7,8 +9,8 @@ export namespace Demo01ContactApi { export interface Demo01Contact { id: number; // 编号 name?: string; // 名字 - sex?: number; // 性别 - birthday?: Date; // 出生年 + sex?: boolean; // 性别 + birthday?: Dayjs | string; // 出生年 description?: string; // 简介 avatar: string; // 头像 } diff --git a/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts b/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts index 02183af11839201e7910acddba0cb20d96973f7e..f9704bf1459433efbcf1a0859dca04eb24c89bab 100644 --- a/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts +++ b/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts @@ -1,3 +1,5 @@ +import type { Dayjs } from 'dayjs'; + import type { PageParam, PageResult } from '@vben/request'; import { requestClient } from '#/api/request'; @@ -24,7 +26,7 @@ export namespace Demo03StudentApi { id: number; // 编号 name?: string; // 名字 sex?: number; // 性别 - birthday?: Date; // 出生日期 + birthday?: Dayjs | string; // 出生日期 description?: string; // 简介 } } diff --git a/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts b/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts index a83cf4215c52a9d59d67a7292c40f103889ff764..a04a919e95d166998dbc3c7432e21edd66a06389 100644 --- a/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts +++ b/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts @@ -1,3 +1,5 @@ +import type { Dayjs } from 'dayjs'; + import type { PageParam, PageResult } from '@vben/request'; import { requestClient } from '#/api/request'; @@ -24,7 +26,7 @@ export namespace Demo03StudentApi { id: number; // 编号 name?: string; // 名字 sex?: number; // 性别 - birthday?: Date; // 出生日期 + birthday?: Dayjs | string; // 出生日期 description?: string; // 简介 demo03courses?: Demo03Course[]; demo03grade?: Demo03Grade; diff --git a/apps/web-antd/src/api/infra/file/index.ts b/apps/web-antd/src/api/infra/file/index.ts index 5f352432f9725a24d2e9d3d6407b47a21ee37a06..a399db67d799f10f3a648a8e6fef8c54208145c1 100644 --- a/apps/web-antd/src/api/infra/file/index.ts +++ b/apps/web-antd/src/api/infra/file/index.ts @@ -23,12 +23,13 @@ export namespace InfraFileApi { configId: number; // 文件配置编号 uploadUrl: string; // 文件上传 URL url: string; // 文件 URL + path: string; // 文件路径 } /** 上传文件 */ export interface FileUploadReqVO { file: globalThis.File; - path?: string; + directory?: string; } } @@ -45,11 +46,11 @@ export function deleteFile(id: number) { } /** 获取文件预签名地址 */ -export function getFilePresignedUrl(path: string) { +export function getFilePresignedUrl(name: string, directory?: string) { return requestClient.get( '/infra/file/presigned-url', { - params: { path }, + params: { name, directory }, }, ); } @@ -64,5 +65,9 @@ export function uploadFile( data: InfraFileApi.FileUploadReqVO, onUploadProgress?: AxiosProgressEvent, ) { + // 特殊:由于 upload 内部封装,即使 directory 为 undefined,也会传递给后端 + if (!data.directory) { + delete data.directory; + } return requestClient.upload('/infra/file/upload', data, { onUploadProgress }); } diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 5b5c15fb75135fc61b0da1e10dc9d4ab089eb42a..7f78987b4b80f25bdcc6de5863374f7399da2300 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -80,6 +80,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { config.headers['tenant-id'] = tenantEnable ? accessStore.tenantId : undefined; + // 只有登录时,才设置 visit-tenant-id 访问租户 + config.headers['visit-tenant-id'] = tenantEnable + ? accessStore.visitTenantId + : undefined; return config; }, }); @@ -136,6 +140,10 @@ baseRequestClient.addRequestInterceptor({ config.headers['tenant-id'] = tenantEnable ? accessStore.tenantId : undefined; + // 只有登录时,才设置 visit-tenant-id 访问租户 + config.headers['visit-tenant-id'] = tenantEnable + ? accessStore.visitTenantId + : undefined; return config; }, }); diff --git a/apps/web-antd/src/api/system/tenant/index.ts b/apps/web-antd/src/api/system/tenant/index.ts index cefacc51124b88f0d07a4c8b1aaccc85d9a70c46..3bed9249ccd3916788868b65b38d604de0e608c1 100644 --- a/apps/web-antd/src/api/system/tenant/index.ts +++ b/apps/web-antd/src/api/system/tenant/index.ts @@ -39,6 +39,13 @@ export function getTenant(id: number) { ); } +/** 获取租户精简信息列表 */ +export function getTenantList() { + return requestClient.get( + '/system/tenant/simple-list', + ); +} + /** 新增租户 */ export function createTenant(data: SystemTenantApi.Tenant) { return requestClient.post('/system/tenant/create', data); diff --git a/apps/web-antd/src/components/content-wrap/content-wrap.vue b/apps/web-antd/src/components/content-wrap/content-wrap.vue index 306e0fa6d5233705abd97836bac567e3ac91e07d..761a312ea8996a34039ead84ed88df02cb8bae82 100644 --- a/apps/web-antd/src/components/content-wrap/content-wrap.vue +++ b/apps/web-antd/src/components/content-wrap/content-wrap.vue @@ -5,25 +5,45 @@ diff --git a/apps/web-antd/src/components/table-toolbar/index.ts b/apps/web-antd/src/components/table-toolbar/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..720e3224ba00d2cbecf673da357f08f7aa199e82 --- /dev/null +++ b/apps/web-antd/src/components/table-toolbar/index.ts @@ -0,0 +1 @@ +export { default as TableToolbar } from './table-toolbar.vue'; diff --git a/apps/web-antd/src/components/table-toolbar/table-toolbar.vue b/apps/web-antd/src/components/table-toolbar/table-toolbar.vue new file mode 100644 index 0000000000000000000000000000000000000000..099d3a8c94514dfb0efa6057c1e4f02608c62f1c --- /dev/null +++ b/apps/web-antd/src/components/table-toolbar/table-toolbar.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web-antd/src/components/upload/file-upload.vue b/apps/web-antd/src/components/upload/file-upload.vue index 91d7cc669afc7cfd265af4790ae2ce07b46d9008..3fbb727b6db4a094470bf29b10769161fdb9af25 100644 --- a/apps/web-antd/src/components/upload/file-upload.vue +++ b/apps/web-antd/src/components/upload/file-upload.vue @@ -28,6 +28,8 @@ const props = withDefaults( file: File, onUploadProgress?: AxiosProgressEvent, ) => Promise>; + // 上传的目录 + directory?: string; disabled?: boolean; helpText?: string; // 最大数量的文件,Infinity不限制 @@ -44,13 +46,14 @@ const props = withDefaults( }>(), { value: () => [], + directory: undefined, disabled: false, helpText: '', maxSize: 2, maxNumber: 1, accept: () => [], multiple: false, - api: useUpload().httpRequest, + api: undefined, resultField: '', showDescription: false, }, @@ -141,10 +144,9 @@ const beforeUpload = async (file: File) => { }; async function customRequest(info: UploadRequestOption) { - const { api } = props; + let { api } = props; if (!api || !isFunction(api)) { - console.warn('upload api must exist and be a function'); - return; + api = useUpload(props.directory).httpRequest; } try { // 上传文件 diff --git a/apps/web-antd/src/components/upload/image-upload.vue b/apps/web-antd/src/components/upload/image-upload.vue index 412d93560eac136e00a65ad436ffc13aa211c720..10da908610ee9fa883a1a59952c98469db7b8f01 100644 --- a/apps/web-antd/src/components/upload/image-upload.vue +++ b/apps/web-antd/src/components/upload/image-upload.vue @@ -30,6 +30,8 @@ const props = withDefaults( file: File, onUploadProgress?: AxiosProgressEvent, ) => Promise>; + // 上传的目录 + directory?: string; disabled?: boolean; helpText?: string; listType?: UploadListType; @@ -47,6 +49,7 @@ const props = withDefaults( }>(), { value: () => [], + directory: undefined, disabled: false, listType: 'picture-card', helpText: '', @@ -54,7 +57,7 @@ const props = withDefaults( maxNumber: 1, accept: () => defaultImageAccepts, multiple: false, - api: useUpload().httpRequest, + api: undefined, resultField: '', showDescription: true, }, @@ -177,10 +180,9 @@ const beforeUpload = async (file: File) => { }; async function customRequest(info: UploadRequestOption) { - const { api } = props; + let { api } = props; if (!api || !isFunction(api)) { - console.warn('upload api must exist and be a function'); - return; + api = useUpload(props.directory).httpRequest; } try { // 上传文件 diff --git a/apps/web-antd/src/components/upload/use-upload.ts b/apps/web-antd/src/components/upload/use-upload.ts index 94e238d1ca0b62daaba551bfa17ea81735e8c9c7..572ae5c4a6ab9e8b9d236ad6c1f933b127d445bd 100644 --- a/apps/web-antd/src/components/upload/use-upload.ts +++ b/apps/web-antd/src/components/upload/use-upload.ts @@ -7,8 +7,7 @@ import { computed, unref } from 'vue'; import { useAppConfig } from '@vben/hooks'; import { $t } from '@vben/locales'; -import CryptoJS from 'crypto-js'; - +// import CryptoJS from 'crypto-js'; import { createFile, getFilePresignedUrl, uploadFile } from '#/api/infra/file'; import { baseRequestClient } from '#/api/request'; @@ -81,7 +80,7 @@ export function useUploadType({ } // TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构 -export const useUpload = () => { +export const useUpload = (directory?: string) => { // 后端上传地址 const uploadUrl = getUploadUrl(); // 是否使用前端直连上传 @@ -97,7 +96,7 @@ export const useUpload = () => { // 1.1 生成文件名称 const fileName = await generateFileName(file); // 1.2 获取文件预签名地址 - const presignedInfo = await getFilePresignedUrl(fileName); + const presignedInfo = await getFilePresignedUrl(fileName, directory); // 1.3 上传文件 return baseRequestClient .put(presignedInfo.uploadUrl, file, { @@ -107,13 +106,13 @@ export const useUpload = () => { }) .then(() => { // 1.4. 记录文件信息到后端(异步) - createFile0(presignedInfo, fileName, file); + createFile0(presignedInfo, file); // 通知成功,数据格式保持与后端上传的返回结果一致 - return { data: presignedInfo.url }; + return { url: presignedInfo.url }; }); } else { // 模式二:后端上传 - return uploadFile({ file }, onUploadProgress); + return uploadFile({ file, directory }, onUploadProgress); } }; @@ -134,18 +133,13 @@ export const getUploadUrl = (): string => { * 创建文件信息 * * @param vo 文件预签名信息 - * @param name 文件名称 * @param file 文件 */ -function createFile0( - vo: InfraFileApi.FilePresignedUrlRespVO, - name: string, - file: File, -) { +function createFile0(vo: InfraFileApi.FilePresignedUrlRespVO, file: File) { const fileVO = { configId: vo.configId, url: vo.url, - path: name, + path: vo.path, name: file.name, type: file.type, size: file.size, @@ -160,12 +154,13 @@ function createFile0( * @param file 要上传的文件 */ async function generateFileName(file: File) { - // 读取文件内容 - const data = await file.arrayBuffer(); - const wordArray = CryptoJS.lib.WordArray.create(data); - // 计算SHA256 - const sha256 = CryptoJS.SHA256(wordArray).toString(); - // 拼接后缀 - const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.'))); - return `${sha256}${ext}`; + // // 读取文件内容 + // const data = await file.arrayBuffer(); + // const wordArray = CryptoJS.lib.WordArray.create(data); + // // 计算SHA256 + // const sha256 = CryptoJS.SHA256(wordArray).toString(); + // // 拼接后缀 + // const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.'))); + // return `${sha256}${ext}`; + return file.name; } diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index 86f4446618a053ec7fbae46ae1c611e3fa1cc298..2777f4a3aaaaaece37c4bd4568a252a20e9baa11 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -34,6 +34,7 @@ import { useAuthStore } from '#/store'; import LoginForm from '#/views/_core/authentication/login.vue'; import Help from './components/help.vue'; +import TenantDropdown from './components/tenant-dropdown.vue'; const userStore = useUserStore(); const authStore = useAuthStore(); @@ -202,6 +203,9 @@ watch( @read="handleNotificationRead" /> +