From 06ae5720982a15fb2230aacac1154c366418c791 Mon Sep 17 00:00:00 2001 From: z9130 <984661593@qq.com> Date: Thu, 19 Sep 2024 16:46:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +- apps/backend-mock/.env | 3 + apps/backend-mock/README.md | 15 + apps/backend-mock/api/auth/codes.ts | 14 + apps/backend-mock/api/auth/login.post.ts | 36 + apps/backend-mock/api/auth/logout.post.ts | 15 + apps/backend-mock/api/auth/refresh.post.ts | 33 + apps/backend-mock/api/menu/all.ts | 13 + apps/backend-mock/api/status.ts | 5 + apps/backend-mock/api/test.get.ts | 1 + apps/backend-mock/api/test.post.ts | 1 + apps/backend-mock/api/user/info.ts | 11 + apps/backend-mock/error.ts | 7 + apps/backend-mock/middleware/1.api.ts | 7 + apps/backend-mock/nitro.config.ts | 18 + apps/backend-mock/package.json | 20 + apps/backend-mock/routes/[...].ts | 12 + apps/backend-mock/tsconfig.build.json | 4 + apps/backend-mock/tsconfig.json | 3 + apps/backend-mock/utils/cookie-utils.ts | 26 + apps/backend-mock/utils/jwt-utils.ts | 59 ++ apps/backend-mock/utils/mock-data.ts | 186 ++++ apps/backend-mock/utils/response.ts | 29 + .../src/router/routes/modules/contract.ts | 815 +++++++++--------- apps/web-contract/src/utils/dict/shared.ts | 6 +- .../src/utils/dict/static.data.js | 5 +- .../src/views/contract/approval/edit/curd.tsx | 27 +- .../src/views/contract/company/edit/curd.tsx | 37 +- .../src/views/contract/company/edit/index.vue | 192 ++--- .../views/contract/declaration/edit/curd.tsx | 9 - .../info-approval/info-approval.vue | 51 +- .../perform/edit/components/break/curd.tsx | 0 .../perform/edit/components/break/index.vue | 3 + .../perform/edit/components/change/curd.tsx | 0 .../perform/edit/components/change/index.vue | 3 + .../perform/edit/components/payment/curd.tsx | 0 .../perform/edit/components/payment/index.vue | 3 + .../perform/edit/components/relieve/curd.tsx | 0 .../perform/edit/components/relieve/index.vue | 3 + .../src/views/contract/perform/edit/index.vue | 68 ++ .../src/views/contract/perform/list/index.vue | 194 +++-- apps/web-contract/vite.config.mts | 53 +- apps/web-test/.env | 5 + apps/web-test/.env.analyze | 7 + apps/web-test/.env.development | 16 + apps/web-test/.env.production | 19 + apps/web-test/index.html | 35 + apps/web-test/package.json | 53 ++ apps/web-test/postcss.config.mjs | 1 + apps/web-test/public/favicon.ico | Bin 0 -> 5430 bytes apps/web-test/src/adapter/form.ts | 114 +++ apps/web-test/src/adapter/index.ts | 1 + apps/web-test/src/api/core/auth.ts | 55 ++ apps/web-test/src/api/core/index.ts | 3 + apps/web-test/src/api/core/menu.ts | 10 + apps/web-test/src/api/core/user.ts | 10 + apps/web-test/src/api/index.ts | 1 + apps/web-test/src/api/request.ts | 104 +++ apps/web-test/src/app.vue | 39 + apps/web-test/src/bootstrap.ts | 35 + .../web-test/src/components/Output1/index.vue | 3 + .../src/components/Output1/src/index.vue | 3 + apps/web-test/src/layouts/auth.vue | 23 + apps/web-test/src/layouts/basic.vue | 140 +++ apps/web-test/src/layouts/index.ts | 6 + apps/web-test/src/locales/README.md | 3 + apps/web-test/src/locales/index.ts | 94 ++ apps/web-test/src/locales/langs/en-US.json | 8 + apps/web-test/src/locales/langs/zh-CN.json | 8 + apps/web-test/src/main.ts | 31 + apps/web-test/src/preferences.ts | 33 + apps/web-test/src/router/access.ts | 42 + apps/web-test/src/router/guard.ts | 137 +++ apps/web-test/src/router/index.ts | 37 + apps/web-test/src/router/routes/core.ts | 86 ++ apps/web-test/src/router/routes/index.ts | 31 + .../src/router/routes/modules/dashboard.ts | 49 ++ .../src/router/routes/modules/demos.ts | 30 + .../src/router/routes/modules/vben.ts | 81 ++ apps/web-test/src/store/auth.ts | 112 +++ apps/web-test/src/store/index.ts | 1 + apps/web-test/src/views/_core/README.md | 3 + apps/web-test/src/views/_core/about/index.vue | 9 + .../views/_core/authentication/code-login.vue | 64 ++ .../_core/authentication/forget-password.vue | 42 + .../src/views/_core/authentication/login.vue | 91 ++ .../_core/authentication/qrcode-login.vue | 10 + .../views/_core/authentication/register.vue | 101 +++ .../src/views/_core/fallback/coming-soon.vue | 7 + .../src/views/_core/fallback/forbidden.vue | 9 + .../views/_core/fallback/internal-error.vue | 9 + .../src/views/_core/fallback/not-found.vue | 9 + .../src/views/_core/fallback/offline.vue | 9 + .../dashboard/analytics/analytics-trends.vue | 100 +++ .../analytics/analytics-visits-data.vue | 84 ++ .../analytics/analytics-visits-sales.vue | 48 ++ .../analytics/analytics-visits-source.vue | 67 ++ .../dashboard/analytics/analytics-visits.vue | 57 ++ .../src/views/dashboard/analytics/index.vue | 90 ++ .../home/components/Output1/index.vue | 3 + .../home/components/Output2/index.vue | 3 + .../home/components/Output3/index.vue | 3 + .../src/views/dashboard/home/index.vue | 338 ++++++++ .../src/views/dashboard/workspace/index.vue | 229 +++++ apps/web-test/src/views/demos/antd/index.vue | 66 ++ apps/web-test/tailwind.config.mjs | 1 + apps/web-test/tsconfig.json | 12 + apps/web-test/tsconfig.node.json | 10 + apps/web-test/vite.config.mts | 20 + .../src/views/system/dict/crud.tsx | 77 ++ .../system/dict/dict-data-edit-modal.vue | 0 .../system/dict/dict-type-edit-modal.vue | 0 .../src/views/system/dict/index.vue | 0 .../src/configs/perfectionist.ts | 31 +- pnpm-lock.yaml | 656 ++++++++++++++ vben-admin.code-workspace | 8 + 116 files changed, 5041 insertions(+), 681 deletions(-) create mode 100644 apps/backend-mock/.env create mode 100644 apps/backend-mock/README.md create mode 100644 apps/backend-mock/api/auth/codes.ts create mode 100644 apps/backend-mock/api/auth/login.post.ts create mode 100644 apps/backend-mock/api/auth/logout.post.ts create mode 100644 apps/backend-mock/api/auth/refresh.post.ts create mode 100644 apps/backend-mock/api/menu/all.ts create mode 100644 apps/backend-mock/api/status.ts create mode 100644 apps/backend-mock/api/test.get.ts create mode 100644 apps/backend-mock/api/test.post.ts create mode 100644 apps/backend-mock/api/user/info.ts create mode 100644 apps/backend-mock/error.ts create mode 100644 apps/backend-mock/middleware/1.api.ts create mode 100644 apps/backend-mock/nitro.config.ts create mode 100644 apps/backend-mock/package.json create mode 100644 apps/backend-mock/routes/[...].ts create mode 100644 apps/backend-mock/tsconfig.build.json create mode 100644 apps/backend-mock/tsconfig.json create mode 100644 apps/backend-mock/utils/cookie-utils.ts create mode 100644 apps/backend-mock/utils/jwt-utils.ts create mode 100644 apps/backend-mock/utils/mock-data.ts create mode 100644 apps/backend-mock/utils/response.ts create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/break/curd.tsx create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/break/index.vue create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/change/curd.tsx create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/change/index.vue create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/payment/curd.tsx create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/payment/index.vue create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/relieve/curd.tsx create mode 100644 apps/web-contract/src/views/contract/perform/edit/components/relieve/index.vue create mode 100644 apps/web-contract/src/views/contract/perform/edit/index.vue create mode 100644 apps/web-test/.env create mode 100644 apps/web-test/.env.analyze create mode 100644 apps/web-test/.env.development create mode 100644 apps/web-test/.env.production create mode 100644 apps/web-test/index.html create mode 100644 apps/web-test/package.json create mode 100644 apps/web-test/postcss.config.mjs create mode 100644 apps/web-test/public/favicon.ico create mode 100644 apps/web-test/src/adapter/form.ts create mode 100644 apps/web-test/src/adapter/index.ts create mode 100644 apps/web-test/src/api/core/auth.ts create mode 100644 apps/web-test/src/api/core/index.ts create mode 100644 apps/web-test/src/api/core/menu.ts create mode 100644 apps/web-test/src/api/core/user.ts create mode 100644 apps/web-test/src/api/index.ts create mode 100644 apps/web-test/src/api/request.ts create mode 100644 apps/web-test/src/app.vue create mode 100644 apps/web-test/src/bootstrap.ts create mode 100644 apps/web-test/src/components/Output1/index.vue create mode 100644 apps/web-test/src/components/Output1/src/index.vue create mode 100644 apps/web-test/src/layouts/auth.vue create mode 100644 apps/web-test/src/layouts/basic.vue create mode 100644 apps/web-test/src/layouts/index.ts create mode 100644 apps/web-test/src/locales/README.md create mode 100644 apps/web-test/src/locales/index.ts create mode 100644 apps/web-test/src/locales/langs/en-US.json create mode 100644 apps/web-test/src/locales/langs/zh-CN.json create mode 100644 apps/web-test/src/main.ts create mode 100644 apps/web-test/src/preferences.ts create mode 100644 apps/web-test/src/router/access.ts create mode 100644 apps/web-test/src/router/guard.ts create mode 100644 apps/web-test/src/router/index.ts create mode 100644 apps/web-test/src/router/routes/core.ts create mode 100644 apps/web-test/src/router/routes/index.ts create mode 100644 apps/web-test/src/router/routes/modules/dashboard.ts create mode 100644 apps/web-test/src/router/routes/modules/demos.ts create mode 100644 apps/web-test/src/router/routes/modules/vben.ts create mode 100644 apps/web-test/src/store/auth.ts create mode 100644 apps/web-test/src/store/index.ts create mode 100644 apps/web-test/src/views/_core/README.md create mode 100644 apps/web-test/src/views/_core/about/index.vue create mode 100644 apps/web-test/src/views/_core/authentication/code-login.vue create mode 100644 apps/web-test/src/views/_core/authentication/forget-password.vue create mode 100644 apps/web-test/src/views/_core/authentication/login.vue create mode 100644 apps/web-test/src/views/_core/authentication/qrcode-login.vue create mode 100644 apps/web-test/src/views/_core/authentication/register.vue create mode 100644 apps/web-test/src/views/_core/fallback/coming-soon.vue create mode 100644 apps/web-test/src/views/_core/fallback/forbidden.vue create mode 100644 apps/web-test/src/views/_core/fallback/internal-error.vue create mode 100644 apps/web-test/src/views/_core/fallback/not-found.vue create mode 100644 apps/web-test/src/views/_core/fallback/offline.vue create mode 100644 apps/web-test/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 apps/web-test/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 apps/web-test/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 apps/web-test/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 apps/web-test/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 apps/web-test/src/views/dashboard/analytics/index.vue create mode 100644 apps/web-test/src/views/dashboard/home/components/Output1/index.vue create mode 100644 apps/web-test/src/views/dashboard/home/components/Output2/index.vue create mode 100644 apps/web-test/src/views/dashboard/home/components/Output3/index.vue create mode 100644 apps/web-test/src/views/dashboard/home/index.vue create mode 100644 apps/web-test/src/views/dashboard/workspace/index.vue create mode 100644 apps/web-test/src/views/demos/antd/index.vue create mode 100644 apps/web-test/tailwind.config.mjs create mode 100644 apps/web-test/tsconfig.json create mode 100644 apps/web-test/tsconfig.node.json create mode 100644 apps/web-test/vite.config.mts create mode 100644 internal/common-utils/src/views/system/dict/crud.tsx create mode 100644 internal/common-utils/src/views/system/dict/dict-data-edit-modal.vue create mode 100644 internal/common-utils/src/views/system/dict/dict-type-edit-modal.vue create mode 100644 internal/common-utils/src/views/system/dict/index.vue 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 @@