diff --git a/.vscode/settings.json b/.vscode/settings.json
index f1a0e9db..9a2b0e6a 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -220,5 +220,6 @@
"i18n-ally.keystyle": "nested",
"commentTranslate.multiLineMerge": true,
"vue.server.hybridMode": true,
- "typescript.tsdk": "node_modules/typescript/lib"
+ "typescript.tsdk": "node_modules/typescript/lib",
+ "svn.ignoreMissingSvnWarning": true
}
diff --git a/apps/backend-mock/.env b/apps/backend-mock/.env
new file mode 100644
index 00000000..b20c4a65
--- /dev/null
+++ b/apps/backend-mock/.env
@@ -0,0 +1,3 @@
+PORT=5320
+ACCESS_TOKEN_SECRET=access_token_secret
+REFRESH_TOKEN_SECRET=refresh_token_secret
diff --git a/apps/backend-mock/README.md b/apps/backend-mock/README.md
new file mode 100644
index 00000000..401bda76
--- /dev/null
+++ b/apps/backend-mock/README.md
@@ -0,0 +1,15 @@
+# @vben/backend-mock
+
+## Description
+
+Vben Admin 数据 mock 服务,没有对接任何的数据库,所有数据都是模拟的,用于前端开发时提供数据支持。线上环境不再提供 mock 集成,可自行部署服务或者对接真实数据,由于 `mock.js` 等工具有一些限制,比如上传文件不行、无法模拟复杂的逻辑等,所以这里使用了真实的后端服务来实现。唯一麻烦的是本地需要同时启动后端服务和前端服务,但是这样可以更好的模拟真实环境。该服务不需要手动启动,已经集成在 vite 插件内,随应用一起启用。
+
+## Running the app
+
+```bash
+# development
+$ pnpm run start
+
+# production mode
+$ pnpm run build
+```
diff --git a/apps/backend-mock/api/auth/codes.ts b/apps/backend-mock/api/auth/codes.ts
new file mode 100644
index 00000000..7ba01270
--- /dev/null
+++ b/apps/backend-mock/api/auth/codes.ts
@@ -0,0 +1,14 @@
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse } from '~/utils/response';
+
+export default eventHandler((event) => {
+ const userinfo = verifyAccessToken(event);
+ if (!userinfo) {
+ return unAuthorizedResponse(event);
+ }
+
+ const codes =
+ MOCK_CODES.find((item) => item.username === userinfo.username)?.codes ?? [];
+
+ return useResponseSuccess(codes);
+});
diff --git a/apps/backend-mock/api/auth/login.post.ts b/apps/backend-mock/api/auth/login.post.ts
new file mode 100644
index 00000000..e002c97f
--- /dev/null
+++ b/apps/backend-mock/api/auth/login.post.ts
@@ -0,0 +1,36 @@
+import {
+ clearRefreshTokenCookie,
+ setRefreshTokenCookie,
+} from '~/utils/cookie-utils';
+import { generateAccessToken, generateRefreshToken } from '~/utils/jwt-utils';
+import { forbiddenResponse } from '~/utils/response';
+
+export default defineEventHandler(async (event) => {
+ const { password, username } = await readBody(event);
+ if (!password || !username) {
+ setResponseStatus(event, 400);
+ return useResponseError(
+ 'BadRequestException',
+ 'Username and password are required',
+ );
+ }
+
+ const findUser = MOCK_USERS.find(
+ (item) => item.username === username && item.password === password,
+ );
+
+ if (!findUser) {
+ clearRefreshTokenCookie(event);
+ return forbiddenResponse(event);
+ }
+
+ const accessToken = generateAccessToken(findUser);
+ const refreshToken = generateRefreshToken(findUser);
+
+ setRefreshTokenCookie(event, refreshToken);
+
+ return useResponseSuccess({
+ ...findUser,
+ accessToken,
+ });
+});
diff --git a/apps/backend-mock/api/auth/logout.post.ts b/apps/backend-mock/api/auth/logout.post.ts
new file mode 100644
index 00000000..ac6afe94
--- /dev/null
+++ b/apps/backend-mock/api/auth/logout.post.ts
@@ -0,0 +1,15 @@
+import {
+ clearRefreshTokenCookie,
+ getRefreshTokenFromCookie,
+} from '~/utils/cookie-utils';
+
+export default defineEventHandler(async (event) => {
+ const refreshToken = getRefreshTokenFromCookie(event);
+ if (!refreshToken) {
+ return useResponseSuccess('');
+ }
+
+ clearRefreshTokenCookie(event);
+
+ return useResponseSuccess('');
+});
diff --git a/apps/backend-mock/api/auth/refresh.post.ts b/apps/backend-mock/api/auth/refresh.post.ts
new file mode 100644
index 00000000..7df4d34f
--- /dev/null
+++ b/apps/backend-mock/api/auth/refresh.post.ts
@@ -0,0 +1,33 @@
+import {
+ clearRefreshTokenCookie,
+ getRefreshTokenFromCookie,
+ setRefreshTokenCookie,
+} from '~/utils/cookie-utils';
+import { verifyRefreshToken } from '~/utils/jwt-utils';
+import { forbiddenResponse } from '~/utils/response';
+
+export default defineEventHandler(async (event) => {
+ const refreshToken = getRefreshTokenFromCookie(event);
+ if (!refreshToken) {
+ return forbiddenResponse(event);
+ }
+
+ clearRefreshTokenCookie(event);
+
+ const userinfo = verifyRefreshToken(refreshToken);
+ if (!userinfo) {
+ return forbiddenResponse(event);
+ }
+
+ const findUser = MOCK_USERS.find(
+ (item) => item.username === userinfo.username,
+ );
+ if (!findUser) {
+ return forbiddenResponse(event);
+ }
+ const accessToken = generateAccessToken(findUser);
+
+ setRefreshTokenCookie(event, refreshToken);
+
+ return accessToken;
+});
diff --git a/apps/backend-mock/api/menu/all.ts b/apps/backend-mock/api/menu/all.ts
new file mode 100644
index 00000000..b27b7ea4
--- /dev/null
+++ b/apps/backend-mock/api/menu/all.ts
@@ -0,0 +1,13 @@
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse } from '~/utils/response';
+
+export default eventHandler((event) => {
+ const userinfo = verifyAccessToken(event);
+ if (!userinfo) {
+ return unAuthorizedResponse(event);
+ }
+
+ const menus =
+ MOCK_MENUS.find((item) => item.username === userinfo.username)?.menus ?? [];
+ return useResponseSuccess(menus);
+});
diff --git a/apps/backend-mock/api/status.ts b/apps/backend-mock/api/status.ts
new file mode 100644
index 00000000..41773e1d
--- /dev/null
+++ b/apps/backend-mock/api/status.ts
@@ -0,0 +1,5 @@
+export default eventHandler((event) => {
+ const { status } = getQuery(event);
+ setResponseStatus(event, Number(status));
+ return useResponseError(`${status}`);
+});
diff --git a/apps/backend-mock/api/test.get.ts b/apps/backend-mock/api/test.get.ts
new file mode 100644
index 00000000..ca4a500b
--- /dev/null
+++ b/apps/backend-mock/api/test.get.ts
@@ -0,0 +1 @@
+export default defineEventHandler(() => 'Test get handler');
diff --git a/apps/backend-mock/api/test.post.ts b/apps/backend-mock/api/test.post.ts
new file mode 100644
index 00000000..698cf211
--- /dev/null
+++ b/apps/backend-mock/api/test.post.ts
@@ -0,0 +1 @@
+export default defineEventHandler(() => 'Test post handler');
diff --git a/apps/backend-mock/api/user/info.ts b/apps/backend-mock/api/user/info.ts
new file mode 100644
index 00000000..e3526ae5
--- /dev/null
+++ b/apps/backend-mock/api/user/info.ts
@@ -0,0 +1,11 @@
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse } from '~/utils/response';
+
+export default eventHandler((event) => {
+ const userinfo = verifyAccessToken(event);
+ if (!userinfo) {
+ return unAuthorizedResponse(event);
+ }
+
+ return useResponseSuccess(userinfo);
+});
diff --git a/apps/backend-mock/error.ts b/apps/backend-mock/error.ts
new file mode 100644
index 00000000..e20beac4
--- /dev/null
+++ b/apps/backend-mock/error.ts
@@ -0,0 +1,7 @@
+import type { NitroErrorHandler } from 'nitropack';
+
+const errorHandler: NitroErrorHandler = function (error, event) {
+ event.node.res.end(`[Error Handler] ${error.stack}`);
+};
+
+export default errorHandler;
diff --git a/apps/backend-mock/middleware/1.api.ts b/apps/backend-mock/middleware/1.api.ts
new file mode 100644
index 00000000..84e2ce0e
--- /dev/null
+++ b/apps/backend-mock/middleware/1.api.ts
@@ -0,0 +1,7 @@
+export default defineEventHandler((event) => {
+ if (event.method === 'OPTIONS') {
+ event.node.res.statusCode = 204;
+ event.node.res.statusMessage = 'No Content.';
+ return 'OK';
+ }
+});
diff --git a/apps/backend-mock/nitro.config.ts b/apps/backend-mock/nitro.config.ts
new file mode 100644
index 00000000..b3013298
--- /dev/null
+++ b/apps/backend-mock/nitro.config.ts
@@ -0,0 +1,18 @@
+import errorHandler from './error';
+
+export default defineNitroConfig({
+ devErrorHandler: errorHandler,
+ errorHandler: '~/error',
+ routeRules: {
+ '/api/**': {
+ cors: true,
+ headers: {
+ 'Access-Control-Allow-Credentials': 'true',
+ 'Access-Control-Allow-Headers': '*',
+ 'Access-Control-Allow-Methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Expose-Headers': '*',
+ },
+ },
+ },
+});
diff --git a/apps/backend-mock/package.json b/apps/backend-mock/package.json
new file mode 100644
index 00000000..b74013d6
--- /dev/null
+++ b/apps/backend-mock/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "@vben/backend-mock",
+ "version": "0.0.1",
+ "description": "",
+ "private": true,
+ "license": "MIT",
+ "author": "",
+ "scripts": {
+ "build": "nitro build",
+ "start": "nitro dev"
+ },
+ "dependencies": {
+ "jsonwebtoken": "^9.0.2",
+ "nitropack": "^2.9.7"
+ },
+ "devDependencies": {
+ "@types/jsonwebtoken": "^9.0.7",
+ "h3": "^1.12.0"
+ }
+}
diff --git a/apps/backend-mock/routes/[...].ts b/apps/backend-mock/routes/[...].ts
new file mode 100644
index 00000000..70c5f7c7
--- /dev/null
+++ b/apps/backend-mock/routes/[...].ts
@@ -0,0 +1,12 @@
+export default defineEventHandler(() => {
+ return `
+
Hello Vben Admin
+Mock service is starting
+
+`;
+});
diff --git a/apps/backend-mock/tsconfig.build.json b/apps/backend-mock/tsconfig.build.json
new file mode 100644
index 00000000..64f86c6b
--- /dev/null
+++ b/apps/backend-mock/tsconfig.build.json
@@ -0,0 +1,4 @@
+{
+ "extends": "./tsconfig.json",
+ "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
+}
diff --git a/apps/backend-mock/tsconfig.json b/apps/backend-mock/tsconfig.json
new file mode 100644
index 00000000..43008af1
--- /dev/null
+++ b/apps/backend-mock/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "./.nitro/types/tsconfig.json"
+}
diff --git a/apps/backend-mock/utils/cookie-utils.ts b/apps/backend-mock/utils/cookie-utils.ts
new file mode 100644
index 00000000..0d92f577
--- /dev/null
+++ b/apps/backend-mock/utils/cookie-utils.ts
@@ -0,0 +1,26 @@
+import type { EventHandlerRequest, H3Event } from 'h3';
+
+export function clearRefreshTokenCookie(event: H3Event) {
+ deleteCookie(event, 'jwt', {
+ httpOnly: true,
+ sameSite: 'none',
+ secure: true,
+ });
+}
+
+export function setRefreshTokenCookie(
+ event: H3Event,
+ refreshToken: string,
+) {
+ setCookie(event, 'jwt', refreshToken, {
+ httpOnly: true,
+ maxAge: 24 * 60 * 60 * 1000,
+ sameSite: 'none',
+ secure: true,
+ });
+}
+
+export function getRefreshTokenFromCookie(event: H3Event) {
+ const refreshToken = getCookie(event, 'jwt');
+ return refreshToken;
+}
diff --git a/apps/backend-mock/utils/jwt-utils.ts b/apps/backend-mock/utils/jwt-utils.ts
new file mode 100644
index 00000000..8cfc6843
--- /dev/null
+++ b/apps/backend-mock/utils/jwt-utils.ts
@@ -0,0 +1,59 @@
+import type { EventHandlerRequest, H3Event } from 'h3';
+
+import jwt from 'jsonwebtoken';
+
+import { UserInfo } from './mock-data';
+
+// TODO: Replace with your own secret key
+const ACCESS_TOKEN_SECRET = 'access_token_secret';
+const REFRESH_TOKEN_SECRET = 'refresh_token_secret';
+
+export interface UserPayload extends UserInfo {
+ iat: number;
+ exp: number;
+}
+
+export function generateAccessToken(user: UserInfo) {
+ return jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '7d' });
+}
+
+export function generateRefreshToken(user: UserInfo) {
+ return jwt.sign(user, REFRESH_TOKEN_SECRET, {
+ expiresIn: '30d',
+ });
+}
+
+export function verifyAccessToken(
+ event: H3Event,
+): null | Omit {
+ const authHeader = getHeader(event, 'Authorization');
+ if (!authHeader?.startsWith('Bearer')) {
+ return null;
+ }
+
+ const token = authHeader.split(' ')[1];
+ try {
+ const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET) as UserPayload;
+
+ const username = decoded.username;
+ const user = MOCK_USERS.find((item) => item.username === username);
+ const { password: _pwd, ...userinfo } = user;
+ return userinfo;
+ } catch {
+ return null;
+ }
+}
+
+export function verifyRefreshToken(
+ token: string,
+): null | Omit {
+ try {
+ const decoded = jwt.verify(token, REFRESH_TOKEN_SECRET) as UserPayload;
+ const username = decoded.username;
+ const user = MOCK_USERS.find((item) => item.username === username);
+ const { password: _pwd, ...userinfo } = user;
+ return userinfo;
+ } catch {
+ return null;
+ }
+}
diff --git a/apps/backend-mock/utils/mock-data.ts b/apps/backend-mock/utils/mock-data.ts
new file mode 100644
index 00000000..b0a2bc1a
--- /dev/null
+++ b/apps/backend-mock/utils/mock-data.ts
@@ -0,0 +1,186 @@
+export interface UserInfo {
+ id: number;
+ password: string;
+ realName: string;
+ roles: string[];
+ username: string;
+}
+
+export const MOCK_USERS: UserInfo[] = [
+ {
+ id: 0,
+ password: '123456',
+ realName: 'Vben',
+ roles: ['super'],
+ username: 'vben',
+ },
+ {
+ id: 1,
+ password: '123456',
+ realName: 'Admin',
+ roles: ['admin'],
+ username: 'admin',
+ },
+ {
+ id: 2,
+ password: '123456',
+ realName: 'Jack',
+ roles: ['user'],
+ username: 'jack',
+ },
+];
+
+export const MOCK_CODES = [
+ // super
+ {
+ codes: ['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010'],
+ username: 'vben',
+ },
+ {
+ // admin
+ codes: ['AC_100010', 'AC_100020', 'AC_100030'],
+ username: 'admin',
+ },
+ {
+ // user
+ codes: ['AC_1000001', 'AC_1000002'],
+ username: 'jack',
+ },
+];
+
+const dashboardMenus = [
+ {
+ component: 'BasicLayout',
+ meta: {
+ order: -1,
+ title: 'page.dashboard.title',
+ },
+ name: 'Dashboard',
+ path: '/',
+ redirect: '/analytics',
+ children: [
+ {
+ name: 'Analytics',
+ path: '/analytics',
+ component: '/dashboard/analytics/index',
+ meta: {
+ affixTab: true,
+ title: 'page.dashboard.analytics',
+ },
+ },
+ {
+ name: 'Workspace',
+ path: '/workspace',
+ component: '/dashboard/workspace/index',
+ meta: {
+ title: 'page.dashboard.workspace',
+ },
+ },
+ ],
+ },
+];
+
+const createDemosMenus = (role: 'admin' | 'super' | 'user') => {
+ const roleWithMenus = {
+ admin: {
+ component: '/demos/access/admin-visible',
+ meta: {
+ icon: 'mdi:button-cursor',
+ title: 'page.demos.access.adminVisible',
+ },
+ name: 'AccessAdminVisibleDemo',
+ path: '/demos/access/admin-visible',
+ },
+ super: {
+ component: '/demos/access/super-visible',
+ meta: {
+ icon: 'mdi:button-cursor',
+ title: 'page.demos.access.superVisible',
+ },
+ name: 'AccessSuperVisibleDemo',
+ path: '/demos/access/super-visible',
+ },
+ user: {
+ component: '/demos/access/user-visible',
+ meta: {
+ icon: 'mdi:button-cursor',
+ title: 'page.demos.access.userVisible',
+ },
+ name: 'AccessUserVisibleDemo',
+ path: '/demos/access/user-visible',
+ },
+ };
+
+ return [
+ {
+ component: 'BasicLayout',
+ meta: {
+ icon: 'ic:baseline-view-in-ar',
+ keepAlive: true,
+ order: 1000,
+ title: 'page.demos.title',
+ },
+ name: 'Demos',
+ path: '/demos',
+ redirect: '/demos/access',
+ children: [
+ {
+ name: 'AccessDemos',
+ path: '/demosaccess',
+ meta: {
+ icon: 'mdi:cloud-key-outline',
+ title: 'page.demos.access.backendPermissions',
+ },
+ redirect: '/demos/access/page-control',
+ children: [
+ {
+ name: 'AccessPageControlDemo',
+ path: '/demos/access/page-control',
+ component: '/demos/access/index',
+ meta: {
+ icon: 'mdi:page-previous-outline',
+ title: 'page.demos.access.pageAccess',
+ },
+ },
+ {
+ name: 'AccessButtonControlDemo',
+ path: '/demos/access/button-control',
+ component: '/demos/access/button-control',
+ meta: {
+ icon: 'mdi:button-cursor',
+ title: 'page.demos.access.buttonControl',
+ },
+ },
+ {
+ name: 'AccessMenuVisible403Demo',
+ path: '/demos/access/menu-visible-403',
+ component: '/demos/access/menu-visible-403',
+ meta: {
+ authority: ['no-body'],
+ icon: 'mdi:button-cursor',
+ menuVisibleWithForbidden: true,
+ title: 'page.demos.access.menuVisible403',
+ },
+ },
+ roleWithMenus[role],
+ ],
+ },
+ ],
+ },
+ ];
+};
+
+export const MOCK_MENUS = [
+ {
+ menus: [...dashboardMenus, ...createDemosMenus('super')],
+ username: 'vben',
+ },
+ {
+ menus: [...dashboardMenus, ...createDemosMenus('admin')],
+ username: 'admin',
+ },
+ {
+ menus: [...dashboardMenus, ...createDemosMenus('user')],
+ username: 'jack',
+ },
+];
diff --git a/apps/backend-mock/utils/response.ts b/apps/backend-mock/utils/response.ts
new file mode 100644
index 00000000..dea14724
--- /dev/null
+++ b/apps/backend-mock/utils/response.ts
@@ -0,0 +1,29 @@
+import type { EventHandlerRequest, H3Event } from 'h3';
+
+export function useResponseSuccess(data: T) {
+ return {
+ code: 0,
+ data,
+ error: null,
+ message: 'ok',
+ };
+}
+
+export function useResponseError(message: string, error: any = null) {
+ return {
+ code: -1,
+ data: null,
+ error,
+ message,
+ };
+}
+
+export function forbiddenResponse(event: H3Event) {
+ setResponseStatus(event, 403);
+ return useResponseError('ForbiddenException', 'Forbidden Exception');
+}
+
+export function unAuthorizedResponse(event: H3Event) {
+ setResponseStatus(event, 401);
+ return useResponseError('UnauthorizedException', 'Unauthorized Exception');
+}
diff --git a/apps/web-contract/src/router/routes/modules/contract.ts b/apps/web-contract/src/router/routes/modules/contract.ts
index 7867292f..f0298805 100644
--- a/apps/web-contract/src/router/routes/modules/contract.ts
+++ b/apps/web-contract/src/router/routes/modules/contract.ts
@@ -24,442 +24,463 @@ const routes: RouteRecordRaw[] = [
},
],
},
+ {
+ name: 'ContractApproval',
+ path: '/contract/approval',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同立项',
+ },
+ children: [
{
- name: 'ContractApproval',
- path: '/contract/approval',
- component: BasicLayout,
- meta: {
- icon: 'lucide:area-chart',
- title: '合同立项',
- },
- children: [
- {
- name: 'ContractApprovalEdit',
- path: '/contract/approval/edit/:id?',
- beforeEnter: (e) => {
- if (e.params.id && e.params.id === ':id') {
- e.params.id = ''
- e.fullPath = '/contract/approval/edit'
- }
- },
- component: () => import('#/views/contract/approval/edit/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '立项申报',
- activePath: '/contract/approval/edit/:id?'
- },
- },
- {
- name: 'ContractApprovalTodo',
- path: '/contract/approval/todo',
- component: () => import('#/views/contract/approval/todo/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '立项提示',
- },
- },
- {
- name: 'ContractApprovalList',
- path: '/contract/approval/list',
- component: () => import('#/views/contract/approval/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '立项查询',
- },
- },
- {
- name: 'ContractApprovalSigningBasis',
- path: '/contract/approval/signing-basis',
- component: () => import('#/views/contract/approval/signing-basis/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '签约依据维护',
- },
+ name: 'ContractApprovalEdit',
+ path: '/contract/approval/edit/:id?',
+ beforeEnter: (e) => {
+ if (e.params.id && e.params.id === ':id') {
+ e.params.id = '';
+ e.fullPath = '/contract/approval/edit';
}
- ],
- },
- {
- name: 'ContractBusiness',
- path: '/contract/business',
- component: BasicLayout,
+ },
+ component: () => import('#/views/contract/approval/edit/index.vue'),
meta: {
icon: 'lucide:area-chart',
- title: '合同选商',
+ title: '立项申报',
+ activePath: '/contract/approval/edit/:id?',
},
- children: [
- {
- name: 'ContractBusinessEdit',
- path: '/contract/business/edit/:id?',
- beforeEnter: (e) => {
- if (e.params.id && e.params.id === ':id') {
- e.params.id = ''
- e.fullPath = '/contract/business/edit'
- }
- },
- component: () => import('#/views/contract/business/edit/index.vue'),
- meta: {
- hideInMenu: true,
- hideInTab: true,
- icon: 'lucide:area-chart',
- title: '选商填报',
- activePath: '/contract/business/edit/:id?'
- },
- },
- {
- name: 'ContractBusinessTodo',
- path: '/contract/business/todo',
- component: () => import('#/views/contract/business/todo/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '选商提示',
- },
- },
- {
- name: 'ContractBusinessList',
- path: '/contract/business/list',
- component: () => import('#/views/contract/business/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '选商查询',
- },
- },
- ],
},
{
- name: 'ContractDeclaration',
- path: '/contract/declaration',
- component: BasicLayout,
+ name: 'ContractApprovalTodo',
+ path: '/contract/approval/todo',
+ component: () => import('#/views/contract/approval/todo/index.vue'),
meta: {
icon: 'lucide:area-chart',
- title: '合同申报',
+ title: '立项提示',
},
- children: [
- {
- name: 'ContractDeclarationEdit',
- path: '/contract/declaration/edit/:id?',
- beforeEnter: (e) => {
- if (e.params.id && e.params.id === ':id') {
- e.params.id = ''
- e.fullPath = '/contract/declaration/edit'
- }
- },
- component: () => import('#/views/contract/declaration/edit/index.vue'),
- meta: {
- hideInMenu: true,
- hideInTab: true,
- icon: 'lucide:area-chart',
- title: '申报填报',
- activePath: '/contract/declaration/edit/:id?'
- },
- },
- {
- name: 'ContractDeclarationTodo',
- path: '/contract/declaration/todo',
- component: () => import('#/views/contract/declaration/todo/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '申报提示',
- },
- },
- {
- name: 'ContractDeclarationList',
- path: '/contract/declaration/list',
- component: () => import('#/views/contract/declaration/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '申报查询',
- },
- },
- {
- name: 'ContractDeclarationPrint',
- path: '/contract/declaration/print',
- component: () => import('#/views/contract/declaration/print/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '合同打印',
- },
- },
- ],
},
- // {
- // name: 'ContractAudit',
- // path: '/contract/audit',
- // component: BasicLayout,
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '合同审批',
- // },
- // children: [
- // {
- // name: 'ContractAuditTodo',
- // path: '/contract/audit/todo',
- // component: () => import('#/views/contract/audit/todo/index.vue'),
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '审批提示',
- // },
- // },
- // {
- // name: 'ContractAuditList',
- // path: '/contract/audit/list',
- // component: () => import('#/views/contract/audit/list/index.vue'),
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '审批查询',
- // },
- // },
- // ],
- // },
{
- name: 'ContractSign',
- path: '/contract/sign',
- component: BasicLayout,
+ name: 'ContractApprovalList',
+ path: '/contract/approval/list',
+ component: () => import('#/views/contract/approval/list/index.vue'),
meta: {
icon: 'lucide:area-chart',
- title: '合同签订',
+ title: '立项查询',
},
- children: [
- {
- name: 'ContractSignTodo',
- path: '/contract/sign/todo',
- component: () => import('#/views/contract/sign/todo/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '签订提示',
- },
- },
- {
- name: 'ContractSignList',
- path: '/contract/sign/list',
- component: () => import('#/views/contract/sign/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '签订查询',
- },
- },
- ],
},
{
- name: 'ContractPerform',
- path: '/contract/perform',
- component: BasicLayout,
+ name: 'ContractApprovalSigningBasis',
+ path: '/contract/approval/signing-basis',
+ component: () =>
+ import('#/views/contract/approval/signing-basis/index.vue'),
meta: {
icon: 'lucide:area-chart',
- title: '合同履行',
+ title: '签约依据维护',
+ },
+ },
+ ],
+ },
+ {
+ name: 'ContractBusiness',
+ path: '/contract/business',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同选商',
+ },
+ children: [
+ {
+ name: 'ContractBusinessEdit',
+ path: '/contract/business/edit/:id?',
+ beforeEnter: (e) => {
+ if (e.params.id && e.params.id === ':id') {
+ e.params.id = '';
+ e.fullPath = '/contract/business/edit';
+ }
+ },
+ component: () => import('#/views/contract/business/edit/index.vue'),
+ meta: {
+ hideInMenu: true,
+ hideInTab: true,
+ icon: 'lucide:area-chart',
+ title: '选商填报',
+ activePath: '/contract/business/edit/:id?',
},
- children: [
- {
- name: 'ContractPerformTodo',
- path: '/contract/perform/todo',
- component: () => import('#/views/contract/perform/todo/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '履行提示',
- },
- },
- {
- name: 'ContractPerformList',
- path: '/contract/perform/list',
- component: () => import('#/views/contract/perform/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '履行查询',
- },
- },
- {
- name: 'ContractPerformResult',
- path: '/contract/perform/result',
- component: () => import('#/views/contract/perform/result/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '履行结果填报',
- },
- },
- {
- name: 'ContractPerformTemporaryArchive',
- path: '/contract/perform/temporary-archive',
- component: () => import('#/views/contract/perform/temporary-archive/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '临时归档',
- },
- },
- ],
},
{
- name: 'ContractArchive',
- path: '/contract/archive',
- component: BasicLayout,
+ name: 'ContractBusinessTodo',
+ path: '/contract/business/todo',
+ component: () => import('#/views/contract/business/todo/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '选商提示',
+ },
+ },
+ {
+ name: 'ContractBusinessList',
+ path: '/contract/business/list',
+ component: () => import('#/views/contract/business/list/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '选商查询',
+ },
+ },
+ ],
+ },
+ {
+ name: 'ContractDeclaration',
+ path: '/contract/declaration',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同申报',
+ },
+ children: [
+ {
+ name: 'ContractDeclarationEdit',
+ path: '/contract/declaration/edit/:id?',
+ beforeEnter: (e) => {
+ if (e.params.id && e.params.id === ':id') {
+ e.params.id = '';
+ e.fullPath = '/contract/declaration/edit';
+ }
+ },
+ component: () => import('#/views/contract/declaration/edit/index.vue'),
+ meta: {
+ hideInMenu: true,
+ hideInTab: true,
+ icon: 'lucide:area-chart',
+ title: '申报填报',
+ activePath: '/contract/declaration/edit/:id?',
+ },
+ },
+ {
+ name: 'ContractDeclarationTodo',
+ path: '/contract/declaration/todo',
+ component: () => import('#/views/contract/declaration/todo/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '申报提示',
+ },
+ },
+ {
+ name: 'ContractDeclarationList',
+ path: '/contract/declaration/list',
+ component: () => import('#/views/contract/declaration/list/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '申报查询',
+ },
+ },
+ {
+ name: 'ContractDeclarationPrint',
+ path: '/contract/declaration/print',
+ component: () => import('#/views/contract/declaration/print/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同打印',
+ },
+ },
+ ],
+ },
+ // {
+ // name: 'ContractAudit',
+ // path: '/contract/audit',
+ // component: BasicLayout,
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '合同审批',
+ // },
+ // children: [
+ // {
+ // name: 'ContractAuditTodo',
+ // path: '/contract/audit/todo',
+ // component: () => import('#/views/contract/audit/todo/index.vue'),
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '审批提示',
+ // },
+ // },
+ // {
+ // name: 'ContractAuditList',
+ // path: '/contract/audit/list',
+ // component: () => import('#/views/contract/audit/list/index.vue'),
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '审批查询',
+ // },
+ // },
+ // ],
+ // },
+ {
+ name: 'ContractSign',
+ path: '/contract/sign',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同签订',
+ },
+ children: [
+ {
+ name: 'ContractSignTodo',
+ path: '/contract/sign/todo',
+ component: () => import('#/views/contract/sign/todo/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '签订提示',
+ },
+ },
+ {
+ name: 'ContractSignList',
+ path: '/contract/sign/list',
+ component: () => import('#/views/contract/sign/list/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '签订查询',
+ },
+ },
+ ],
+ },
+ {
+ name: 'ContractPerform',
+ path: '/contract/perform',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同履行',
+ },
+ children: [
+ {
+ name: 'ContractPerformEdit',
+ path: '/contract/perform/edit/:id?',
+ beforeEnter: (e) => {
+ if (e.params.id && e.params.id === ':id') {
+ e.params.id = '';
+ e.fullPath = '/contract/perform/edit';
+ }
+ },
+ component: () => import('#/views/contract/perform/edit/index.vue'),
+ meta: {
+ hideInMenu: true,
+ hideInTab: true,
+ icon: 'lucide:area-chart',
+ title: '选商填报',
+ activePath: '/contract/perform/edit/:id?',
+ },
+ },
+ {
+ name: 'ContractPerformTodo',
+ path: '/contract/perform/todo',
+ component: () => import('#/views/contract/perform/todo/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '履行提示',
+ },
+ },
+ {
+ name: 'ContractPerformList',
+ path: '/contract/perform/list',
+ component: () => import('#/views/contract/perform/list/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '履行查询',
+ },
+ },
+ {
+ name: 'ContractPerformResult',
+ path: '/contract/perform/result',
+ component: () => import('#/views/contract/perform/result/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '履行结果填报',
+ },
+ },
+ {
+ name: 'ContractPerformTemporaryArchive',
+ path: '/contract/perform/temporary-archive',
+ component: () =>
+ import('#/views/contract/perform/temporary-archive/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '临时归档',
+ },
+ },
+ ],
+ },
+ {
+ name: 'ContractArchive',
+ path: '/contract/archive',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同归档',
+ },
+ children: [
+ {
+ name: 'ContractArchiveTodoArchive',
+ path: '/contract/archive/todo/archive',
+ component: () => import('#/views/contract/archive/todo/archive.vue'),
meta: {
icon: 'lucide:area-chart',
title: '合同归档',
},
- children: [
- {
- name: 'ContractArchiveTodoArchive',
- path: '/contract/archive/todo/archive',
- component: () => import('#/views/contract/archive/todo/archive.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '合同归档',
- },
- },
- {
- name: 'ContractArchiveTodoRetracement',
- path: '/contract/archive/todo/retracement',
- component: () => import('#/views/contract/archive/todo/retracement.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '合同回档',
- },
- },
- {
- name: 'ContractArchiveList',
- path: '/contract/archive/list',
- component: () => import('#/views/contract/archive/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '归档查询',
- },
- },
- ],
},
{
- name: 'ContractSignAuthorization',
- path: '/contract/sign-authorization',
- component: BasicLayout,
+ name: 'ContractArchiveTodoRetracement',
+ path: '/contract/archive/todo/retracement',
+ component: () =>
+ import('#/views/contract/archive/todo/retracement.vue'),
meta: {
icon: 'lucide:area-chart',
- title: '签约授权管理',
+ title: '合同回档',
},
- children: [
- {
- name: 'ContractSignAuthorizationEdit',
- path: '/contract/sign-authorization/edit/:id?',
- beforeEnter: (e) => {
- if (e.params.id && e.params.id === ':id') {
- e.params.id = ''
- e.fullPath = '/contract/sign-authorization/edit'
- }
- },
- component: () => import('#/views/contract/sign-authorization/edit/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '签约授权申报',
- },
- },
- {
- name: 'ContractSignAuthorizationList',
- path: '/contract/sign-authorization/list',
- component: () => import('#/views/contract/sign-authorization/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '签约授权查询',
- },
- },
-
- ],
},
{
- name: 'ContractCompany',
- path: '/contract/company',
- component: BasicLayout,
+ name: 'ContractArchiveList',
+ path: '/contract/archive/list',
+ component: () => import('#/views/contract/archive/list/index.vue'),
meta: {
icon: 'lucide:area-chart',
- title: '合同相对人',
+ title: '归档查询',
},
- children: [
- {
- name: 'ContractCompanyEdit',
- path: '/contract/company/edit/:id?',
- beforeEnter: (e) => {
- if (e.params.id && e.params.id === ':id') {
- e.params.id = ''
- e.fullPath = '/contract/company/edit'
- }
- },
- component: () => import('#/views/contract/company/edit/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '相对人录入维护',
- },
- },
- {
- name: 'ContractCompanyList',
- path: '/contract/company/list',
- component: () => import('#/views/contract/company/list/index.vue'),
- meta: {
- icon: 'lucide:area-chart',
- title: '相对人查询',
- },
- },
-
- ],
},
- // {
- // name: 'ContractStatistic',
- // path: '/contract/statistic',
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '统计分析',
- // },
- // children: [
- // {
- // name: 'ContractStatisticAnalysis',
- // path: '/contract/statistic/analysis',
- // beforeEnter: (e) => {
- // if (e.params.id && e.params.id === ':id') {
- // e.params.id = ''
- // e.fullPath = '/contract/company/edit'
- // }
- // },
- // component: () => import('#/views/contract/company/edit/index.vue'),
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '统计分析',
- // },
- // },
- // {
- // name: 'ContractStatisticAbrogateList',
- // path: '/contract/statistic/abrogate-list',
- // component: () => import('#/views/contract/company/list/index.vue'),
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '废除查询',
- // },
- // },
+ ],
+ },
+ {
+ name: 'ContractSignAuthorization',
+ path: '/contract/sign-authorization',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '签约授权管理',
+ },
+ children: [
+ {
+ name: 'ContractSignAuthorizationEdit',
+ path: '/contract/sign-authorization/edit/:id?',
+ beforeEnter: (e) => {
+ if (e.params.id && e.params.id === ':id') {
+ e.params.id = '';
+ e.fullPath = '/contract/sign-authorization/edit';
+ }
+ },
+ component: () =>
+ import('#/views/contract/sign-authorization/edit/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '签约授权申报',
+ },
+ },
+ {
+ name: 'ContractSignAuthorizationList',
+ path: '/contract/sign-authorization/list',
+ component: () =>
+ import('#/views/contract/sign-authorization/list/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '签约授权查询',
+ },
+ },
+ ],
+ },
+ {
+ name: 'ContractCompany',
+ path: '/contract/company',
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '合同相对人',
+ },
+ children: [
+ {
+ name: 'ContractCompanyEdit',
+ path: '/contract/company/edit/:id?',
+ beforeEnter: (e) => {
+ if (e.params.id && e.params.id === ':id') {
+ e.params.id = '';
+ e.fullPath = '/contract/company/edit';
+ }
+ },
+ component: () => import('#/views/contract/company/edit/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '相对人录入维护',
+ },
+ },
+ {
+ name: 'ContractCompanyList',
+ path: '/contract/company/list',
+ component: () => import('#/views/contract/company/list/index.vue'),
+ meta: {
+ icon: 'lucide:area-chart',
+ title: '相对人查询',
+ },
+ },
+ ],
+ },
+ // {
+ // name: 'ContractStatistic',
+ // path: '/contract/statistic',
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '统计分析',
+ // },
+ // children: [
+ // {
+ // name: 'ContractStatisticAnalysis',
+ // path: '/contract/statistic/analysis',
+ // beforeEnter: (e) => {
+ // if (e.params.id && e.params.id === ':id') {
+ // e.params.id = ''
+ // e.fullPath = '/contract/company/edit'
+ // }
+ // },
+ // component: () => import('#/views/contract/company/edit/index.vue'),
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '统计分析',
+ // },
+ // },
+ // {
+ // name: 'ContractStatisticAbrogateList',
+ // path: '/contract/statistic/abrogate-list',
+ // component: () => import('#/views/contract/company/list/index.vue'),
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '废除查询',
+ // },
+ // },
- // ],
- // },
- // {
- // name: 'ContractPrint',
- // path: '/contract/statistic',
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '统计分析',
- // },
- // children: [
- // {
- // name: 'ContractPrintBusiness',
- // path: '/contract/print/business',
- // component: () => import('#/views/contract/company/edit/index.vue'),
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '统计分析',
- // },
- // },
- // {
- // name: 'ContractPrintAbrogateList',
- // path: '/contract/print/abrogate-list',
- // component: () => import('#/views/contract/company/list/index.vue'),
- // meta: {
- // icon: 'lucide:area-chart',
- // title: '废除查询',
- // },
- // },
+ // ],
+ // },
+ // {
+ // name: 'ContractPrint',
+ // path: '/contract/statistic',
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '统计分析',
+ // },
+ // children: [
+ // {
+ // name: 'ContractPrintBusiness',
+ // path: '/contract/print/business',
+ // component: () => import('#/views/contract/company/edit/index.vue'),
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '统计分析',
+ // },
+ // },
+ // {
+ // name: 'ContractPrintAbrogateList',
+ // path: '/contract/print/abrogate-list',
+ // component: () => import('#/views/contract/company/list/index.vue'),
+ // meta: {
+ // icon: 'lucide:area-chart',
+ // title: '废除查询',
+ // },
+ // },
- // ],
- // },
+ // ],
+ // },
];
export default routes;
diff --git a/apps/web-contract/src/utils/dict/shared.ts b/apps/web-contract/src/utils/dict/shared.ts
index a639a2bf..09f65d5e 100644
--- a/apps/web-contract/src/utils/dict/shared.ts
+++ b/apps/web-contract/src/utils/dict/shared.ts
@@ -30,6 +30,8 @@ export enum DICT_TYPE {
comprehensive_config = 'comprehensive_config',
/** 合同管理-签约依据类型 */
contract_basis_type = 'contract_basis_type',
+ /** 合同管理-合同相对人性质 */
+ counterparty_nature = 'counterparty_nature',
/** 变更原因 */
cancel_reson = 'cancel_reson',
/** 划分标段 */
@@ -45,5 +47,7 @@ export enum DICT_TYPE {
/** 付款性质 */
payment_nature = 'payment_nature',
/** 合同立项节点流程 */
- contract_approval_flow_node = 'contract_approval_flow_node'
+ contract_approval_flow_node = 'contract_approval_flow_node',
+ /** 合同立项节点流程 */
+ contract_abolish_flow_node = 'contract_abolish_flow_node'
}
diff --git a/apps/web-contract/src/utils/dict/static.data.js b/apps/web-contract/src/utils/dict/static.data.js
index 9d7270b6..f46f525b 100644
--- a/apps/web-contract/src/utils/dict/static.data.js
+++ b/apps/web-contract/src/utils/dict/static.data.js
@@ -24,6 +24,8 @@ export default {
contract_basis_type: createEntry('合同管理-签约依据类型'),
+ counterparty_nature: createEntry('合同管理-合同相对人性质'),
+
cancel_reson: createEntry('变更原因', [
{ label: '未填写原因', value: '0' },
{ label: '用户取消', value: '1' },
@@ -73,7 +75,7 @@ export default {
{ label: '待部门审核', value: 'departmentAudit' },
{ label: '已结束', value: 'end' },
{ label: '废除发起', value: 'abolishEdit' },
- { label: '待废除审批', value: 'abolishEdit' },
+ { label: '待废除审批', value: 'abolishDepartmentAudit' },
]),
contract_abolish_flow_node: createEntry('合同立项节点流程', [
@@ -82,5 +84,4 @@ export default {
{ label: '已结束', value: 'end' },
{ label: '废除', value: 'abolishEdit' },
]),
-
};
diff --git a/apps/web-contract/src/views/contract/approval/edit/curd.tsx b/apps/web-contract/src/views/contract/approval/edit/curd.tsx
index f7c7264f..66b564e8 100644
--- a/apps/web-contract/src/views/contract/approval/edit/curd.tsx
+++ b/apps/web-contract/src/views/contract/approval/edit/curd.tsx
@@ -46,17 +46,21 @@ export function getFormSchema(params: any = {}) {
name: 'fs-dict-select',
vModel: 'value',
class: 'min-w-[200px]',
+ prototype:true,
dict: dict({
async getData({ form = {} }) {
return filterContractTypes(contractTypeData, '-1');
},
}),
},
- valueChange({ form, value, getComponentRef }) {
- form.ctrTwoType = undefined;
- if (value) {
+ valueChange: {
+ immediate: true, //是否立即执行一次
+ handle({ form, value, getComponentRef }) {
+ form.ctrTwoType = undefined;
+ console.log(getComponentRef('ctrTwoType'));
getComponentRef('ctrTwoType').reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典
- }
+ },
+
},
conditionalRender: {
match({ form }) {
@@ -82,6 +86,7 @@ export function getFormSchema(params: any = {}) {
name: 'fs-dict-select',
vModel: 'value',
class: 'min-w-[200px]',
+ prototype:true,
dict: dict({
async getData({ form = {} }) {
return filterContractTypes(contractTypeData, form.ctrType);
@@ -172,6 +177,8 @@ export function getFormSchema(params: any = {}) {
valueChange: {
immediate: true, //是否立即执行一次
handle({ form }) {
+ debugger;
+
console.log(form);
form.fundAllocationName = getDictObj(
DICT_TYPE.contract_fund_flow,
@@ -267,7 +274,12 @@ export function getFormSchema(params: any = {}) {
render({ form }) {
return (
- {getDictObj(DICT_TYPE.contract_currency_unit, form.priceType)}
+ {
+ getDictObj(
+ DICT_TYPE.contract_currency_unit,
+ form.priceType || '',
+ )?.label
+ }
);
},
@@ -293,7 +305,10 @@ export function getFormSchema(params: any = {}) {
render({ form }) {
return (
- {getDictObj(DICT_TYPE.contract_organization_form, form.organiza)}
+ {
+ getDictObj(DICT_TYPE.contract_organization_form, form.organiza)
+ ?.label
+ }
);
},
diff --git a/apps/web-contract/src/views/contract/company/edit/curd.tsx b/apps/web-contract/src/views/contract/company/edit/curd.tsx
index e05a5d08..e5fc07fb 100644
--- a/apps/web-contract/src/views/contract/company/edit/curd.tsx
+++ b/apps/web-contract/src/views/contract/company/edit/curd.tsx
@@ -1,9 +1,13 @@
-import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { dict } from '@fast-crud/fast-crud';
+import { DICT_TYPE, getDictObj, getDictOptions } from '#/utils/dict';
+
export function getFormSchema(_params: any = {}) {
return {
labelCol: { style: { width: '140px' } },
+ initialForm: {
+ currencyTypeId: 'CNY',
+ },
columns: {
providerName: {
title: '单位全称',
@@ -14,7 +18,7 @@ export function getFormSchema(_params: any = {}) {
vModel: 'value',
allowClear: false,
},
- rules: [{ required: true }],
+ rules: [{ required: true, message: '请输入单位名称' }],
},
providerKind: {
title: '性质',
@@ -25,10 +29,10 @@ export function getFormSchema(_params: any = {}) {
vModel: 'value',
class: 'min-w-[200px]',
dict: dict({
- data: [],
+ data: getDictOptions(DICT_TYPE.counterparty_nature),
}),
},
- rules: [{ required: true }],
+ rules: [{ required: true, message: '请选择单位性质' }],
},
providerAddr: {
title: '住所',
@@ -111,6 +115,15 @@ export function getFormSchema(_params: any = {}) {
data: getDictOptions(DICT_TYPE.contract_currency_unit),
}),
},
+ valueChange: {
+ immediate: true, // 是否立即执行一次
+ handle({ form }) {
+ form.currencyTypeName = getDictObj(
+ DICT_TYPE.contract_currency_unit,
+ form.currencyTypeId,
+ )?.label;
+ },
+ },
},
marketScope: {
title: '营业执照经营范围',
@@ -262,7 +275,7 @@ export function getFormSchemaByBank(_params: any = {}) {
};
}
-export function getFormSchemaByCertification(params: any = {}) {
+export function getFormSchemaByCertification(_params: any = {}) {
return {
labelCol: { style: { width: '140px' } },
initialForm: {},
@@ -292,23 +305,23 @@ export function getFormSchemaByCertification(params: any = {}) {
key: 'time',
col: { span: 12 },
render({ form }) {
- //注意此处的v-model写法
+ // 注意此处的v-model写法
return (
至
@@ -335,7 +348,7 @@ export function getFormSchemaByCertification(params: any = {}) {
};
}
-export function getFormSchemaByShareholder(params: any = {}) {
+export function getFormSchemaByShareholder(_params: any = {}) {
return {
labelCol: { style: { width: '140px' } },
columns: {
@@ -367,7 +380,7 @@ export function getFormSchemaByShareholder(params: any = {}) {
name: 'fs-dict-select',
vModel: 'value',
dict: dict({
- data: [],
+ data: getDictOptions(DICT_TYPE.counterparty_nature),
}),
},
},
diff --git a/apps/web-contract/src/views/contract/company/edit/index.vue b/apps/web-contract/src/views/contract/company/edit/index.vue
index fa767e6e..ecd22091 100644
--- a/apps/web-contract/src/views/contract/company/edit/index.vue
+++ b/apps/web-contract/src/views/contract/company/edit/index.vue
@@ -1,30 +1,27 @@
-
+
- 保存
+
+ 保存
+
删除
- 返回
+
+ 返回
+
-
一键{{ isFold ? '展开' : '收起' }}
+
+ 一键{{ isFold ? '展开' : '收起' }}
+
-
-
-
-
+
+
+
-
+
-
+ />
-
+
{
>
@@ -324,24 +293,23 @@ onMounted(async () => {
-
+
-
+ />
-
+
-
+
diff --git a/apps/web-contract/src/views/contract/declaration/edit/curd.tsx b/apps/web-contract/src/views/contract/declaration/edit/curd.tsx
index 838a1c81..73bac5fd 100644
--- a/apps/web-contract/src/views/contract/declaration/edit/curd.tsx
+++ b/apps/web-contract/src/views/contract/declaration/edit/curd.tsx
@@ -96,15 +96,6 @@ export function getFormSchema(_params: any = {}) {
},
},
},
- projectNum6: {
- title: '公告发布方式',
- key: 'projectNum6',
- col: { span: 8 },
- component: {
- name: 'a-input',
- vModel: 'value',
- },
- },
isBid: {
title: '划分标段',
key: 'isBid',
diff --git a/apps/web-contract/src/views/contract/iframe-info/components/info-approval/info-approval.vue b/apps/web-contract/src/views/contract/iframe-info/components/info-approval/info-approval.vue
index ba64770d..ef107937 100644
--- a/apps/web-contract/src/views/contract/iframe-info/components/info-approval/info-approval.vue
+++ b/apps/web-contract/src/views/contract/iframe-info/components/info-approval/info-approval.vue
@@ -41,6 +41,9 @@ let isLoading = ref(false);
let contractTypeData = ref([]);
+let collapses = ['1', '2'];
+let collapseActiveKey = ref(collapses);
+
const formBinding = ref({
col: { span: 24 },
initialForm: {
@@ -188,33 +191,31 @@ onMounted(async () => {
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/break/curd.tsx b/apps/web-contract/src/views/contract/perform/edit/components/break/curd.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/break/index.vue b/apps/web-contract/src/views/contract/perform/edit/components/break/index.vue
new file mode 100644
index 00000000..63e3768b
--- /dev/null
+++ b/apps/web-contract/src/views/contract/perform/edit/components/break/index.vue
@@ -0,0 +1,3 @@
+
+ break
+
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/change/curd.tsx b/apps/web-contract/src/views/contract/perform/edit/components/change/curd.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/change/index.vue b/apps/web-contract/src/views/contract/perform/edit/components/change/index.vue
new file mode 100644
index 00000000..83f5ea98
--- /dev/null
+++ b/apps/web-contract/src/views/contract/perform/edit/components/change/index.vue
@@ -0,0 +1,3 @@
+
+ change
+
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/payment/curd.tsx b/apps/web-contract/src/views/contract/perform/edit/components/payment/curd.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/payment/index.vue b/apps/web-contract/src/views/contract/perform/edit/components/payment/index.vue
new file mode 100644
index 00000000..89f07ec8
--- /dev/null
+++ b/apps/web-contract/src/views/contract/perform/edit/components/payment/index.vue
@@ -0,0 +1,3 @@
+
+ payment
+
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/relieve/curd.tsx b/apps/web-contract/src/views/contract/perform/edit/components/relieve/curd.tsx
new file mode 100644
index 00000000..e69de29b
diff --git a/apps/web-contract/src/views/contract/perform/edit/components/relieve/index.vue b/apps/web-contract/src/views/contract/perform/edit/components/relieve/index.vue
new file mode 100644
index 00000000..5512b2ff
--- /dev/null
+++ b/apps/web-contract/src/views/contract/perform/edit/components/relieve/index.vue
@@ -0,0 +1,3 @@
+
+ relieve
+
diff --git a/apps/web-contract/src/views/contract/perform/edit/index.vue b/apps/web-contract/src/views/contract/perform/edit/index.vue
new file mode 100644
index 00000000..fcbcc7c8
--- /dev/null
+++ b/apps/web-contract/src/views/contract/perform/edit/index.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-contract/src/views/contract/perform/list/index.vue b/apps/web-contract/src/views/contract/perform/list/index.vue
index e770a913..48cc8b71 100644
--- a/apps/web-contract/src/views/contract/perform/list/index.vue
+++ b/apps/web-contract/src/views/contract/perform/list/index.vue
@@ -1,101 +1,119 @@
-
+
-
-
+
至
-
-
+
-
-
+
至
-
-
+
-
-
+
+
-
+
新增
-
-
+
+
修改
-
+
导出
-
-
+
+
删除
@@ -204,7 +237,6 @@ function toDetail(row) {
-
diff --git a/apps/web-contract/vite.config.mts b/apps/web-contract/vite.config.mts
index f796cf10..3cb16d35 100644
--- a/apps/web-contract/vite.config.mts
+++ b/apps/web-contract/vite.config.mts
@@ -1,8 +1,7 @@
import { defineConfig } from '@vben/vite-config';
-import autoImport from 'unplugin-auto-import/vite';
-
import { autoBuildDict } from 'common-utils';
+import autoImport from 'unplugin-auto-import/vite';
export default defineConfig(async () => {
return {
@@ -14,12 +13,12 @@ export default defineConfig(async () => {
sharedPath: 'src/utils/dict/shared.ts',
}),
autoImport({
+ dts: true, // (false) 配置文件生成位置,默认是根目录 /auto-imports.d.ts
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue'],
include: [/\.[tj]sx?$/, /\.vue$/, /\.vue\?vue/],
resolvers: [],
vueTemplate: true, // 是否在 vue 模板中自动导入
- dts: true, // (false) 配置文件生成位置,默认是根目录 /auto-imports.d.ts
}),
],
server: {
@@ -34,13 +33,6 @@ export default defineConfig(async () => {
target: `http://192.168.148.88:8083/rl`,
ws: true,
},
- '/api/uc': {
- changeOrigin: true,
- rewrite: (path) => path.replace(/^\/api\/uc/, '/'),
- // target: `http://10.71.220.24:8082`,
- target: `http://192.168.148.88:8082`,
- ws: true,
- },
'/api/czg/app': {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/czg\/app/, '/'),
@@ -55,17 +47,10 @@ export default defineConfig(async () => {
// target: `http://192.168.148.88:8082`,
ws: true,
},
- '/api/zp/app': {
+ '/api/uc': {
changeOrigin: true,
- rewrite: (path) => path.replace(/^\/api\/zp\/app/, '/'),
- // mock代理目标地址
- target: `http://192.168.153.236:8083/rl`,
- ws: true,
- },
- '/api/zp/uc': {
- changeOrigin: true,
- rewrite: (path) => path.replace(/^\/api\/zp\/uc/, '/'),
- // mock代理目标地址
+ rewrite: (path) => path.replace(/^\/api\/uc/, '/'),
+ // target: `http://10.71.220.24:8082`,
target: `http://192.168.148.88:8082`,
ws: true,
},
@@ -83,6 +68,34 @@ export default defineConfig(async () => {
target: `http://192.168.148.88:8082`,
ws: true,
},
+ '/api/zp/app': {
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api\/zp\/app/, '/'),
+ // mock代理目标地址
+ target: `http://192.168.153.236:8083/rl`,
+ ws: true,
+ },
+ '/api/zp/uc': {
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api\/zp\/uc/, '/'),
+ // mock代理目标地址
+ target: `http://192.168.148.88:8082`,
+ ws: true,
+ },
+ '/api/zzz/app': {
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api\/zzz\/app/, '/'),
+ // target: `http://192.168.0.193:8083/rl`,
+ target: `http://192.168.147.164:8089/rl`,
+ ws: true,
+ },
+ '/api/zzz/uc': {
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api\/zzz\/uc/, '/'),
+ target: `http://192.168.148.88:8082`,
+ // target: `http://192.168.148.88:8082`,
+ ws: true,
+ },
},
},
},
diff --git a/apps/web-test/.env b/apps/web-test/.env
new file mode 100644
index 00000000..c14a467f
--- /dev/null
+++ b/apps/web-test/.env
@@ -0,0 +1,5 @@
+# 应用标题
+VITE_APP_TITLE=Vben Admin Antd
+
+# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
+VITE_APP_NAMESPACE=vben-web-antd
diff --git a/apps/web-test/.env.analyze b/apps/web-test/.env.analyze
new file mode 100644
index 00000000..ffafa8dd
--- /dev/null
+++ b/apps/web-test/.env.analyze
@@ -0,0 +1,7 @@
+# public path
+VITE_BASE=/
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/api
+
+VITE_VISUALIZER=true
diff --git a/apps/web-test/.env.development b/apps/web-test/.env.development
new file mode 100644
index 00000000..c138f482
--- /dev/null
+++ b/apps/web-test/.env.development
@@ -0,0 +1,16 @@
+# 端口号
+VITE_PORT=5666
+
+VITE_BASE=/
+
+# 接口地址
+VITE_GLOB_API_URL=/api
+
+# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
+VITE_NITRO_MOCK=true
+
+# 是否打开 devtools,true 为打开,false 为关闭
+VITE_DEVTOOLS=false
+
+# 是否注入全局loading
+VITE_INJECT_APP_LOADING=true
diff --git a/apps/web-test/.env.production b/apps/web-test/.env.production
new file mode 100644
index 00000000..5375847a
--- /dev/null
+++ b/apps/web-test/.env.production
@@ -0,0 +1,19 @@
+VITE_BASE=/
+
+# 接口地址
+VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
+
+# 是否开启压缩,可以设置为 none, brotli, gzip
+VITE_COMPRESS=none
+
+# 是否开启 PWA
+VITE_PWA=false
+
+# vue-router 的模式
+VITE_ROUTER_HISTORY=hash
+
+# 是否注入全局loading
+VITE_INJECT_APP_LOADING=true
+
+# 打包后是否生成dist.zip
+VITE_ARCHIVER=true
diff --git a/apps/web-test/index.html b/apps/web-test/index.html
new file mode 100644
index 00000000..480eb84d
--- /dev/null
+++ b/apps/web-test/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+ <%= VITE_APP_TITLE %>
+
+
+
+
+
+
+
+
diff --git a/apps/web-test/package.json b/apps/web-test/package.json
new file mode 100644
index 00000000..14584c33
--- /dev/null
+++ b/apps/web-test/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "web-test",
+ "version": "5.3.0-beta.2",
+ "homepage": "https://vben.pro",
+ "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "apps/web-antd"
+ },
+ "license": "MIT",
+ "author": {
+ "name": "vben",
+ "email": "ann.vben@gmail.com",
+ "url": "https://github.com/anncwb"
+ },
+ "type": "module",
+ "scripts": {
+ "build": "pnpm vite build --mode production",
+ "build:analyze": "pnpm vite build --mode analyze",
+ "dev": "pnpm vite --mode development",
+ "preview": "vite preview",
+ "typecheck": "vue-tsc --noEmit --skipLibCheck"
+ },
+ "imports": {
+ "#/*": "./src/*"
+ },
+ "dependencies": {
+ "@vben/access": "workspace:*",
+ "@vben/common-ui": "workspace:*",
+ "@vben/constants": "workspace:*",
+ "@vben/hooks": "workspace:*",
+ "@vben/icons": "workspace:*",
+ "@vben/layouts": "workspace:*",
+ "@vben/locales": "workspace:*",
+ "@vben/plugins": "workspace:*",
+ "@vben/preferences": "workspace:*",
+ "@vben/request": "workspace:*",
+ "@vben/stores": "workspace:*",
+ "@vben/styles": "workspace:*",
+ "@vben/types": "workspace:*",
+ "@vben/utils": "workspace:*",
+ "@vueuse/core": "^11.0.3",
+ "ant-design-vue": "^4.2.3",
+ "dayjs": "^1.11.13",
+ "pinia": "2.2.2",
+ "vue": "^3.5.4",
+ "vue-grid-layout": "3.0.0-beta1",
+ "vue-grid-layout-v3": "^3.0.3",
+ "vue-router": "^4.4.4",
+ "vue3-grid-layout-next": "^1.0.7"
+ }
+}
diff --git a/apps/web-test/postcss.config.mjs b/apps/web-test/postcss.config.mjs
new file mode 100644
index 00000000..3d807045
--- /dev/null
+++ b/apps/web-test/postcss.config.mjs
@@ -0,0 +1 @@
+export { default } from '@vben/tailwind-config/postcss';
diff --git a/apps/web-test/public/favicon.ico b/apps/web-test/public/favicon.ico
new file mode 100644
index 00000000..fcf9818e
Binary files /dev/null and b/apps/web-test/public/favicon.ico differ
diff --git a/apps/web-test/src/adapter/form.ts b/apps/web-test/src/adapter/form.ts
new file mode 100644
index 00000000..06e8cf13
--- /dev/null
+++ b/apps/web-test/src/adapter/form.ts
@@ -0,0 +1,114 @@
+import type {
+ BaseFormComponentType,
+ VbenFormSchema as FormSchema,
+ VbenFormProps,
+} from '@vben/common-ui';
+
+import { h } from 'vue';
+
+import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
+import { $t } from '@vben/locales';
+
+import {
+ AutoComplete,
+ Button,
+ Checkbox,
+ CheckboxGroup,
+ DatePicker,
+ Divider,
+ Input,
+ InputNumber,
+ InputPassword,
+ Mentions,
+ Radio,
+ RadioGroup,
+ RangePicker,
+ Rate,
+ Select,
+ Space,
+ Switch,
+ TimePicker,
+ TreeSelect,
+ Upload,
+} from 'ant-design-vue';
+
+// 业务表单组件适配
+
+export type FormComponentType =
+ | 'AutoComplete'
+ | 'Checkbox'
+ | 'CheckboxGroup'
+ | 'DatePicker'
+ | 'Divider'
+ | 'Input'
+ | 'InputNumber'
+ | 'InputPassword'
+ | 'Mentions'
+ | 'Radio'
+ | 'RadioGroup'
+ | 'RangePicker'
+ | 'Rate'
+ | 'Select'
+ | 'Space'
+ | 'Switch'
+ | 'TimePicker'
+ | 'TreeSelect'
+ | 'Upload'
+ | BaseFormComponentType;
+
+// 初始化表单组件,并注册到form组件内部
+setupVbenForm({
+ components: {
+ AutoComplete,
+ Checkbox,
+ CheckboxGroup,
+ DatePicker,
+ // 自定义默认的重置按钮
+ DefaultResetActionButton: (props, { attrs, slots }) => {
+ return h(Button, { ...props, attrs, type: 'default' }, slots);
+ },
+ // 自定义默认的提交按钮
+ DefaultSubmitActionButton: (props, { attrs, slots }) => {
+ return h(Button, { ...props, attrs, type: 'primary' }, slots);
+ },
+ Divider,
+ Input,
+ InputNumber,
+ InputPassword,
+ Mentions,
+ Radio,
+ RadioGroup,
+ RangePicker,
+ Rate,
+ Select,
+ Space,
+ Switch,
+ TimePicker,
+ TreeSelect,
+ Upload,
+ },
+ config: {
+ baseModelPropName: 'value',
+ modelPropNameMap: {
+ Checkbox: 'checked',
+ Radio: 'checked',
+ Switch: 'checked',
+ Upload: 'fileList',
+ },
+ },
+ defineRules: {
+ required: (value, _params, ctx) => {
+ if ((!value && value !== 0) || value.length === 0) {
+ return $t('formRules.required', [ctx.label]);
+ }
+ return true;
+ },
+ },
+});
+
+const useVbenForm = useForm;
+
+export { useVbenForm, z };
+
+export type VbenFormSchema = FormSchema;
+export type { VbenFormProps };
diff --git a/apps/web-test/src/adapter/index.ts b/apps/web-test/src/adapter/index.ts
new file mode 100644
index 00000000..698d687b
--- /dev/null
+++ b/apps/web-test/src/adapter/index.ts
@@ -0,0 +1 @@
+export * from './form';
diff --git a/apps/web-test/src/api/core/auth.ts b/apps/web-test/src/api/core/auth.ts
new file mode 100644
index 00000000..a40ff0a1
--- /dev/null
+++ b/apps/web-test/src/api/core/auth.ts
@@ -0,0 +1,55 @@
+import { baseRequestClient, requestClient } from '#/api/request';
+
+export namespace AuthApi {
+ /** 登录接口参数 */
+ export interface LoginParams {
+ password: string;
+ username: string;
+ }
+
+ /** 登录接口返回值 */
+ export interface LoginResult {
+ accessToken: string;
+ desc: string;
+ realName: string;
+ userId: string;
+ username: string;
+ }
+
+ export interface RefreshTokenResult {
+ data: string;
+ status: number;
+ }
+}
+
+/**
+ * 登录
+ */
+export async function loginApi(data: AuthApi.LoginParams) {
+ return requestClient.post('/auth/login', data);
+}
+
+/**
+ * 刷新accessToken
+ */
+export async function refreshTokenApi() {
+ return baseRequestClient.post('/auth/refresh', {
+ withCredentials: true,
+ });
+}
+
+/**
+ * 退出登录
+ */
+export async function logoutApi() {
+ return baseRequestClient.post('/auth/logout', {
+ withCredentials: true,
+ });
+}
+
+/**
+ * 获取用户权限码
+ */
+export async function getAccessCodesApi() {
+ return requestClient.get('/auth/codes');
+}
diff --git a/apps/web-test/src/api/core/index.ts b/apps/web-test/src/api/core/index.ts
new file mode 100644
index 00000000..28a5aef4
--- /dev/null
+++ b/apps/web-test/src/api/core/index.ts
@@ -0,0 +1,3 @@
+export * from './auth';
+export * from './menu';
+export * from './user';
diff --git a/apps/web-test/src/api/core/menu.ts b/apps/web-test/src/api/core/menu.ts
new file mode 100644
index 00000000..9ef60b11
--- /dev/null
+++ b/apps/web-test/src/api/core/menu.ts
@@ -0,0 +1,10 @@
+import type { RouteRecordStringComponent } from '@vben/types';
+
+import { requestClient } from '#/api/request';
+
+/**
+ * 获取用户所有菜单
+ */
+export async function getAllMenusApi() {
+ return requestClient.get('/menu/all');
+}
diff --git a/apps/web-test/src/api/core/user.ts b/apps/web-test/src/api/core/user.ts
new file mode 100644
index 00000000..7e28ea84
--- /dev/null
+++ b/apps/web-test/src/api/core/user.ts
@@ -0,0 +1,10 @@
+import type { UserInfo } from '@vben/types';
+
+import { requestClient } from '#/api/request';
+
+/**
+ * 获取用户信息
+ */
+export async function getUserInfoApi() {
+ return requestClient.get('/user/info');
+}
diff --git a/apps/web-test/src/api/index.ts b/apps/web-test/src/api/index.ts
new file mode 100644
index 00000000..4b0e0413
--- /dev/null
+++ b/apps/web-test/src/api/index.ts
@@ -0,0 +1 @@
+export * from './core';
diff --git a/apps/web-test/src/api/request.ts b/apps/web-test/src/api/request.ts
new file mode 100644
index 00000000..3c7ad767
--- /dev/null
+++ b/apps/web-test/src/api/request.ts
@@ -0,0 +1,104 @@
+/**
+ * 该文件可自行根据业务逻辑进行调整
+ */
+import { useAppConfig } from '@vben/hooks';
+import { preferences } from '@vben/preferences';
+import {
+ authenticateResponseInterceptor,
+ errorMessageResponseInterceptor,
+ RequestClient,
+} from '@vben/request';
+import { useAccessStore } from '@vben/stores';
+
+import { message } from 'ant-design-vue';
+
+import { useAuthStore } from '#/store';
+
+import { refreshTokenApi } from './core';
+
+const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
+
+function createRequestClient(baseURL: string) {
+ const client = new RequestClient({
+ baseURL,
+ });
+
+ /**
+ * 重新认证逻辑
+ */
+ async function doReAuthenticate() {
+ console.warn('Access token or refresh token is invalid or expired. ');
+ const accessStore = useAccessStore();
+ const authStore = useAuthStore();
+ accessStore.setAccessToken(null);
+ if (
+ preferences.app.loginExpiredMode === 'modal' &&
+ accessStore.isAccessChecked
+ ) {
+ accessStore.setLoginExpired(true);
+ } else {
+ await authStore.logout();
+ }
+ }
+
+ /**
+ * 刷新token逻辑
+ */
+ async function doRefreshToken() {
+ const accessStore = useAccessStore();
+ const resp = await refreshTokenApi();
+ const newToken = resp.data;
+ accessStore.setAccessToken(newToken);
+ return newToken;
+ }
+
+ function formatToken(token: null | string) {
+ return token ? `Bearer ${token}` : null;
+ }
+
+ // 请求头处理
+ client.addRequestInterceptor({
+ fulfilled: async (config) => {
+ const accessStore = useAccessStore();
+
+ config.headers.Authorization = formatToken(accessStore.accessToken);
+ config.headers['Accept-Language'] = preferences.app.locale;
+ return config;
+ },
+ });
+
+ // response数据解构
+ client.addResponseInterceptor({
+ fulfilled: (response) => {
+ const { data: responseData, status } = response;
+
+ const { code, data, message: msg } = responseData;
+ if (status >= 200 && status < 400 && code === 0) {
+ return data;
+ }
+ throw new Error(`Error ${status}: ${msg}`);
+ },
+ });
+
+ // token过期的处理
+ client.addResponseInterceptor(
+ authenticateResponseInterceptor({
+ client,
+ doReAuthenticate,
+ doRefreshToken,
+ enableRefreshToken: preferences.app.enableRefreshToken,
+ formatToken,
+ }),
+ );
+
+ // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
+ client.addResponseInterceptor(
+ errorMessageResponseInterceptor((msg: string) => message.error(msg)),
+ );
+
+ return client;
+}
+
+export const requestClient = createRequestClient(apiURL);
+
+export const baseRequestClient = new RequestClient({ baseURL: apiURL });
diff --git a/apps/web-test/src/app.vue b/apps/web-test/src/app.vue
new file mode 100644
index 00000000..bbaccce1
--- /dev/null
+++ b/apps/web-test/src/app.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-test/src/bootstrap.ts b/apps/web-test/src/bootstrap.ts
new file mode 100644
index 00000000..e76befc9
--- /dev/null
+++ b/apps/web-test/src/bootstrap.ts
@@ -0,0 +1,35 @@
+import { createApp } from 'vue';
+
+import { registerAccessDirective } from '@vben/access';
+import { initStores } from '@vben/stores';
+import '@vben/styles';
+import '@vben/styles/antd';
+
+import VueGridLayout from 'vue3-grid-layout-next';
+
+import { setupI18n } from '#/locales';
+
+import App from './app.vue';
+import { router } from './router';
+
+async function bootstrap(namespace: string) {
+ const app = createApp(App);
+
+ // 国际化 i18n 配置
+ await setupI18n(app);
+
+ // 配置 pinia-tore
+ await initStores(app, { namespace });
+
+ // 安装权限指令
+ registerAccessDirective(app);
+
+ // 配置路由及路由守卫
+ app.use(router);
+
+ app.use(VueGridLayout);
+
+ app.mount('#app');
+}
+
+export { bootstrap };
diff --git a/apps/web-test/src/components/Output1/index.vue b/apps/web-test/src/components/Output1/index.vue
new file mode 100644
index 00000000..0662ad2f
--- /dev/null
+++ b/apps/web-test/src/components/Output1/index.vue
@@ -0,0 +1,3 @@
+
+ 123123
+
diff --git a/apps/web-test/src/components/Output1/src/index.vue b/apps/web-test/src/components/Output1/src/index.vue
new file mode 100644
index 00000000..954d296d
--- /dev/null
+++ b/apps/web-test/src/components/Output1/src/index.vue
@@ -0,0 +1,3 @@
+
+ 1231231231
+
diff --git a/apps/web-test/src/layouts/auth.vue b/apps/web-test/src/layouts/auth.vue
new file mode 100644
index 00000000..18d415bc
--- /dev/null
+++ b/apps/web-test/src/layouts/auth.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/apps/web-test/src/layouts/basic.vue b/apps/web-test/src/layouts/basic.vue
new file mode 100644
index 00000000..3c302634
--- /dev/null
+++ b/apps/web-test/src/layouts/basic.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-test/src/layouts/index.ts b/apps/web-test/src/layouts/index.ts
new file mode 100644
index 00000000..a4320780
--- /dev/null
+++ b/apps/web-test/src/layouts/index.ts
@@ -0,0 +1,6 @@
+const BasicLayout = () => import('./basic.vue');
+const AuthPageLayout = () => import('./auth.vue');
+
+const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
+
+export { AuthPageLayout, BasicLayout, IFrameView };
diff --git a/apps/web-test/src/locales/README.md b/apps/web-test/src/locales/README.md
new file mode 100644
index 00000000..7b451032
--- /dev/null
+++ b/apps/web-test/src/locales/README.md
@@ -0,0 +1,3 @@
+# locale
+
+每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。
diff --git a/apps/web-test/src/locales/index.ts b/apps/web-test/src/locales/index.ts
new file mode 100644
index 00000000..c3fd8c84
--- /dev/null
+++ b/apps/web-test/src/locales/index.ts
@@ -0,0 +1,94 @@
+import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
+import type { Locale } from 'ant-design-vue/es/locale';
+
+import type { App } from 'vue';
+import { ref } from 'vue';
+
+import { $t, setupI18n as coreSetup, loadLocalesMap } from '@vben/locales';
+import { preferences } from '@vben/preferences';
+
+import antdEnLocale from 'ant-design-vue/es/locale/en_US';
+import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN';
+import dayjs from 'dayjs';
+
+const antdLocale = ref(antdDefaultLocale);
+
+const modules = import.meta.glob('./langs/*.json');
+
+const localesMap = loadLocalesMap(modules);
+
+/**
+ * 加载应用特有的语言包
+ * 这里也可以改造为从服务端获取翻译数据
+ * @param lang
+ */
+async function loadMessages(lang: SupportedLanguagesType) {
+ const [appLocaleMessages] = await Promise.all([
+ localesMap[lang]?.(),
+ loadThirdPartyMessage(lang),
+ ]);
+ return appLocaleMessages?.default;
+}
+
+/**
+ * 加载第三方组件库的语言包
+ * @param lang
+ */
+async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
+ await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]);
+}
+
+/**
+ * 加载dayjs的语言包
+ * @param lang
+ */
+async function loadDayjsLocale(lang: SupportedLanguagesType) {
+ let locale;
+ switch (lang) {
+ case 'zh-CN': {
+ locale = await import('dayjs/locale/zh-cn');
+ break;
+ }
+ case 'en-US': {
+ locale = await import('dayjs/locale/en');
+ break;
+ }
+ // 默认使用英语
+ default: {
+ locale = await import('dayjs/locale/en');
+ }
+ }
+ if (locale) {
+ dayjs.locale(locale);
+ } else {
+ console.error(`Failed to load dayjs locale for ${lang}`);
+ }
+}
+
+/**
+ * 加载antd的语言包
+ * @param lang
+ */
+async function loadAntdLocale(lang: SupportedLanguagesType) {
+ switch (lang) {
+ case 'zh-CN': {
+ antdLocale.value = antdDefaultLocale;
+ break;
+ }
+ case 'en-US': {
+ antdLocale.value = antdEnLocale;
+ break;
+ }
+ }
+}
+
+async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
+ await coreSetup(app, {
+ defaultLocale: preferences.app.locale,
+ loadMessages,
+ missingWarn: !import.meta.env.PROD,
+ ...options,
+ });
+}
+
+export { $t, antdLocale, setupI18n };
diff --git a/apps/web-test/src/locales/langs/en-US.json b/apps/web-test/src/locales/langs/en-US.json
new file mode 100644
index 00000000..864c721f
--- /dev/null
+++ b/apps/web-test/src/locales/langs/en-US.json
@@ -0,0 +1,8 @@
+{
+ "page": {
+ "demos": {
+ "title": "Demos",
+ "antd": "Ant Design Vue"
+ }
+ }
+}
diff --git a/apps/web-test/src/locales/langs/zh-CN.json b/apps/web-test/src/locales/langs/zh-CN.json
new file mode 100644
index 00000000..31d3475b
--- /dev/null
+++ b/apps/web-test/src/locales/langs/zh-CN.json
@@ -0,0 +1,8 @@
+{
+ "page": {
+ "demos": {
+ "title": "演示",
+ "antd": "Ant Design Vue"
+ }
+ }
+}
diff --git a/apps/web-test/src/main.ts b/apps/web-test/src/main.ts
new file mode 100644
index 00000000..5d728a02
--- /dev/null
+++ b/apps/web-test/src/main.ts
@@ -0,0 +1,31 @@
+import { initPreferences } from '@vben/preferences';
+import { unmountGlobalLoading } from '@vben/utils';
+
+import { overridesPreferences } from './preferences';
+
+/**
+ * 应用初始化完成之后再进行页面加载渲染
+ */
+async function initApplication() {
+ // name用于指定项目唯一标识
+ // 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据
+ const env = import.meta.env.PROD ? 'prod' : 'dev';
+ const appVersion = import.meta.env.VITE_APP_VERSION;
+ const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`;
+
+ // app偏好设置初始化
+ await initPreferences({
+ namespace,
+ overrides: overridesPreferences,
+ });
+
+ // 启动应用并挂载
+ // vue应用主要逻辑及视图
+ const { bootstrap } = await import('./bootstrap');
+ await bootstrap(namespace);
+
+ // 移除并销毁loading
+ unmountGlobalLoading();
+}
+
+initApplication();
diff --git a/apps/web-test/src/preferences.ts b/apps/web-test/src/preferences.ts
new file mode 100644
index 00000000..7791ddd7
--- /dev/null
+++ b/apps/web-test/src/preferences.ts
@@ -0,0 +1,33 @@
+import { defineOverridesPreferences } from '@vben/preferences';
+
+/**
+ * @description 项目配置文件
+ * 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
+ */
+export const overridesPreferences = defineOverridesPreferences({
+ // overrides
+ app: {
+ name: import.meta.env.VITE_APP_TITLE,
+ accessMode: 'frontend',
+ authPageLayout: 'panel-center',
+ colorGrayMode: false,
+ colorWeakMode: false,
+ compact: false,
+ contentCompact: 'wide',
+ defaultAvatar: '',
+ dynamicTitle: true,
+ // 是否开启检查更新
+ enableCheckUpdates: true,
+ // 检查更新的时间间隔,单位为分钟
+ checkUpdatesInterval: 1,
+ // 开启布局设置按钮
+ enablePreferences: false,
+ enableRefreshToken: false,
+ isMobile: false,
+ layout: 'sidebar-nav',
+ locale: 'zh-CN',
+ loginExpiredMode: 'page',
+ preferencesButtonPosition: 'auto',
+ watermark: false,
+ },
+});
diff --git a/apps/web-test/src/router/access.ts b/apps/web-test/src/router/access.ts
new file mode 100644
index 00000000..3a48be23
--- /dev/null
+++ b/apps/web-test/src/router/access.ts
@@ -0,0 +1,42 @@
+import type {
+ ComponentRecordType,
+ GenerateMenuAndRoutesOptions,
+} from '@vben/types';
+
+import { generateAccessible } from '@vben/access';
+import { preferences } from '@vben/preferences';
+
+import { message } from 'ant-design-vue';
+
+import { getAllMenusApi } from '#/api';
+import { BasicLayout, IFrameView } from '#/layouts';
+import { $t } from '#/locales';
+
+const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
+
+async function generateAccess(options: GenerateMenuAndRoutesOptions) {
+ const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue');
+
+ const layoutMap: ComponentRecordType = {
+ BasicLayout,
+ IFrameView,
+ };
+
+ return await generateAccessible(preferences.app.accessMode, {
+ ...options,
+ fetchMenuListAsync: async () => {
+ message.loading({
+ content: `${$t('common.loadingMenu')}...`,
+ duration: 1.5,
+ });
+ return await getAllMenusApi();
+ },
+ // 可以指定没有权限跳转403页面
+ forbiddenComponent,
+ // 如果 route.meta.menuVisibleWithForbidden = true
+ layoutMap,
+ pageMap,
+ });
+}
+
+export { generateAccess };
diff --git a/apps/web-test/src/router/guard.ts b/apps/web-test/src/router/guard.ts
new file mode 100644
index 00000000..673a86e4
--- /dev/null
+++ b/apps/web-test/src/router/guard.ts
@@ -0,0 +1,137 @@
+import type { Router } from 'vue-router';
+
+import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
+import { preferences } from '@vben/preferences';
+import { useAccessStore, useUserStore } from '@vben/stores';
+import { startProgress, stopProgress } from '@vben/utils';
+
+import { useTitle } from '@vueuse/core';
+
+import { $t } from '#/locales';
+import { coreRouteNames, dynamicRoutes } from '#/router/routes';
+import { useAuthStore } from '#/store';
+
+import { generateAccess } from './access';
+
+/**
+ * 通用守卫配置
+ * @param router
+ */
+function setupCommonGuard(router: Router) {
+ // 记录已经加载的页面
+ const loadedPaths = new Set();
+
+ router.beforeEach(async (to) => {
+ to.meta.loaded = loadedPaths.has(to.path);
+
+ // 页面加载进度条
+ if (!to.meta.loaded && preferences.transition.progress) {
+ startProgress();
+ }
+ return true;
+ });
+
+ router.afterEach((to) => {
+ // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
+
+ if (preferences.tabbar.enable) {
+ loadedPaths.add(to.path);
+ }
+
+ // 关闭页面加载进度条
+ if (preferences.transition.progress) {
+ stopProgress();
+ }
+
+ // 动态修改标题
+ if (preferences.app.dynamicTitle) {
+ const { title } = to.meta;
+ // useTitle(`${$t(title)} - ${preferences.app.name}`);
+ useTitle(`${$t(title)} - ${preferences.app.name}`);
+ }
+ });
+}
+
+/**
+ * 权限访问守卫配置
+ * @param router
+ */
+function setupAccessGuard(router: Router) {
+ router.beforeEach(async (to, from) => {
+ const accessStore = useAccessStore();
+ const userStore = useUserStore();
+ const authStore = useAuthStore();
+
+ // 基本路由,这些路由不需要进入权限拦截
+ if (coreRouteNames.includes(to.name as string)) {
+ if (to.path === LOGIN_PATH && accessStore.accessToken) {
+ return decodeURIComponent(
+ (to.query?.redirect as string) || DEFAULT_HOME_PATH,
+ );
+ }
+ return true;
+ }
+
+ // accessToken 检查
+ if (!accessStore.accessToken) {
+ // 明确声明忽略权限访问权限,则可以访问
+ if (to.meta.ignoreAccess) {
+ return true;
+ }
+
+ // 没有访问权限,跳转登录页面
+ if (to.fullPath !== LOGIN_PATH) {
+ return {
+ path: LOGIN_PATH,
+ // 如不需要,直接删除 query
+ query: { redirect: encodeURIComponent(to.fullPath) },
+ // 携带当前跳转的页面,登录后重新跳转该页面
+ replace: true,
+ };
+ }
+ return to;
+ }
+
+ // 是否已经生成过动态路由
+ if (accessStore.isAccessChecked) {
+ return true;
+ }
+
+ // 生成路由表
+ // 当前登录用户拥有的角色标识列表
+ const userInfo = userStore.userInfo || (await authStore.fetchUserInfo());
+ const userRoles = userInfo.roles ?? [];
+
+ // 生成菜单和路由
+ const { accessibleMenus, accessibleRoutes } = await generateAccess({
+ roles: userRoles,
+ router,
+ // 则会在菜单中显示,但是访问会被重定向到403
+ routes: dynamicRoutes,
+ });
+
+ // 保存菜单信息和路由信息
+ accessStore.setAccessMenus(accessibleMenus);
+ accessStore.setAccessRoutes(accessibleRoutes);
+ accessStore.setIsAccessChecked(true);
+ const redirectPath = (from.query.redirect ?? to.fullPath) as string;
+
+ return {
+ ...router.resolve(decodeURIComponent(redirectPath)),
+ replace: true,
+ };
+ });
+}
+
+/**
+ * 项目守卫配置
+ * @param router
+ */
+function createRouterGuard(router: Router) {
+ /** 通用 */
+ setupCommonGuard(router);
+ /** 权限访问 */
+ setupAccessGuard(router);
+}
+
+export { createRouterGuard };
diff --git a/apps/web-test/src/router/index.ts b/apps/web-test/src/router/index.ts
new file mode 100644
index 00000000..48402303
--- /dev/null
+++ b/apps/web-test/src/router/index.ts
@@ -0,0 +1,37 @@
+import {
+ createRouter,
+ createWebHashHistory,
+ createWebHistory,
+} from 'vue-router';
+
+import { resetStaticRoutes } from '@vben/utils';
+
+import { createRouterGuard } from './guard';
+import { routes } from './routes';
+
+/**
+ * @zh_CN 创建vue-router实例
+ */
+const router = createRouter({
+ history:
+ import.meta.env.VITE_ROUTER_HISTORY === 'hash'
+ ? createWebHashHistory(import.meta.env.VITE_BASE)
+ : createWebHistory(import.meta.env.VITE_BASE),
+ // 应该添加到路由的初始路由列表。
+ routes,
+ scrollBehavior: (to, _from, savedPosition) => {
+ if (savedPosition) {
+ return savedPosition;
+ }
+ return to.hash ? { behavior: 'smooth', el: to.hash } : { left: 0, top: 0 };
+ },
+ // 是否应该禁止尾部斜杠。
+ // strict: true,
+});
+
+const resetRoutes = () => resetStaticRoutes(router, routes);
+
+// 创建路由守卫
+createRouterGuard(router);
+
+export { resetRoutes, router };
diff --git a/apps/web-test/src/router/routes/core.ts b/apps/web-test/src/router/routes/core.ts
new file mode 100644
index 00000000..f793a57e
--- /dev/null
+++ b/apps/web-test/src/router/routes/core.ts
@@ -0,0 +1,86 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { DEFAULT_HOME_PATH } from '@vben/constants';
+
+import { AuthPageLayout } from '#/layouts';
+import { $t } from '#/locales';
+import Login from '#/views/_core/authentication/login.vue';
+
+/** 全局404页面 */
+const fallbackNotFoundRoute: RouteRecordRaw = {
+ component: () => import('#/views/_core/fallback/not-found.vue'),
+ meta: {
+ hideInBreadcrumb: true,
+ hideInMenu: true,
+ hideInTab: true,
+ title: '404',
+ },
+ name: 'FallbackNotFound',
+ path: '/:path(.*)*',
+};
+
+/** 基本路由,这些路由是必须存在的 */
+const coreRoutes: RouteRecordRaw[] = [
+ {
+ meta: {
+ title: 'Root',
+ },
+ name: 'Root',
+ path: '/',
+ redirect: DEFAULT_HOME_PATH,
+ },
+ {
+ component: AuthPageLayout,
+ meta: {
+ title: 'Authentication',
+ },
+ name: 'Authentication',
+ path: '/auth',
+ children: [
+ {
+ name: 'Login',
+ path: 'login',
+ component: Login,
+ meta: {
+ title: $t('page.core.login'),
+ },
+ },
+ {
+ name: 'CodeLogin',
+ path: 'code-login',
+ component: () => import('#/views/_core/authentication/code-login.vue'),
+ meta: {
+ title: $t('page.core.codeLogin'),
+ },
+ },
+ {
+ name: 'QrCodeLogin',
+ path: 'qrcode-login',
+ component: () =>
+ import('#/views/_core/authentication/qrcode-login.vue'),
+ meta: {
+ title: $t('page.core.qrcodeLogin'),
+ },
+ },
+ {
+ name: 'ForgetPassword',
+ path: 'forget-password',
+ component: () =>
+ import('#/views/_core/authentication/forget-password.vue'),
+ meta: {
+ title: $t('page.core.forgetPassword'),
+ },
+ },
+ {
+ name: 'Register',
+ path: 'register',
+ component: () => import('#/views/_core/authentication/register.vue'),
+ meta: {
+ title: $t('page.core.register'),
+ },
+ },
+ ],
+ },
+];
+
+export { coreRoutes, fallbackNotFoundRoute };
diff --git a/apps/web-test/src/router/routes/index.ts b/apps/web-test/src/router/routes/index.ts
new file mode 100644
index 00000000..a70c4875
--- /dev/null
+++ b/apps/web-test/src/router/routes/index.ts
@@ -0,0 +1,31 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { mergeRouteModules, traverseTreeValues } from '@vben/utils';
+
+import { coreRoutes, fallbackNotFoundRoute } from './core';
+
+const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
+ eager: true,
+});
+
+// 有需要可以自行打开注释,并创建文件夹
+// const externalRouteFiles = import.meta.glob('./external/**/*.ts', { eager: true });
+
+/** 动态路由 */
+const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
+
+/** 外部路由列表,访问这些页面可以不需要Layout,可能用于内嵌在别的系统 */
+// const externalRoutes: RouteRecordRaw[] = mergeRouteModules(externalRouteFiles);
+const externalRoutes: RouteRecordRaw[] = [];
+
+/** 路由列表,由基本路由+静态路由组成 */
+const routes: RouteRecordRaw[] = [
+ ...coreRoutes,
+ ...externalRoutes,
+ fallbackNotFoundRoute,
+];
+
+/** 基本路由列表,这些路由不需要进入权限拦截 */
+const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
+
+export { coreRouteNames, dynamicRoutes, routes };
diff --git a/apps/web-test/src/router/routes/modules/dashboard.ts b/apps/web-test/src/router/routes/modules/dashboard.ts
new file mode 100644
index 00000000..c83eeb0b
--- /dev/null
+++ b/apps/web-test/src/router/routes/modules/dashboard.ts
@@ -0,0 +1,49 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { BasicLayout } from '#/layouts';
+
+const routes: RouteRecordRaw[] = [
+ {
+ component: BasicLayout,
+ meta: {
+ icon: 'lucide:layout-dashboard',
+ order: -1,
+ title: '首页',
+ },
+ name: 'Dashboard',
+ path: '/home',
+ children: [
+ {
+ name: 'home',
+ path: '/home',
+ component: () => import('#/views/dashboard/home/index.vue'),
+ meta: {
+ affixTab: true,
+ hideInMenu: true,
+ icon: 'lucide:area-chart',
+ title: '首页',
+ },
+ },
+ // {
+ // name: 'Analytics',
+ // path: '/analytics',
+ // component: () => import('#/views/dashboard/analytics/index.vue'),
+ // meta: {
+ // affixTab: true,
+ // icon: 'lucide:area-chart',
+ // title: $t('page.dashboard.analytics'),
+ // },
+ // },
+ // {
+ // name: 'Workspace',
+ // path: '/workspace',
+ // component: () => import('#/views/dashboard/workspace/index.vue'),
+ // meta: {
+ // title: $t('page.dashboard.workspace'),
+ // },
+ // },
+ ],
+ },
+];
+
+export default routes;
diff --git a/apps/web-test/src/router/routes/modules/demos.ts b/apps/web-test/src/router/routes/modules/demos.ts
new file mode 100644
index 00000000..fa28acc7
--- /dev/null
+++ b/apps/web-test/src/router/routes/modules/demos.ts
@@ -0,0 +1,30 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import { BasicLayout } from '#/layouts';
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ component: BasicLayout,
+ meta: {
+ icon: 'ic:baseline-view-in-ar',
+ keepAlive: true,
+ order: 1000,
+ title: $t('page.demos.title'),
+ },
+ name: 'Demos',
+ path: '/demos',
+ children: [
+ {
+ meta: {
+ title: $t('page.demos.antd'),
+ },
+ name: 'AntDesignDemos',
+ path: '/demos/ant-design',
+ component: () => import('#/views/demos/antd/index.vue'),
+ },
+ ],
+ },
+];
+
+export default routes;
diff --git a/apps/web-test/src/router/routes/modules/vben.ts b/apps/web-test/src/router/routes/modules/vben.ts
new file mode 100644
index 00000000..46947212
--- /dev/null
+++ b/apps/web-test/src/router/routes/modules/vben.ts
@@ -0,0 +1,81 @@
+import type { RouteRecordRaw } from 'vue-router';
+
+import {
+ VBEN_DOC_URL,
+ VBEN_ELE_PREVIEW_URL,
+ VBEN_GITHUB_URL,
+ VBEN_LOGO_URL,
+ VBEN_NAIVE_PREVIEW_URL,
+} from '@vben/constants';
+
+import { BasicLayout, IFrameView } from '#/layouts';
+import { $t } from '#/locales';
+
+const routes: RouteRecordRaw[] = [
+ {
+ component: BasicLayout,
+ meta: {
+ badgeType: 'dot',
+ icon: VBEN_LOGO_URL,
+ order: 9999,
+ title: $t('page.vben.title'),
+ },
+ name: 'VbenProject',
+ path: '/vben-admin',
+ children: [
+ {
+ name: 'VbenAbout',
+ path: '/vben-admin/about',
+ component: () => import('#/views/_core/about/index.vue'),
+ meta: {
+ icon: 'lucide:copyright',
+ title: $t('page.vben.about'),
+ },
+ },
+ {
+ name: 'VbenDocument',
+ path: '/vben-admin/document',
+ component: IFrameView,
+ meta: {
+ icon: 'lucide:book-open-text',
+ link: VBEN_DOC_URL,
+ title: $t('page.vben.document'),
+ },
+ },
+ {
+ name: 'VbenGithub',
+ path: '/vben-admin/github',
+ component: IFrameView,
+ meta: {
+ icon: 'mdi:github',
+ link: VBEN_GITHUB_URL,
+ title: 'Github',
+ },
+ },
+ {
+ name: 'VbenNaive',
+ path: '/vben-admin/naive',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: 'logos:naiveui',
+ link: VBEN_NAIVE_PREVIEW_URL,
+ title: $t('page.vben.naive-ui'),
+ },
+ },
+ {
+ name: 'VbenElementPlus',
+ path: '/vben-admin/ele',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: 'logos:element',
+ link: VBEN_ELE_PREVIEW_URL,
+ title: $t('page.vben.element-plus'),
+ },
+ },
+ ],
+ },
+];
+
+export default routes;
diff --git a/apps/web-test/src/store/auth.ts b/apps/web-test/src/store/auth.ts
new file mode 100644
index 00000000..e9e89634
--- /dev/null
+++ b/apps/web-test/src/store/auth.ts
@@ -0,0 +1,112 @@
+import type { LoginAndRegisterParams } from '@vben/common-ui';
+import type { UserInfo } from '@vben/types';
+
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
+import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
+
+import { notification } from 'ant-design-vue';
+import { defineStore } from 'pinia';
+
+import { getAccessCodesApi, getUserInfoApi, loginApi, logoutApi } from '#/api';
+import { $t } from '#/locales';
+
+export const useAuthStore = defineStore('auth', () => {
+ const accessStore = useAccessStore();
+ const userStore = useUserStore();
+ const router = useRouter();
+
+ const loginLoading = ref(false);
+
+ /**
+ * 异步处理登录操作
+ * Asynchronously handle the login process
+ * @param params 登录表单数据
+ */
+ async function authLogin(
+ params: LoginAndRegisterParams,
+ onSuccess?: () => Promise | void,
+ ) {
+ // 异步处理用户登录操作并获取 accessToken
+ let userInfo: null | UserInfo = null;
+ try {
+ loginLoading.value = true;
+ const { accessToken } = await loginApi(params);
+
+ // 如果成功获取到 accessToken
+ if (accessToken) {
+ accessStore.setAccessToken(accessToken);
+
+ // 获取用户信息并存储到 accessStore 中
+ const [fetchUserInfoResult, accessCodes] = await Promise.all([
+ fetchUserInfo(),
+ getAccessCodesApi(),
+ ]);
+
+ userInfo = fetchUserInfoResult;
+
+ userStore.setUserInfo(userInfo);
+ accessStore.setAccessCodes(accessCodes);
+
+ if (accessStore.loginExpired) {
+ accessStore.setLoginExpired(false);
+ } else {
+ onSuccess
+ ? await onSuccess?.()
+ : await router.push(userInfo.homePath || DEFAULT_HOME_PATH);
+ }
+
+ if (userInfo?.realName) {
+ notification.success({
+ description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.realName}`,
+ duration: 3,
+ message: $t('authentication.loginSuccess'),
+ });
+ }
+ }
+ } finally {
+ loginLoading.value = false;
+ }
+
+ return {
+ userInfo,
+ };
+ }
+
+ async function logout(redirect: boolean = true) {
+ await logoutApi();
+ resetAllStores();
+ accessStore.setLoginExpired(false);
+
+ // 回登陆页带上当前路由地址
+ await router.replace({
+ path: LOGIN_PATH,
+ query: redirect
+ ? {
+ redirect: encodeURIComponent(router.currentRoute.value.fullPath),
+ }
+ : {},
+ });
+ }
+
+ async function fetchUserInfo() {
+ let userInfo: null | UserInfo = null;
+ userInfo = await getUserInfoApi();
+ userStore.setUserInfo(userInfo);
+ return userInfo;
+ }
+
+ function $reset() {
+ loginLoading.value = false;
+ }
+
+ return {
+ $reset,
+ authLogin,
+ fetchUserInfo,
+ loginLoading,
+ logout,
+ };
+});
diff --git a/apps/web-test/src/store/index.ts b/apps/web-test/src/store/index.ts
new file mode 100644
index 00000000..269586ee
--- /dev/null
+++ b/apps/web-test/src/store/index.ts
@@ -0,0 +1 @@
+export * from './auth';
diff --git a/apps/web-test/src/views/_core/README.md b/apps/web-test/src/views/_core/README.md
new file mode 100644
index 00000000..8248afe6
--- /dev/null
+++ b/apps/web-test/src/views/_core/README.md
@@ -0,0 +1,3 @@
+# \_core
+
+此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。
diff --git a/apps/web-test/src/views/_core/about/index.vue b/apps/web-test/src/views/_core/about/index.vue
new file mode 100644
index 00000000..0ee52433
--- /dev/null
+++ b/apps/web-test/src/views/_core/about/index.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/authentication/code-login.vue b/apps/web-test/src/views/_core/authentication/code-login.vue
new file mode 100644
index 00000000..03cf5502
--- /dev/null
+++ b/apps/web-test/src/views/_core/authentication/code-login.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/authentication/forget-password.vue b/apps/web-test/src/views/_core/authentication/forget-password.vue
new file mode 100644
index 00000000..6cedf3e8
--- /dev/null
+++ b/apps/web-test/src/views/_core/authentication/forget-password.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/authentication/login.vue b/apps/web-test/src/views/_core/authentication/login.vue
new file mode 100644
index 00000000..6b994539
--- /dev/null
+++ b/apps/web-test/src/views/_core/authentication/login.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/authentication/qrcode-login.vue b/apps/web-test/src/views/_core/authentication/qrcode-login.vue
new file mode 100644
index 00000000..23f5f2da
--- /dev/null
+++ b/apps/web-test/src/views/_core/authentication/qrcode-login.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/authentication/register.vue b/apps/web-test/src/views/_core/authentication/register.vue
new file mode 100644
index 00000000..5e0cad07
--- /dev/null
+++ b/apps/web-test/src/views/_core/authentication/register.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/fallback/coming-soon.vue b/apps/web-test/src/views/_core/fallback/coming-soon.vue
new file mode 100644
index 00000000..f394930f
--- /dev/null
+++ b/apps/web-test/src/views/_core/fallback/coming-soon.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/fallback/forbidden.vue b/apps/web-test/src/views/_core/fallback/forbidden.vue
new file mode 100644
index 00000000..8ea65fed
--- /dev/null
+++ b/apps/web-test/src/views/_core/fallback/forbidden.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/fallback/internal-error.vue b/apps/web-test/src/views/_core/fallback/internal-error.vue
new file mode 100644
index 00000000..819a47d5
--- /dev/null
+++ b/apps/web-test/src/views/_core/fallback/internal-error.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/fallback/not-found.vue b/apps/web-test/src/views/_core/fallback/not-found.vue
new file mode 100644
index 00000000..4d178e9c
--- /dev/null
+++ b/apps/web-test/src/views/_core/fallback/not-found.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/_core/fallback/offline.vue b/apps/web-test/src/views/_core/fallback/offline.vue
new file mode 100644
index 00000000..5de4a88d
--- /dev/null
+++ b/apps/web-test/src/views/_core/fallback/offline.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-test/src/views/dashboard/analytics/analytics-trends.vue
new file mode 100644
index 00000000..fadfc917
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/analytics/analytics-trends.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/analytics/analytics-visits-data.vue b/apps/web-test/src/views/dashboard/analytics/analytics-visits-data.vue
new file mode 100644
index 00000000..30c4265d
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/analytics/analytics-visits-data.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/web-test/src/views/dashboard/analytics/analytics-visits-sales.vue
new file mode 100644
index 00000000..260520b8
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/analytics/analytics-visits-sales.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/analytics/analytics-visits-source.vue b/apps/web-test/src/views/dashboard/analytics/analytics-visits-source.vue
new file mode 100644
index 00000000..e0d0aab5
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/analytics/analytics-visits-source.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-test/src/views/dashboard/analytics/analytics-visits.vue
new file mode 100644
index 00000000..7e1f14ee
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/analytics/analytics-visits.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/analytics/index.vue b/apps/web-test/src/views/dashboard/analytics/index.vue
new file mode 100644
index 00000000..00b34df1
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/analytics/index.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/home/components/Output1/index.vue b/apps/web-test/src/views/dashboard/home/components/Output1/index.vue
new file mode 100644
index 00000000..ea9f719e
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/home/components/Output1/index.vue
@@ -0,0 +1,3 @@
+
+ 我是示例组件1
+
diff --git a/apps/web-test/src/views/dashboard/home/components/Output2/index.vue b/apps/web-test/src/views/dashboard/home/components/Output2/index.vue
new file mode 100644
index 00000000..93576846
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/home/components/Output2/index.vue
@@ -0,0 +1,3 @@
+
+ 我是示例组件2
+
diff --git a/apps/web-test/src/views/dashboard/home/components/Output3/index.vue b/apps/web-test/src/views/dashboard/home/components/Output3/index.vue
new file mode 100644
index 00000000..c9b2c92c
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/home/components/Output3/index.vue
@@ -0,0 +1,3 @@
+
+ 我是示例组件3
+
diff --git a/apps/web-test/src/views/dashboard/home/index.vue b/apps/web-test/src/views/dashboard/home/index.vue
new file mode 100644
index 00000000..04a77eeb
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/home/index.vue
@@ -0,0 +1,338 @@
+
+
+
+
+
+
+
+
+ x
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-test/src/views/dashboard/workspace/index.vue b/apps/web-test/src/views/dashboard/workspace/index.vue
new file mode 100644
index 00000000..b84ceebb
--- /dev/null
+++ b/apps/web-test/src/views/dashboard/workspace/index.vue
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+ 早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
+
+ 今日晴,20℃ - 32℃!
+
+
+
+
+
diff --git a/apps/web-test/src/views/demos/antd/index.vue b/apps/web-test/src/views/demos/antd/index.vue
new file mode 100644
index 00000000..b3b05cc1
--- /dev/null
+++ b/apps/web-test/src/views/demos/antd/index.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-test/tailwind.config.mjs b/apps/web-test/tailwind.config.mjs
new file mode 100644
index 00000000..f17f556f
--- /dev/null
+++ b/apps/web-test/tailwind.config.mjs
@@ -0,0 +1 @@
+export { default } from '@vben/tailwind-config';
diff --git a/apps/web-test/tsconfig.json b/apps/web-test/tsconfig.json
new file mode 100644
index 00000000..02c287fe
--- /dev/null
+++ b/apps/web-test/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/tsconfig/web-app.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "#/*": ["./src/*"]
+ }
+ },
+ "references": [{ "path": "./tsconfig.node.json" }],
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
+}
diff --git a/apps/web-test/tsconfig.node.json b/apps/web-test/tsconfig.node.json
new file mode 100644
index 00000000..c2f0d86c
--- /dev/null
+++ b/apps/web-test/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/tsconfig/node.json",
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "noEmit": false
+ },
+ "include": ["vite.config.mts"]
+}
diff --git a/apps/web-test/vite.config.mts b/apps/web-test/vite.config.mts
new file mode 100644
index 00000000..b6360f1d
--- /dev/null
+++ b/apps/web-test/vite.config.mts
@@ -0,0 +1,20 @@
+import { defineConfig } from '@vben/vite-config';
+
+export default defineConfig(async () => {
+ return {
+ application: {},
+ vite: {
+ server: {
+ proxy: {
+ '/api': {
+ changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api/, ''),
+ // mock代理目标地址
+ target: 'http://localhost:5320/api',
+ ws: true,
+ },
+ },
+ },
+ },
+ };
+});
diff --git a/internal/common-utils/src/views/system/dict/crud.tsx b/internal/common-utils/src/views/system/dict/crud.tsx
new file mode 100644
index 00000000..c2401728
--- /dev/null
+++ b/internal/common-utils/src/views/system/dict/crud.tsx
@@ -0,0 +1,77 @@
+// import { type CreateCrudOptionsRet, dict, utils } from '@fast-crud/fast-crud';
+
+// export default function (): CreateCrudOptionsRet {
+// return {
+// crudOptions: {
+// columns: {
+// age: {
+// form: {
+// helper: '正则表达式',
+// rules: [{ message: '必须为整数', pattern: /^\d+$/ }],
+// },
+// title: '年龄',
+// type: 'text',
+// },
+// email: {
+// form: {
+// rules: [{ message: '请填写正确的邮箱', type: 'email' }],
+// },
+// title: '邮箱',
+// type: 'text',
+// },
+// name: {
+// editForm: {
+// rules: [{ max: 5, message: '姓名长度为2-5', min: 2 }],
+// },
+// form: {
+// component: {
+// props: {
+// showWordLimit: true,
+// type: 'text',
+// },
+// },
+// helper: '添加和编辑时必填,编辑时额外需要校验长度',
+// rules: [{ message: '请输入姓名', required: true }],
+// },
+// search: {
+// show: true,
+// },
+// title: '姓名',
+// type: 'text',
+// },
+// status: {
+// dict: dict({
+// url: '/mock/dicts/OpenStatusEnum',
+// }),
+// form: {
+// rules: [{ message: '请选择一个选项', required: true }],
+// },
+// title: '必选',
+// type: 'dict-select',
+// },
+// url: {
+// form: {
+// rules: [{ message: '请填写正确的url', type: 'url' }],
+// },
+// title: 'URL',
+// type: 'text',
+// },
+// },
+// form: {
+// afterSubmit(context) {
+// utils.logger.log('afterSubmit', context);
+// },
+// beforeSubmit(context) {
+// utils.logger.log('beforeSubmit', context);
+// },
+// row: {
+// gutter: 20,
+// },
+// },
+// request: {},
+// settings: {
+// viewFormUseCellComponent: true,
+// },
+// },
+// };
+// }
diff --git a/internal/common-utils/src/views/system/dict/dict-data-edit-modal.vue b/internal/common-utils/src/views/system/dict/dict-data-edit-modal.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/internal/common-utils/src/views/system/dict/dict-type-edit-modal.vue b/internal/common-utils/src/views/system/dict/dict-type-edit-modal.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/internal/common-utils/src/views/system/dict/index.vue b/internal/common-utils/src/views/system/dict/index.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/internal/lint-configs/eslint-config/src/configs/perfectionist.ts b/internal/lint-configs/eslint-config/src/configs/perfectionist.ts
index 1b17b30f..d123fe8f 100644
--- a/internal/lint-configs/eslint-config/src/configs/perfectionist.ts
+++ b/internal/lint-configs/eslint-config/src/configs/perfectionist.ts
@@ -56,21 +56,22 @@ export async function perfectionist(): Promise {
type: 'natural',
},
],
- 'perfectionist/sort-objects': [
- 'error',
- {
- customGroups: {
- items: 'items',
- list: 'list',
- children: 'children',
- },
- groups: ['unknown', 'items', 'list', 'children'],
- ignorePattern: ['children'],
- order: 'asc',
- partitionByComment: 'Part:**',
- type: 'natural',
- },
- ],
+ 'perfectionist/sort-objects': 'off',
+ // 'perfectionist/sort-objects': [
+ // 'error',
+ // {
+ // customGroups: {
+ // items: 'items',
+ // list: 'list',
+ // children: 'children',
+ // },
+ // groups: ['unknown', 'items', 'list', 'children'],
+ // ignorePattern: ['children'],
+ // order: 'asc',
+ // partitionByComment: 'Part:**',
+ // type: 'natural',
+ // },
+ // ],
'perfectionist/sort-vue-attributes': [
'error',
{
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 33e391af..f109e1a3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -111,6 +111,22 @@ importers:
specifier: ^2.1.6
version: 2.1.6(typescript@5.6.2)
+ apps/backend-mock:
+ dependencies:
+ jsonwebtoken:
+ specifier: ^9.0.2
+ version: 9.0.2
+ nitropack:
+ specifier: ^2.9.7
+ version: 2.9.7(encoding@0.1.13)(xml2js@0.6.2)
+ devDependencies:
+ '@types/jsonwebtoken':
+ specifier: ^9.0.7
+ version: 9.0.7
+ h3:
+ specifier: ^1.12.0
+ version: 1.12.0
+
apps/web-antd:
dependencies:
'@vben/access':
@@ -591,6 +607,78 @@ importers:
specifier: ^1.0.7
version: 1.0.7
+ apps/web-test:
+ dependencies:
+ '@vben/access':
+ specifier: workspace:*
+ version: link:../../packages/effects/access
+ '@vben/common-ui':
+ specifier: workspace:*
+ version: link:../../packages/effects/common-ui
+ '@vben/constants':
+ specifier: workspace:*
+ version: link:../../packages/constants
+ '@vben/hooks':
+ specifier: workspace:*
+ version: link:../../packages/effects/hooks
+ '@vben/icons':
+ specifier: workspace:*
+ version: link:../../packages/icons
+ '@vben/layouts':
+ specifier: workspace:*
+ version: link:../../packages/effects/layouts
+ '@vben/locales':
+ specifier: workspace:*
+ version: link:../../packages/locales
+ '@vben/plugins':
+ specifier: workspace:*
+ version: link:../../packages/effects/plugins
+ '@vben/preferences':
+ specifier: workspace:*
+ version: link:../../packages/preferences
+ '@vben/request':
+ specifier: workspace:*
+ version: link:../../packages/effects/request
+ '@vben/stores':
+ specifier: workspace:*
+ version: link:../../packages/stores
+ '@vben/styles':
+ specifier: workspace:*
+ version: link:../../packages/styles
+ '@vben/types':
+ specifier: workspace:*
+ version: link:../../packages/types
+ '@vben/utils':
+ specifier: workspace:*
+ version: link:../../packages/utils
+ '@vueuse/core':
+ specifier: ^11.0.3
+ version: 11.0.3(vue@3.5.4(typescript@5.6.2))
+ ant-design-vue:
+ specifier: ^4.2.3
+ version: 4.2.3(vue@3.5.4(typescript@5.6.2))
+ dayjs:
+ specifier: ^1.11.13
+ version: 1.11.13
+ pinia:
+ specifier: 2.2.2
+ version: 2.2.2(typescript@5.6.2)(vue@3.5.4(typescript@5.6.2))
+ vue:
+ specifier: 3.5.4
+ version: 3.5.4(typescript@5.6.2)
+ vue-grid-layout:
+ specifier: 3.0.0-beta1
+ version: 3.0.0-beta1(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ vue-grid-layout-v3:
+ specifier: ^3.0.3
+ version: 3.0.3(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)(typescript@5.6.2)
+ vue-router:
+ specifier: ^4.4.4
+ version: 4.4.4(vue@3.5.4(typescript@5.6.2))
+ vue3-grid-layout-next:
+ specifier: ^1.0.7
+ version: 1.0.7(typescript@5.6.2)
+
internal/common-utils:
dependencies:
dayjs:
@@ -3727,6 +3815,173 @@ packages:
peerDependencies:
vue: 3.5.4
+ '@interactjs/actions@1.10.2':
+ resolution: {integrity: sha512-BHJcW84WCMf/LsKmha/1Yog7aH3+QBXbLvowvZvwYvgjdUIb3xCa1a7FUYXuWAeKNMyKPVjFun+WPce75B+1tA==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/actions@1.10.27':
+ resolution: {integrity: sha512-FCRg5KwB+stkPcAMx/Cn0fgGP6p4LyMX9S/Upcn/W+hpYme31bPi54PCqmOebzz6myTthN6zFf9jMyLOqtI/gg==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/arrange@1.10.2':
+ resolution: {integrity: sha512-pPLA9o4RWMFN0VfalklOFSRLL4WqqXcD9no4XEuqV00goZPCxLBbMTztaWwnutlRy7evtOhUjUH+pZVsS+dZ4Q==}
+
+ '@interactjs/auto-scroll@1.10.2':
+ resolution: {integrity: sha512-yYqzOawwvWd1NNnlqZdzrXoOMFafQ2/ws85erpJqdaNMQE221z2uP+QYhFRLQRgYUlTbHFfmjDpzhuJgq4uA8Q==}
+ peerDependencies:
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/auto-scroll@1.10.27':
+ resolution: {integrity: sha512-zPg5TnVsZv+9Hnt4qnbxLvBMf+rIWHkoJVoSETEbLNaj90C8hIyr0pVwukSUySSgDhCgQ7np0f3pg4INLq9beQ==}
+ peerDependencies:
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/auto-start@1.10.2':
+ resolution: {integrity: sha512-nZudj8VzJzz+uEyDHqXwtKpvUYr+Oj1+xBrJEu21CywroHQWM2J4fCIiCgeCo3d5/p/TrzFk5b+YfAWePKiLxA==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/auto-start@1.10.27':
+ resolution: {integrity: sha512-ECLBO/nxmaF1knncJKIE5F7la3KKRgEkn0Cu2JTPOYj9xy/LpfYElo3wkRHsodgOqF651nR70GK2/IzPR2lO9A==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/clone@1.10.2':
+ resolution: {integrity: sha512-XzA8BRHSCwvysOegZ1kopg+IJF3erh4qzY6DRoZsIJovKAXawoa176E58IAzDbgYPJ2yoaSGT+XyzT2C0wa3pQ==}
+
+ '@interactjs/core@1.10.2':
+ resolution: {integrity: sha512-SA5KRGo+gFJOhBj1Z2dLHhAf0/2nyHNd4SQ460aIQ3jj/QhqbJW6kGzmh7hBa2FzVGgxLhcQu7NZaP4rnDfUNw==}
+ peerDependencies:
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/core@1.10.27':
+ resolution: {integrity: sha512-SliUr/3ZbLAdED8LokzYzWHWMdCB5Cq+UnpXuRy+BIod1j97m4IUFf/D1iIKUBBjBcucgXbz28z96WnenVCB7Q==}
+ peerDependencies:
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/dev-tools@1.10.2':
+ resolution: {integrity: sha512-aAd9NgTAGA3yVdFCYcAAYrM4TYQFuVqEvsF+xj+g5SlGyrJ7+GTjPZ2rScOyAsABY4Tz64L2pXvWmXMG87dncA==}
+
+ '@interactjs/dev-tools@1.10.27':
+ resolution: {integrity: sha512-YolmBwRaKH1gWbvyLeV3m5QSwtD38lOZnCBA87PCAlcd9PQAC2gb03fEPeEyD336bE20oLB8f0WZt4Wre+afiw==}
+ peerDependencies:
+ '@interactjs/modifiers': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/feedback@1.10.2':
+ resolution: {integrity: sha512-XlcoICGrFeUwwRtDgOpstOOvlU42WZoEg7gJHK3LwF7j0IctPd1+3blXofFlBeVvodle8MvUMepm5CRXz741fA==}
+
+ '@interactjs/inertia@1.10.2':
+ resolution: {integrity: sha512-ZmN1joN6J36Q6SOp3V0iZOisXZOBMSAUj0STo8wbwCKy7K8IrC9vjUBbO2JM52cT6o7hg5ebHsp5c8FrebSHlg==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/modifiers': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/inertia@1.10.27':
+ resolution: {integrity: sha512-S/SVj/M0D+wWWPVXHcXN/YUWOK51LFJsEA+CTgVnFhlSU04+1FUvNLwilCZcHgECu1RJxZNKDwZysDATg+r8jQ==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/modifiers': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/interact@1.10.2':
+ resolution: {integrity: sha512-Ms5uVCY9IobVYpQyBnBdkP6Bk6iDY7TkC7GupsdUPUxzAvYSQCTEAGr/1PwxSrSS6dN/8O8TuyUWPbCaylr/JA==}
+
+ '@interactjs/interact@1.10.27':
+ resolution: {integrity: sha512-XdH3A2UUzjEFGGJgFuJlhiz99tE8jB8xNh/DmnoMuL6uOQPxNA+sWRnzEVjG0+zY2P3/dbhEpi4Cn3FLPzydwA==}
+
+ '@interactjs/interactjs@1.10.2':
+ resolution: {integrity: sha512-OwLl70af6lfZOOg/bvWKSNm1DS1nDI72QnzDYljSKfc2D8stqLIGDO1wPY2rhZudUG5q3t50EhmMUQF76yll/g==}
+
+ '@interactjs/interactjs@1.10.27':
+ resolution: {integrity: sha512-UwhfUZMZVXUY72efPABuKSBz1sUY+r+49v8t6Ku9o5Jq76AKg9mwmdGszIlOn3ppnFDDjvtzK/8TL+Sbd0EQEA==}
+
+ '@interactjs/modifiers@1.10.2':
+ resolution: {integrity: sha512-3wYEucvZF2NTIslnVIKw5MWhkn9LM42cGCQaC19o3LZeWnbps7NnHJCyQp6zylJrCbwt7f+CSt4Oj2/s0f6XEA==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/modifiers@1.10.27':
+ resolution: {integrity: sha512-ei/qfoQ+9/8k6WzNzdNqHI6cWkIV576N4Ap16r5CoqOWwhA6Xzj3OMHf1g0t1O4eSq2HdJsVJn3eLNfw9HsbeQ==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/multi-target@1.10.2':
+ resolution: {integrity: sha512-O2GiIqgZBzjAVTOpL8doTnAcM9AtM3+H/Bb+An12wWKtNutVK7JbqUAO2nYueOk55/PP3yDLY9Qdr15RJns3lQ==}
+
+ '@interactjs/offset@1.10.2':
+ resolution: {integrity: sha512-xLgQqinFUY7ZqSX9d9on7XRcxvQdHNEAktj2QFwxMsEwrA6zbKROpPVwt8WQ1yBAeJSFjgYGcmCMPW5K41dT0w==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/offset@1.10.27':
+ resolution: {integrity: sha512-AezsLiuK+Qv4jXdYuRa65HJ2pMFMZPlqiAep6ZRLwhP9HE7O75c0EAm+gfx+dpPrHNHs6J9LaiKSZl+B+A2qAw==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/pointer-events@1.10.2':
+ resolution: {integrity: sha512-O8s3N399hkGIzWGlcJVy0LJyDn5YWDh6XKjyowh/QivtlZSWPY8eglmlN2uZX0lmiqUYghbKI4CpQYP/cE0ZDA==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/pointer-events@1.10.27':
+ resolution: {integrity: sha512-Yo5SS6PhWfC93gHNxnwwW0wvebo5hSYJKGaSnAHO4f9Lh25yibecMnmPBmiEfWVcdMboK/kXrme43mHQaRegVg==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/react@1.10.2':
+ resolution: {integrity: sha512-JXzPdANft+W2vq3SCSzprCwom5UuC8TaiAAhVdt8R+/P6xHbOeAX93XLS5YmDwT8e0Zh9J9jYvz55tkTdwjFZQ==}
+
+ '@interactjs/reflow@1.10.2':
+ resolution: {integrity: sha512-pc6o6RRhSCYQC4auZexRb7z5FQkdSVev5HzlRfUAjfw4C076qgbcs63ESRKy4YXdSBtUTvARQZxpuWUNGquzJw==}
+ peerDependencies:
+ '@interactjs/core': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/reflow@1.10.27':
+ resolution: {integrity: sha512-Msm0QdYFr40oSsPFxyCR3dHN/pQx34k7QSkdN1uIsUn/drrm+YSFvrvVOu99DFOwr7gTThr5vNe06Sz4vubTSA==}
+ peerDependencies:
+ '@interactjs/core': 1.10.27
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/snappers@1.10.2':
+ resolution: {integrity: sha512-wQ41Vn5GRn6VavjIEUtTkd9d5QgdKgC4+CPW0fjKkiQclLBmaic7VibNETO8twN0Jx5e73EoPj9K2nAVHIA0hA==}
+ peerDependencies:
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/snappers@1.10.27':
+ resolution: {integrity: sha512-HZLZ0XSi6HI08OmTv/HKG6AltQoaKAALLQ+KDW92utj3XSgw7oren0KsWUKPhaPg3Av7R1jFQd08s+uafqIlLw==}
+ peerDependencies:
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/types@1.10.2':
+ resolution: {integrity: sha512-l0T1bU8OHRv716ztQOYwP+K7b/lA76C0T3r/cdabbUv6CKeAFdFRRFlmNxYOM36SxMGWAiq5VWrN3SeXlB7Fow==}
+
+ '@interactjs/types@1.10.27':
+ resolution: {integrity: sha512-BUdv0cvs4H5ODuwft2Xp4eL8Vmi3LcihK42z0Ft/FbVJZoRioBsxH+LlsBdK4tAie7PqlKGy+1oyOncu1nQ6eA==}
+
+ '@interactjs/utils@1.10.2':
+ resolution: {integrity: sha512-sOr+pu7XGAN4qv+ikajMo3RJygbkbMLegmx0Tv5Qf6e80sF42FjkmHeMGuV7fL98nwat0VmDiWerOFBnKctXow==}
+
+ '@interactjs/utils@1.10.27':
+ resolution: {integrity: sha512-+qfLOio2OxQqg1cXSnRaCl+N8MQDQLDS9w+aOGxH8YLAhIMyt7Asxx/46//sT8orgsi16pmlBPtngPHT9s8zKw==}
+
+ '@interactjs/vue@1.10.2':
+ resolution: {integrity: sha512-msLdc42DFsCPQZt6YBGZrw8Ro23kQcNnj+iLz2OUQcOrp/lma7WjorUuAwwfyFna2DevLtiYlMLbT0dpO/cVhg==}
+
'@internationalized/date@3.5.5':
resolution: {integrity: sha512-H+CfYvOZ0LTJeeLOqm19E3uj/4YjrmOFtBufDHPfvtI80hFAMqtrp7oCACpe4Cil5l8S0Qu/9dYfZc/5lY8WQQ==}
@@ -4541,6 +4796,9 @@ packages:
'@types/jsonfile@6.1.4':
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
+ '@types/jsonwebtoken@9.0.7':
+ resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==}
+
'@types/lodash-es@4.17.12':
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
@@ -5270,6 +5528,9 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ batch-processor@1.0.0:
+ resolution: {integrity: sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==}
+
better-path-resolve@1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'}
@@ -5341,6 +5602,9 @@ packages:
resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==}
engines: {node: '>=8.0.0'}
+ buffer-equal-constant-time@1.0.1:
+ resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
@@ -6207,6 +6471,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+ ecdsa-sig-formatter@1.0.11:
+ resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+
echarts@5.5.1:
resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==}
@@ -6226,6 +6493,9 @@ packages:
electron-to-chromium@1.5.19:
resolution: {integrity: sha512-kpLJJi3zxTR1U828P+LIUDZ5ohixyo68/IcYOHLqnbTPr/wdgn4i1ECvmALN9E16JPA6cvCG5UG79gVwVdEK5w==}
+ element-resize-detector@1.2.4:
+ resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==}
+
emoji-regex@10.4.0:
resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
@@ -7234,6 +7504,9 @@ packages:
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ interactjs@1.10.27:
+ resolution: {integrity: sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA==}
+
internal-slot@1.0.7:
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
engines: {node: '>= 0.4'}
@@ -7650,12 +7923,22 @@ packages:
resolution: {integrity: sha512-idqReg23J0PVRAADmZMc5xQM3xeOX5bTB6OTyMnzq33IXJXmn9iJuWIEvGmrN80rQf4d7uLTMEDwpzujNcI0Rg==}
hasBin: true
+ jsonwebtoken@9.0.2:
+ resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
+ engines: {node: '>=12', npm: '>=6'}
+
jstoxml@2.2.9:
resolution: {integrity: sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==}
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
+ jwa@1.4.1:
+ resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==}
+
+ jws@3.2.2:
+ resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==}
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -7780,6 +8063,9 @@ packages:
lodash.groupby@4.6.0:
resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}
+ lodash.includes@4.3.0:
+ resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
+
lodash.isarguments@3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
@@ -7792,12 +8078,21 @@ packages:
lodash.isfunction@3.0.9:
resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
+ lodash.isinteger@4.0.4:
+ resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
+
lodash.isnil@4.0.0:
resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==}
+ lodash.isnumber@3.0.3:
+ resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
+
lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+ lodash.isstring@4.0.1:
+ resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
+
lodash.isundefined@3.0.1:
resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==}
@@ -7813,6 +8108,9 @@ packages:
lodash.mergewith@4.6.2:
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+ lodash.once@4.1.1:
+ resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
+
lodash.snakecase@4.1.1:
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
@@ -8040,6 +8338,9 @@ packages:
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
engines: {node: '>= 8'}
+ mitt@2.1.0:
+ resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==}
+
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
@@ -10585,6 +10886,12 @@ packages:
peerDependencies:
eslint: '>=6.0.0'
+ vue-grid-layout-v3@3.0.3:
+ resolution: {integrity: sha512-xM575WYjAXP7QiBwPi4nlJMfrjBySntk/HlwHOsmInUSr/sNu6jVj5JxugDI59gGFvgokIfHtRZU8jYL3EonaA==}
+
+ vue-grid-layout@3.0.0-beta1:
+ resolution: {integrity: sha512-MsW0yfYNtnAO/uDhfZvkP6effxSJxvhAFbIL37x6Rn3vW9xf0WHVefKaSbQMLpSq3mXnR6ut0pg2Cd5lqIIZzg==}
+
vue-i18n@9.14.0:
resolution: {integrity: sha512-LxmpRuCt2rI8gqU+kxeflRZMQn4D5+4M3oP3PWZdowW/ePJraHqhF7p4CuaME52mUxdw3Mmy2yAUKgfZYgCRjA==}
engines: {node: '>= 16'}
@@ -10608,6 +10915,9 @@ packages:
peerDependencies:
vue: 3.5.4
+ vue3-grid-layout-next@1.0.7:
+ resolution: {integrity: sha512-0MPPqF+CiOrDZZ1SwyOfsere7VpKXh/KIev4N09cMWio5uf7leM3McvZW0cJO/jOWC1thU26rmWnXjiKThIoxg==}
+
vue@3.5.4:
resolution: {integrity: sha512-3yAj2gkmiY+i7+22A1PWM+kjOVXjU74UPINcTiN7grIVPyFFI0lpGwHlV/4xydDmobaBn7/xmi+YG8HeSlCTcg==}
peerDependencies:
@@ -13562,6 +13872,251 @@ snapshots:
'@iconify/types': 2.0.0
vue: 3.5.4(typescript@5.6.2)
+ '@interactjs/actions@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/actions@1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/actions@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/arrange@1.10.2': {}
+
+ '@interactjs/auto-scroll@1.10.2(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/auto-scroll@1.10.27(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/auto-start@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/auto-start@1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/auto-start@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/clone@1.10.2': {}
+
+ '@interactjs/core@1.10.2(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/core@1.10.27(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/dev-tools@1.10.2':
+ dependencies:
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/dev-tools@1.10.27(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)(typescript@5.6.2)':
+ dependencies:
+ '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+ vue: 3.5.4(typescript@5.6.2)
+ transitivePeerDependencies:
+ - typescript
+
+ '@interactjs/feedback@1.10.2': {}
+
+ '@interactjs/inertia@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/offset': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/inertia@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/offset': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/interact@1.10.2':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/types': 1.10.2
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/interact@1.10.27':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/interactjs@1.10.2':
+ dependencies:
+ '@interactjs/actions': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/arrange': 1.10.2
+ '@interactjs/auto-scroll': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/auto-start': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/clone': 1.10.2
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/dev-tools': 1.10.2
+ '@interactjs/feedback': 1.10.2
+ '@interactjs/inertia': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/interact': 1.10.2
+ '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/multi-target': 1.10.2
+ '@interactjs/offset': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/pointer-events': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/react': 1.10.2
+ '@interactjs/reflow': 1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ '@interactjs/vue': 1.10.2
+
+ '@interactjs/interactjs@1.10.27(typescript@5.6.2)':
+ dependencies:
+ '@interactjs/actions': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/auto-scroll': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/auto-start': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/dev-tools': 1.10.27(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)(typescript@5.6.2)
+ '@interactjs/inertia': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/interact': 1.10.27
+ '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/offset': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/pointer-events': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/reflow': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ transitivePeerDependencies:
+ - typescript
+
+ '@interactjs/modifiers@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/snappers': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/modifiers@1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/snappers': 1.10.2(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/snappers': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/multi-target@1.10.2': {}
+
+ '@interactjs/offset@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/offset@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/pointer-events@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/pointer-events@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/react@1.10.2': {}
+
+ '@interactjs/reflow@1.10.2(@interactjs/core@1.10.2(@interactjs/utils@1.10.2))(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/core': 1.10.2(@interactjs/utils@1.10.2)
+ '@interactjs/utils': 1.10.2
+ optionalDependencies:
+ '@interactjs/interact': 1.10.2
+
+ '@interactjs/reflow@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/snappers@1.10.2(@interactjs/utils@1.10.2)':
+ dependencies:
+ '@interactjs/utils': 1.10.2
+
+ '@interactjs/snappers@1.10.2(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/utils': 1.10.27
+
+ '@interactjs/snappers@1.10.27(@interactjs/utils@1.10.27)':
+ dependencies:
+ '@interactjs/utils': 1.10.27
+ optionalDependencies:
+ '@interactjs/interact': 1.10.27
+
+ '@interactjs/types@1.10.2': {}
+
+ '@interactjs/types@1.10.27': {}
+
+ '@interactjs/utils@1.10.2': {}
+
+ '@interactjs/utils@1.10.27': {}
+
+ '@interactjs/vue@1.10.2': {}
+
'@internationalized/date@3.5.5':
dependencies:
'@swc/helpers': 0.5.13
@@ -14587,6 +15142,10 @@ snapshots:
'@types/node': 22.5.4
optional: true
+ '@types/jsonwebtoken@9.0.7':
+ dependencies:
+ '@types/node': 22.5.4
+
'@types/lodash-es@4.17.12':
dependencies:
'@types/lodash': 4.17.7
@@ -15567,6 +16126,8 @@ snapshots:
base64-js@1.5.1: {}
+ batch-processor@1.0.0: {}
+
better-path-resolve@1.0.0:
dependencies:
is-windows: 1.0.2
@@ -15646,6 +16207,8 @@ snapshots:
buffer-crc32@1.0.0: {}
+ buffer-equal-constant-time@1.0.1: {}
+
buffer-from@1.1.2: {}
buffer-indexof-polyfill@1.0.2: {}
@@ -16586,6 +17149,10 @@ snapshots:
eastasianwidth@0.2.0: {}
+ ecdsa-sig-formatter@1.0.11:
+ dependencies:
+ safe-buffer: 5.2.1
+
echarts@5.5.1:
dependencies:
tslib: 2.3.0
@@ -16606,6 +17173,10 @@ snapshots:
electron-to-chromium@1.5.19: {}
+ element-resize-detector@1.2.4:
+ dependencies:
+ batch-processor: 1.0.0
+
emoji-regex@10.4.0: {}
emoji-regex@8.0.0: {}
@@ -17871,6 +18442,10 @@ snapshots:
ini@4.1.1: {}
+ interactjs@1.10.27:
+ dependencies:
+ '@interactjs/types': 1.10.27
+
internal-slot@1.0.7:
dependencies:
es-errors: 1.3.0
@@ -18245,6 +18820,19 @@ snapshots:
jsonrepair@3.1.0: {}
+ jsonwebtoken@9.0.2:
+ dependencies:
+ jws: 3.2.2
+ lodash.includes: 4.3.0
+ lodash.isboolean: 3.0.3
+ lodash.isinteger: 4.0.4
+ lodash.isnumber: 3.0.3
+ lodash.isplainobject: 4.0.6
+ lodash.isstring: 4.0.1
+ lodash.once: 4.1.1
+ ms: 2.1.3
+ semver: 7.6.3
+
jstoxml@2.2.9: {}
jszip@3.10.1:
@@ -18254,6 +18842,17 @@ snapshots:
readable-stream: 2.3.8
setimmediate: 1.0.5
+ jwa@1.4.1:
+ dependencies:
+ buffer-equal-constant-time: 1.0.1
+ ecdsa-sig-formatter: 1.0.11
+ safe-buffer: 5.2.1
+
+ jws@3.2.2:
+ dependencies:
+ jwa: 1.4.1
+ safe-buffer: 5.2.1
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@@ -18398,6 +18997,8 @@ snapshots:
lodash.groupby@4.6.0: {}
+ lodash.includes@4.3.0: {}
+
lodash.isarguments@3.1.0: {}
lodash.isboolean@3.0.3: {}
@@ -18406,10 +19007,16 @@ snapshots:
lodash.isfunction@3.0.9: {}
+ lodash.isinteger@4.0.4: {}
+
lodash.isnil@4.0.0: {}
+ lodash.isnumber@3.0.3: {}
+
lodash.isplainobject@4.0.6: {}
+ lodash.isstring@4.0.1: {}
+
lodash.isundefined@3.0.1: {}
lodash.kebabcase@4.1.1: {}
@@ -18420,6 +19027,8 @@ snapshots:
lodash.mergewith@4.6.2: {}
+ lodash.once@4.1.1: {}
+
lodash.snakecase@4.1.1: {}
lodash.sortby@4.7.0: {}
@@ -18637,6 +19246,8 @@ snapshots:
minipass: 3.3.6
yallist: 4.0.0
+ mitt@2.1.0: {}
+
mitt@3.0.1: {}
mkdirp@0.5.6:
@@ -21440,6 +22051,35 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ vue-grid-layout-v3@3.0.3(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)(typescript@5.6.2):
+ dependencies:
+ '@interactjs/actions': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/auto-scroll': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/auto-start': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/dev-tools': 1.10.27(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)(typescript@5.6.2)
+ '@interactjs/interact': 1.10.27
+ '@interactjs/interactjs': 1.10.27(typescript@5.6.2)
+ '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ element-resize-detector: 1.2.4
+ mitt: 3.0.1
+ transitivePeerDependencies:
+ - '@interactjs/core'
+ - '@interactjs/utils'
+ - typescript
+
+ vue-grid-layout@3.0.0-beta1(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27):
+ dependencies:
+ '@interactjs/actions': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/auto-start': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/dev-tools': 1.10.2
+ '@interactjs/interactjs': 1.10.2
+ '@interactjs/modifiers': 1.10.2(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ element-resize-detector: 1.2.4
+ mitt: 2.1.0
+ transitivePeerDependencies:
+ - '@interactjs/core'
+ - '@interactjs/utils'
+
vue-i18n@9.14.0(vue@3.5.4(typescript@5.6.2)):
dependencies:
'@intlify/core-base': 9.14.0
@@ -21464,6 +22104,22 @@ snapshots:
is-plain-object: 3.0.1
vue: 3.5.4(typescript@5.6.2)
+ vue3-grid-layout-next@1.0.7(typescript@5.6.2):
+ dependencies:
+ '@interactjs/actions': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/auto-scroll': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/auto-start': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/core': 1.10.27(@interactjs/utils@1.10.27)
+ '@interactjs/dev-tools': 1.10.27(@interactjs/modifiers@1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)(typescript@5.6.2)
+ '@interactjs/interact': 1.10.27
+ '@interactjs/modifiers': 1.10.27(@interactjs/core@1.10.27(@interactjs/utils@1.10.27))(@interactjs/utils@1.10.27)
+ '@interactjs/utils': 1.10.27
+ element-resize-detector: 1.2.4
+ interactjs: 1.10.27
+ mitt: 3.0.1
+ transitivePeerDependencies:
+ - typescript
+
vue@3.5.4(typescript@5.6.2):
dependencies:
'@vue/compiler-dom': 3.5.4
diff --git a/vben-admin.code-workspace b/vben-admin.code-workspace
index f12c5c07..c3f06b00 100644
--- a/vben-admin.code-workspace
+++ b/vben-admin.code-workspace
@@ -1,5 +1,9 @@
{
"folders": [
+ {
+ "name": "@vben/backend-mock",
+ "path": "apps/backend-mock",
+ },
{
"name": "@vben/web-antd",
"path": "apps/web-antd",
@@ -16,6 +20,10 @@
"name": "web-office",
"path": "apps/web-office",
},
+ {
+ "name": "web-test",
+ "path": "apps/web-test",
+ },
{
"name": "common-utils",
"path": "internal/common-utils",