提交格式验证修改
This commit is contained in:
@@ -37,7 +37,6 @@
|
||||
"fmt": "oxfmt",
|
||||
"gen-route": "sa gen-route",
|
||||
"lint": "oxlint --fix && eslint --fix .",
|
||||
"prepare": "simple-git-hooks",
|
||||
"preview": "vite preview",
|
||||
"release": "sa release",
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck",
|
||||
@@ -108,7 +107,6 @@
|
||||
"oxlint": "^1.64.0",
|
||||
"pro-naive-ui-resolver": "1.0.2",
|
||||
"sass": "1.99.0",
|
||||
"simple-git-hooks": "2.13.1",
|
||||
"tsx": "4.21.0",
|
||||
"typescript": "6.0.3",
|
||||
"unocss": "^66.6.8",
|
||||
@@ -122,10 +120,6 @@
|
||||
"vue-eslint-parser": "10.4.0",
|
||||
"vue-tsc": "3.2.8"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm sa git-commit-verify",
|
||||
"pre-commit": "pnpm typecheck && pnpm lint && pnpm fmt && git diff --exit-code"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0",
|
||||
"pnpm": ">=10.5.0"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@sa/alova",
|
||||
"version": "2.2.0",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./fetch": "./src/fetch.ts",
|
||||
"./client": "./src/client.ts",
|
||||
"./mock": "./src/mock.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alova/mock": "2.0.20",
|
||||
"@sa/utils": "workspace:*",
|
||||
"alova": "3.5.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from 'alova/client';
|
||||
@@ -0,0 +1,2 @@
|
||||
/** the backend error code key */
|
||||
export const BACKEND_ERROR_CODE = 'BACKEND_ERROR';
|
||||
@@ -0,0 +1,2 @@
|
||||
import adapterFetch from 'alova/fetch';
|
||||
export default adapterFetch;
|
||||
@@ -0,0 +1,77 @@
|
||||
import { createAlova } from 'alova';
|
||||
import type { AlovaDefaultCacheAdapter, AlovaGenerics, AlovaGlobalCacheAdapter, AlovaRequestAdapter } from 'alova';
|
||||
import VueHook from 'alova/vue';
|
||||
import type { VueHookType } from 'alova/vue';
|
||||
import adapterFetch from 'alova/fetch';
|
||||
import { createServerTokenAuthentication } from 'alova/client';
|
||||
import type { FetchRequestInit } from 'alova/fetch';
|
||||
import { BACKEND_ERROR_CODE } from './constant';
|
||||
import type { CustomAlovaConfig, RequestOptions } from './type';
|
||||
|
||||
export const createAlovaRequest = <
|
||||
RequestConfig = FetchRequestInit,
|
||||
ResponseType = Response,
|
||||
ResponseHeader = Headers,
|
||||
L1Cache extends AlovaGlobalCacheAdapter = AlovaDefaultCacheAdapter,
|
||||
L2Cache extends AlovaGlobalCacheAdapter = AlovaDefaultCacheAdapter
|
||||
>(
|
||||
customConfig: CustomAlovaConfig<
|
||||
AlovaGenerics<any, any, RequestConfig, ResponseType, ResponseHeader, L1Cache, L2Cache, any>
|
||||
>,
|
||||
options: RequestOptions<AlovaGenerics<any, any, RequestConfig, ResponseType, ResponseHeader, L1Cache, L2Cache, any>>
|
||||
) => {
|
||||
const { tokenRefresher } = options;
|
||||
const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
|
||||
VueHookType,
|
||||
AlovaRequestAdapter<RequestConfig, ResponseType, ResponseHeader>
|
||||
>({
|
||||
refreshTokenOnSuccess: {
|
||||
isExpired: (response, method) => tokenRefresher?.isExpired(response, method) || false,
|
||||
handler: async (response, method) => tokenRefresher?.handler(response, method)
|
||||
},
|
||||
refreshTokenOnError: {
|
||||
isExpired: (response, method) => tokenRefresher?.isExpired(response, method) || false,
|
||||
handler: async (response, method) => tokenRefresher?.handler(response, method)
|
||||
}
|
||||
});
|
||||
|
||||
const instance = createAlova({
|
||||
...customConfig,
|
||||
timeout: customConfig.timeout ?? 10 * 1000,
|
||||
requestAdapter: (customConfig.requestAdapter as any) ?? adapterFetch(),
|
||||
statesHook: VueHook,
|
||||
beforeRequest: onAuthRequired(options.onRequest as any),
|
||||
responded: onResponseRefreshToken({
|
||||
onSuccess: async (response, method) => {
|
||||
// check if http status is success
|
||||
let error: any = null;
|
||||
let transformedData: any = null;
|
||||
try {
|
||||
if (await options.isBackendSuccess(response)) {
|
||||
transformedData = await options.transformBackendResponse(response);
|
||||
} else {
|
||||
error = new Error('the backend request error');
|
||||
error.code = BACKEND_ERROR_CODE;
|
||||
}
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
await options.onError?.(error, response, method);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return transformedData;
|
||||
},
|
||||
onComplete: options.onComplete,
|
||||
onError: (error, method) => options.onError?.(error, null, method)
|
||||
})
|
||||
});
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
export { BACKEND_ERROR_CODE };
|
||||
export type * from './type';
|
||||
export type * from 'alova';
|
||||
@@ -0,0 +1 @@
|
||||
export * from '@alova/mock';
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { AlovaGenerics, AlovaOptions, AlovaRequestAdapter, Method, ResponseCompleteHandler } from 'alova';
|
||||
|
||||
export type CustomAlovaConfig<AG extends AlovaGenerics> = Omit<
|
||||
AlovaOptions<AG>,
|
||||
'statesHook' | 'beforeRequest' | 'responded' | 'requestAdapter'
|
||||
> & {
|
||||
/** request adapter. all request of alova will be sent by it. */
|
||||
requestAdapter?: AlovaRequestAdapter<AG['RequestConfig'], AG['Response'], AG['ResponseHeader']>;
|
||||
};
|
||||
|
||||
export interface RequestOptions<AG extends AlovaGenerics> {
|
||||
/**
|
||||
* The hook before request
|
||||
*
|
||||
* For example: You can add header token in this hook
|
||||
*
|
||||
* @param method alova Method Instance
|
||||
*/
|
||||
onRequest?: AlovaOptions<AG>['beforeRequest'];
|
||||
/**
|
||||
* The hook to check backend response is success or not
|
||||
*
|
||||
* @param response alova response
|
||||
*/
|
||||
isBackendSuccess: (response: AG['Response']) => Promise<boolean>;
|
||||
|
||||
/** The config to refresh token */
|
||||
tokenRefresher?: {
|
||||
/** detect the token is expired */
|
||||
isExpired(response: AG['Response'], Method: Method<AG>): Promise<boolean> | boolean;
|
||||
/** refresh token handler */
|
||||
handler(response: AG['Response'], Method: Method<AG>): Promise<void>;
|
||||
};
|
||||
|
||||
/** The hook after backend request complete */
|
||||
onComplete?: ResponseCompleteHandler<AG>;
|
||||
|
||||
/**
|
||||
* The hook to handle error
|
||||
*
|
||||
* For example: You can show error message in this hook
|
||||
*
|
||||
* @param error
|
||||
*/
|
||||
onError?: (error: any, response: AG['Response'] | null, methodInstance: Method<AG>) => any | Promise<any>;
|
||||
/**
|
||||
* transform backend response when the responseType is json
|
||||
*
|
||||
* @param response alova response
|
||||
*/
|
||||
transformBackendResponse: (response: AG['Response']) => any;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"types": ["node"],
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Generated
-9
@@ -195,9 +195,6 @@ importers:
|
||||
sass:
|
||||
specifier: 1.99.0
|
||||
version: 1.99.0
|
||||
simple-git-hooks:
|
||||
specifier: 2.13.1
|
||||
version: 2.13.1
|
||||
tsx:
|
||||
specifier: 4.21.0
|
||||
version: 4.21.0
|
||||
@@ -4705,10 +4702,6 @@ packages:
|
||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
simple-git-hooks@2.13.1:
|
||||
resolution: {integrity: sha512-WszCLXwT4h2k1ufIXAgsbiTOazqqevFCIncOuUBZJ91DdvWcC5+OFkluWRQPrcuSYd8fjq+o2y1QfWqYMoAToQ==}
|
||||
hasBin: true
|
||||
|
||||
simple-statistics@6.1.0:
|
||||
resolution: {integrity: sha512-Vi/xPPuiEIizXOCDx3N8LjcIqC7Z8euiIfvmj5oYimm9/KmXNKeK4aHiVMeE9q7e9wfg1i9YRoWnUNURq54ZFg==}
|
||||
|
||||
@@ -10139,8 +10132,6 @@ snapshots:
|
||||
|
||||
signal-exit@4.1.0: {}
|
||||
|
||||
simple-git-hooks@2.13.1: {}
|
||||
|
||||
simple-statistics@6.1.0: {}
|
||||
|
||||
simple-statistics@7.8.9: {}
|
||||
|
||||
@@ -12,10 +12,6 @@ export const themeSchemaRecord: Record<UnionKey.ThemeScheme, App.I18n.I18nKey> =
|
||||
|
||||
export const themeSchemaOptions = transformRecordToOption(themeSchemaRecord);
|
||||
|
||||
export const loginModuleRecord: Record<UnionKey.LoginModule, App.I18n.I18nKey> = {
|
||||
'pwd-login': 'page.login.pwdLogin.title',
|
||||
};
|
||||
|
||||
export const themeLayoutModeRecord: Record<UnionKey.ThemeLayoutMode, App.I18n.I18nKey> = {
|
||||
vertical: 'theme.layout.layoutMode.vertical',
|
||||
'vertical-mix': 'theme.layout.layoutMode.vertical-mix',
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import { computed } from 'vue';
|
||||
import { useCountDown, useLoading } from '@sa/hooks';
|
||||
import { REG_PHONE } from '@/constants/reg';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
export function useCaptcha() {
|
||||
const { loading, startLoading, endLoading } = useLoading();
|
||||
const { count, start, stop, isCounting } = useCountDown(10);
|
||||
|
||||
const label = computed(() => {
|
||||
let text = $t('page.login.codeLogin.getCode');
|
||||
|
||||
const countingLabel = $t('page.login.codeLogin.reGetCode', { time: count.value });
|
||||
|
||||
if (loading.value) {
|
||||
text = '';
|
||||
}
|
||||
|
||||
if (isCounting.value) {
|
||||
text = countingLabel;
|
||||
}
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
function isPhoneValid(phone: string) {
|
||||
if (phone.trim() === '') {
|
||||
window.$message?.error?.($t('form.phone.required'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!REG_PHONE.test(phone)) {
|
||||
window.$message?.error?.($t('form.phone.invalid'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getCaptcha(phone: string) {
|
||||
const valid = isPhoneValid(phone);
|
||||
|
||||
if (!valid || loading.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
startLoading();
|
||||
|
||||
// request
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 500);
|
||||
});
|
||||
|
||||
window.$message?.success?.($t('page.login.codeLogin.sendCodeSuccess'));
|
||||
|
||||
start();
|
||||
|
||||
endLoading();
|
||||
}
|
||||
|
||||
return {
|
||||
label,
|
||||
start,
|
||||
stop,
|
||||
isCounting,
|
||||
loading,
|
||||
getCaptcha
|
||||
};
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import type { VNode } from 'vue';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { useSvgIcon } from '@/hooks/common/icon';
|
||||
import { $t } from '@/locales';
|
||||
|
||||
defineOptions({
|
||||
name: 'UserAvatar'
|
||||
@@ -34,7 +33,7 @@ type DropdownOption =
|
||||
const options = computed(() => {
|
||||
const opts: DropdownOption[] = [
|
||||
{
|
||||
label: $t('common.userCenter'),
|
||||
label: '个人中心',
|
||||
key: 'user-center',
|
||||
icon: SvgIconVNode({ icon: 'ph:user-circle', fontSize: 18 })
|
||||
},
|
||||
@@ -43,7 +42,7 @@ const options = computed(() => {
|
||||
key: 'divider'
|
||||
},
|
||||
{
|
||||
label: $t('common.logout'),
|
||||
label: '退出登录',
|
||||
key: 'logout',
|
||||
icon: SvgIconVNode({ icon: 'ph:sign-out', fontSize: 18 })
|
||||
}
|
||||
@@ -54,10 +53,10 @@ const options = computed(() => {
|
||||
|
||||
function logout() {
|
||||
window.$dialog?.info({
|
||||
title: $t('common.tip'),
|
||||
content: $t('common.logoutConfirm'),
|
||||
positiveText: $t('common.confirm'),
|
||||
negativeText: $t('common.cancel'),
|
||||
title: '提示',
|
||||
content: '确定退出登录吗?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
authStore.resetStore();
|
||||
}
|
||||
@@ -69,14 +68,14 @@ function handleDropdown(key: DropdownKey) {
|
||||
logout();
|
||||
} else {
|
||||
// If your other options are jumps from other routes, they will be directly supported here
|
||||
routerPushByKey(key);
|
||||
// routerPushByKey(key);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NButton v-if="!authStore.isLogin" quaternary @click="loginOrRegister">
|
||||
{{ $t('page.login.common.loginOrRegister') }}
|
||||
登录/注册
|
||||
</NButton>
|
||||
<NDropdown v-else placement="bottom" trigger="click" :options="options" @select="handleDropdown">
|
||||
<div>
|
||||
|
||||
+10
-76
@@ -229,82 +229,16 @@ const local: App.I18n.Schema = {
|
||||
404: 'Page Not Found',
|
||||
500: 'Server Error',
|
||||
home: 'Home',
|
||||
'user-center': 'User Center',
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
loginOrRegister: 'Login / Register',
|
||||
userNamePlaceholder: 'Please enter user name',
|
||||
phonePlaceholder: 'Please enter phone number',
|
||||
codePlaceholder: 'Please enter verification code',
|
||||
passwordPlaceholder: 'Please enter password',
|
||||
confirmPasswordPlaceholder: 'Please enter password again',
|
||||
codeLogin: 'Verification code login',
|
||||
confirm: 'Confirm',
|
||||
back: 'Back',
|
||||
validateSuccess: 'Verification passed',
|
||||
loginSuccess: 'Login successfully',
|
||||
welcomeBack: 'Welcome back, {userName} !'
|
||||
},
|
||||
pwdLogin: {
|
||||
title: 'Password Login',
|
||||
rememberMe: 'Remember me',
|
||||
forgetPassword: 'Forget password?',
|
||||
register: 'Register',
|
||||
otherAccountLogin: 'Other Account Login',
|
||||
otherLoginMode: 'Other Login Mode',
|
||||
superAdmin: 'Super Admin',
|
||||
admin: 'Admin',
|
||||
user: 'User'
|
||||
},
|
||||
codeLogin: {
|
||||
title: 'Verification Code Login',
|
||||
getCode: 'Get verification code',
|
||||
reGetCode: 'Reacquire after {time}s',
|
||||
sendCodeSuccess: 'Verification code sent successfully',
|
||||
imageCodePlaceholder: 'Please enter image verification code'
|
||||
},
|
||||
register: {
|
||||
title: 'Register',
|
||||
agreement: 'I have read and agree to',
|
||||
protocol: '《User Agreement》',
|
||||
policy: '《Privacy Policy》'
|
||||
},
|
||||
resetPwd: {
|
||||
title: 'Reset Password'
|
||||
},
|
||||
bindWeChat: {
|
||||
title: 'Bind WeChat'
|
||||
}
|
||||
},
|
||||
home: {
|
||||
branchDesc:
|
||||
'For the convenience of everyone in developing and updating the merge, we have streamlined the code of the main branch, only retaining the homepage menu, and the rest of the content has been moved to the example branch for maintenance. The preview address displays the content of the example branch.',
|
||||
projectCount: 'Project Count',
|
||||
todo: 'Todo',
|
||||
message: 'Message',
|
||||
downloadCount: 'Download Count',
|
||||
registerCount: 'Register Count',
|
||||
schedule: 'Work and rest Schedule',
|
||||
study: 'Study',
|
||||
work: 'Work',
|
||||
rest: 'Rest',
|
||||
entertainment: 'Entertainment',
|
||||
visitCount: 'Visit Count',
|
||||
turnover: 'Turnover',
|
||||
dealCount: 'Deal Count',
|
||||
projectNews: {
|
||||
title: 'Project News',
|
||||
moreNews: 'More News',
|
||||
desc1: 'Soybean created the open source project soybean-admin on May 28, 2021!',
|
||||
desc2: 'Yanbowe submitted a bug to soybean-admin, the multi-tab bar will not adapt.',
|
||||
desc3: 'Soybean is ready to do sufficient preparation for the release of soybean-admin!',
|
||||
desc4: 'Soybean is busy writing project documentation for soybean-admin!',
|
||||
desc5: 'Soybean just wrote some of the workbench pages casually, and it was enough to see!'
|
||||
},
|
||||
creativity: 'Creativity'
|
||||
},
|
||||
home_index: '工作台首页',
|
||||
home_initiatechange: '发起变更',
|
||||
home_mydraft: '我的草稿',
|
||||
home_pending: '待我处理',
|
||||
home_process: '流程演示',
|
||||
home_processdetail: '流程演示详情',
|
||||
changeledger: '变更台账',
|
||||
changeledger_mychanges: '我发起的变更',
|
||||
changeledger_workshopchanges: '本车间变更',
|
||||
changestatistics: '本部门变更统计',
|
||||
},
|
||||
form: {
|
||||
required: 'Cannot be empty',
|
||||
|
||||
@@ -235,17 +235,6 @@ const local: App.I18n.Schema = {
|
||||
changeledger_mychanges: '我发起的变更',
|
||||
changeledger_workshopchanges: '本车间变更',
|
||||
changestatistics: '本部门变更统计',
|
||||
'user-center': '个人中心',
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
confirm: '确定',
|
||||
back: '返回',
|
||||
loginSuccess: '登录成功',
|
||||
welcomeBack: '欢迎回来,{userName} !'
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
required: '不能为空',
|
||||
|
||||
@@ -28,5 +28,4 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
||||
home_process: () => import("@/views/home/process/index.vue"),
|
||||
home_processdetail: () => import("@/views/home/processDetail/index.vue"),
|
||||
login: () => import("@/views/login/index.vue"),
|
||||
"user-center": () => import("@/views/user-center/index.vue"),
|
||||
};
|
||||
|
||||
@@ -169,15 +169,5 @@ export const generatedRoutes: GeneratedRoute[] = [
|
||||
constant: true,
|
||||
hideInMenu: true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'user-center',
|
||||
path: '/user-center',
|
||||
component: 'layout.base$view.user-center',
|
||||
meta: {
|
||||
title: 'user-center',
|
||||
i18nKey: 'route.user-center',
|
||||
hideInMenu: true
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -177,8 +177,7 @@ const routeMap: RouteMap = {
|
||||
"home_pending": "/home/pending",
|
||||
"home_process": "/home/process",
|
||||
"home_processdetail": "/home/processdetail",
|
||||
"login": "/login/:module(pwd-login)?",
|
||||
"user-center": "/user-center"
|
||||
"login": "/login/:module(pwd-login)?"
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
|
||||
import type { ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
|
||||
import { generatedRoutes } from '../elegant/routes';
|
||||
import { layouts, views } from '../elegant/imports';
|
||||
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
|
||||
|
||||
@@ -6,7 +6,6 @@ import { fetchGetUserInfo, fetchLogin } from '@/service/api';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { localStg } from '@/utils/storage';
|
||||
import { SetupStoreId } from '@/enum';
|
||||
import { $t } from '@/locales';
|
||||
import { useRouteStore } from '../route';
|
||||
import { useTabStore } from '../tab';
|
||||
import { clearAuthStorage, getToken } from './shared';
|
||||
@@ -116,8 +115,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
|
||||
await redirectFromLogin(needRedirect);
|
||||
|
||||
window.$notification?.success({
|
||||
title: $t('page.login.common.loginSuccess'),
|
||||
content: $t('page.login.common.welcomeBack', { userName: userInfo.userName }),
|
||||
title: '登录成功',
|
||||
content: `欢迎回来,${userInfo.userName} !`,
|
||||
duration: 4500
|
||||
});
|
||||
}
|
||||
|
||||
Vendored
-334
@@ -480,340 +480,6 @@ declare namespace App {
|
||||
};
|
||||
};
|
||||
route: Record<I18nRouteKey, string>;
|
||||
page: {
|
||||
login: {
|
||||
common: {
|
||||
loginOrRegister: string;
|
||||
userNamePlaceholder: string;
|
||||
phonePlaceholder: string;
|
||||
codePlaceholder: string;
|
||||
passwordPlaceholder: string;
|
||||
confirmPasswordPlaceholder: string;
|
||||
codeLogin: string;
|
||||
confirm: string;
|
||||
back: string;
|
||||
validateSuccess: string;
|
||||
loginSuccess: string;
|
||||
welcomeBack: string;
|
||||
};
|
||||
pwdLogin: {
|
||||
title: string;
|
||||
rememberMe: string;
|
||||
forgetPassword: string;
|
||||
register: string;
|
||||
otherAccountLogin: string;
|
||||
otherLoginMode: string;
|
||||
superAdmin: string;
|
||||
admin: string;
|
||||
user: string;
|
||||
};
|
||||
codeLogin: {
|
||||
title: string;
|
||||
getCode: string;
|
||||
reGetCode: string;
|
||||
sendCodeSuccess: string;
|
||||
imageCodePlaceholder: string;
|
||||
};
|
||||
register: {
|
||||
title: string;
|
||||
agreement: string;
|
||||
protocol: string;
|
||||
policy: string;
|
||||
};
|
||||
resetPwd: {
|
||||
title: string;
|
||||
};
|
||||
bindWeChat: {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
about: {
|
||||
title: string;
|
||||
introduction: string;
|
||||
projectInfo: {
|
||||
title: string;
|
||||
version: string;
|
||||
latestBuildTime: string;
|
||||
githubLink: string;
|
||||
previewLink: string;
|
||||
};
|
||||
prdDep: string;
|
||||
devDep: string;
|
||||
};
|
||||
home: {
|
||||
branchDesc: string;
|
||||
projectCount: string;
|
||||
todo: string;
|
||||
message: string;
|
||||
downloadCount: string;
|
||||
registerCount: string;
|
||||
schedule: string;
|
||||
study: string;
|
||||
work: string;
|
||||
rest: string;
|
||||
entertainment: string;
|
||||
visitCount: string;
|
||||
turnover: string;
|
||||
dealCount: string;
|
||||
projectNews: {
|
||||
title: string;
|
||||
moreNews: string;
|
||||
desc1: string;
|
||||
desc2: string;
|
||||
desc3: string;
|
||||
desc4: string;
|
||||
desc5: string;
|
||||
};
|
||||
creativity: string;
|
||||
};
|
||||
function: {
|
||||
tab: {
|
||||
tabOperate: {
|
||||
title: string;
|
||||
addTab: string;
|
||||
addTabDesc: string;
|
||||
closeTab: string;
|
||||
closeCurrentTab: string;
|
||||
closeAboutTab: string;
|
||||
addMultiTab: string;
|
||||
addMultiTabDesc1: string;
|
||||
addMultiTabDesc2: string;
|
||||
};
|
||||
tabTitle: {
|
||||
title: string;
|
||||
changeTitle: string;
|
||||
change: string;
|
||||
resetTitle: string;
|
||||
reset: string;
|
||||
};
|
||||
};
|
||||
multiTab: {
|
||||
routeParam: string;
|
||||
backTab: string;
|
||||
};
|
||||
toggleAuth: {
|
||||
toggleAccount: string;
|
||||
authHook: string;
|
||||
superAdminVisible: string;
|
||||
adminVisible: string;
|
||||
adminOrUserVisible: string;
|
||||
};
|
||||
request: {
|
||||
repeatedErrorOccurOnce: string;
|
||||
repeatedError: string;
|
||||
repeatedErrorMsg1: string;
|
||||
repeatedErrorMsg2: string;
|
||||
};
|
||||
};
|
||||
alova: {
|
||||
scenes: {
|
||||
captchaSend: string;
|
||||
autoRequest: string;
|
||||
visibilityRequestTips: string;
|
||||
pollingRequestTips: string;
|
||||
networkRequestTips: string;
|
||||
refreshTime: string;
|
||||
startRequest: string;
|
||||
stopRequest: string;
|
||||
requestCrossComponent: string;
|
||||
triggerAllRequest: string;
|
||||
};
|
||||
};
|
||||
proNaive: {
|
||||
form: {
|
||||
basic: {
|
||||
title: string;
|
||||
appName: string;
|
||||
appStatus: string;
|
||||
createTime: string;
|
||||
responseDate: string;
|
||||
specificationInfo: string;
|
||||
specificate: string;
|
||||
specificationName: string;
|
||||
specificationValue: string;
|
||||
specificationColorRed: string;
|
||||
specificationColorOrange: string;
|
||||
addSpecificateItem: string;
|
||||
fillValue: string;
|
||||
reset: string;
|
||||
submit: string;
|
||||
add: string;
|
||||
delete: string;
|
||||
color: string;
|
||||
normal: string;
|
||||
anomaly: string;
|
||||
};
|
||||
query: {
|
||||
title1: string;
|
||||
title2: string;
|
||||
appName: string;
|
||||
appStatus: string;
|
||||
createTime: string;
|
||||
responseDate: string;
|
||||
endDate: string;
|
||||
field: string;
|
||||
};
|
||||
step: {
|
||||
title: string;
|
||||
step1: {
|
||||
title: string;
|
||||
field: string;
|
||||
nextStep: string;
|
||||
};
|
||||
step2: {
|
||||
title: string;
|
||||
field: string;
|
||||
prevStep: string;
|
||||
submit: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
table: {
|
||||
remote: {
|
||||
filterCondition: string;
|
||||
name: string;
|
||||
createTime: string;
|
||||
responseTime: string;
|
||||
title: string;
|
||||
replicableText: string;
|
||||
tags: string;
|
||||
dateFormatting: string;
|
||||
image: string;
|
||||
};
|
||||
rowEdit: {
|
||||
title: string;
|
||||
reset: string;
|
||||
submit: string;
|
||||
edit: string;
|
||||
delete: string;
|
||||
save: string;
|
||||
task: string;
|
||||
score: string;
|
||||
time: string;
|
||||
name: string;
|
||||
action: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
manage: {
|
||||
common: {
|
||||
status: {
|
||||
enable: string;
|
||||
disable: string;
|
||||
};
|
||||
};
|
||||
role: {
|
||||
title: string;
|
||||
roleName: string;
|
||||
roleCode: string;
|
||||
roleStatus: string;
|
||||
roleDesc: string;
|
||||
form: {
|
||||
roleName: string;
|
||||
roleCode: string;
|
||||
roleStatus: string;
|
||||
roleDesc: string;
|
||||
};
|
||||
addRole: string;
|
||||
editRole: string;
|
||||
menuAuth: string;
|
||||
buttonAuth: string;
|
||||
};
|
||||
user: {
|
||||
title: string;
|
||||
userName: string;
|
||||
userGender: string;
|
||||
nickName: string;
|
||||
userPhone: string;
|
||||
userEmail: string;
|
||||
userStatus: string;
|
||||
userRole: string;
|
||||
form: {
|
||||
userName: string;
|
||||
userGender: string;
|
||||
nickName: string;
|
||||
userPhone: string;
|
||||
userEmail: string;
|
||||
userStatus: string;
|
||||
userRole: string;
|
||||
};
|
||||
addUser: string;
|
||||
editUser: string;
|
||||
gender: {
|
||||
male: string;
|
||||
female: string;
|
||||
};
|
||||
};
|
||||
menu: {
|
||||
home: string;
|
||||
title: string;
|
||||
id: string;
|
||||
parentId: string;
|
||||
menuType: string;
|
||||
menuName: string;
|
||||
routeName: string;
|
||||
routePath: string;
|
||||
pathParam: string;
|
||||
layout: string;
|
||||
page: string;
|
||||
i18nKey: string;
|
||||
icon: string;
|
||||
localIcon: string;
|
||||
iconTypeTitle: string;
|
||||
order: string;
|
||||
constant: string;
|
||||
keepAlive: string;
|
||||
href: string;
|
||||
hideInMenu: string;
|
||||
activeMenu: string;
|
||||
multiTab: string;
|
||||
fixedIndexInTab: string;
|
||||
query: string;
|
||||
button: string;
|
||||
buttonCode: string;
|
||||
buttonDesc: string;
|
||||
menuStatus: string;
|
||||
form: {
|
||||
home: string;
|
||||
menuType: string;
|
||||
menuName: string;
|
||||
routeName: string;
|
||||
routePath: string;
|
||||
pathParam: string;
|
||||
layout: string;
|
||||
page: string;
|
||||
i18nKey: string;
|
||||
icon: string;
|
||||
localIcon: string;
|
||||
order: string;
|
||||
keepAlive: string;
|
||||
href: string;
|
||||
hideInMenu: string;
|
||||
activeMenu: string;
|
||||
multiTab: string;
|
||||
fixedInTab: string;
|
||||
fixedIndexInTab: string;
|
||||
queryKey: string;
|
||||
queryValue: string;
|
||||
button: string;
|
||||
buttonCode: string;
|
||||
buttonDesc: string;
|
||||
menuStatus: string;
|
||||
};
|
||||
addMenu: string;
|
||||
editMenu: string;
|
||||
addChildMenu: string;
|
||||
type: {
|
||||
directory: string;
|
||||
menu: string;
|
||||
};
|
||||
iconType: {
|
||||
iconify: string;
|
||||
local: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
form: {
|
||||
required: string;
|
||||
userName: FormMsg;
|
||||
|
||||
Vendored
-3
@@ -32,7 +32,6 @@ declare module "@elegant-router/types" {
|
||||
"home_process": "/home/process";
|
||||
"home_processdetail": "/home/processdetail";
|
||||
"login": "/login/:module(pwd-login)?";
|
||||
"user-center": "/user-center";
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -71,7 +70,6 @@ declare module "@elegant-router/types" {
|
||||
| "changestatistics"
|
||||
| "home"
|
||||
| "login"
|
||||
| "user-center"
|
||||
>;
|
||||
|
||||
/**
|
||||
@@ -101,7 +99,6 @@ declare module "@elegant-router/types" {
|
||||
| "home_process"
|
||||
| "home_processdetail"
|
||||
| "login"
|
||||
| "user-center"
|
||||
>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,14 +33,13 @@ const ROLES = [
|
||||
{ id: "viewer", name: "非审批人员", person: "吴新", org: "相关部门", desc: "以信息查看为主,不参与变更流程" },
|
||||
];
|
||||
const mine = computed(() => CHANGES.filter((c) => c.applicant === ROLES[0].person));
|
||||
interface MyRow { c: ChangeOrder | null; id: string; title: string; type: ChangeType; level: ChangeLevel; status: ChangeStatus; date: string; urgent: boolean; duration: "永久" | "临时" }
|
||||
const myRows = computed(() => [
|
||||
...mine.value.map((c:ChangeOrder) => ({ c, id: c.id, title: c.title, type: c.type, level: c.level, status: c.status, date: c.date, urgent: c.urgent, duration: c.duration })),
|
||||
...MY_CLOSED.map((m) => ({ c: null, ...m, urgent: false })),
|
||||
]);
|
||||
const BIG_STAGES = ["申请审批", "实施与关闭", "变更关闭"];
|
||||
const bigStageOf = (s: ChangeStatus) => (s === "审批中" ? 0 : s === "已关闭" ? 2 : 1);
|
||||
const openRow = (row: MyRow) => {
|
||||
const bigStageOf = (s: any) => (s === "审批中" ? 0 : s === "已关闭" ? 2 : 1);
|
||||
const openRow = (row: any) => {
|
||||
if (row.c){
|
||||
emit("openChange", row.c);
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
CHANGES, DOC_TODO, LEVEL_COLOR, STATUS_COLOR, TEMP_WATCH, URGE_RECORDS,
|
||||
countOpenStatus, type ChangeLevel, type ChangeOrder, type ChangeStatus, type ChangeType,
|
||||
} from "@/typings/model";
|
||||
import { getColorWithOpacity } from "@/utils/common";
|
||||
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
@@ -27,6 +28,7 @@ const form = ref<ApplyForm>({
|
||||
name: "", dept: "", mainChanges: [""], related: "", purposes: [], effect: "",
|
||||
type: "", level: "", duration: "", urgent: false, date: "", restoreDate: "",
|
||||
});
|
||||
const tab = ref<string>("pre");
|
||||
const TABS = [
|
||||
{ id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
|
||||
{ id: "form", name: "变更申请表", icon: 'akar-icons:file' },
|
||||
@@ -36,8 +38,7 @@ const TABS = [
|
||||
{ id: "accept", name: "验收评价", icon: 'material-symbols:check-circle-outline' },
|
||||
{ id: "close", name: "变更关闭确认表", icon: 'quill:folder-open' },
|
||||
];
|
||||
const tab = ref<"pre" | "form" | "risk" | "train" | "pssr" | "accept" | "close">("pre");
|
||||
const formGen = ref<"idle" | "generating" | "done">("idle");
|
||||
const formGen = ref<string>("idle");
|
||||
const confirmed = ref<Record<string, boolean>>({});
|
||||
const blockedTabs = ref<string[]>([]);
|
||||
|
||||
@@ -47,10 +48,16 @@ const blockedTabs = ref<string[]>([]);
|
||||
<NSpace vertical :size="16">
|
||||
<!-- 发起变更 -->
|
||||
<NCard :bordered="false" size="small" :style="{ borderRadius: themeStore.themeRadius + 'px', height: '100%' }">
|
||||
<div class="sticky top-0 z-20 rounded-t-xl border-b bg-[#e8f2fd]">
|
||||
<div
|
||||
class="sticky top-0 z-20 border-b bg-[var(--bg-color)]"
|
||||
:style="{
|
||||
borderRadius: themeStore.themeRadius + 'px',
|
||||
'--bg-color': getColorWithOpacity(themeStore.themeColor, 0.1)
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="flex flex-wrap items-center gap-x-3 gap-y-2 border-b px-4 py-2"
|
||||
:style="{borderColor: themeStore.themeColor}"
|
||||
class="flex flex-wrap items-center gap-x-3 gap-y-2 border-b px-4 py-2 border-[var(--border-color)]"
|
||||
:style="{'--border-color': getColorWithOpacity(themeStore.themeColor, 0.2)}"
|
||||
>
|
||||
<span class="whitespace-nowrap text-sm font-medium text-slate-600">变更名称 <span class="text-red-500">*</span></span>
|
||||
<NInput v-model:value="form.name" maxlength="28" show-count placeholder="AI 自动总结(28 字以内),可手动修改" class="!w-420px" />
|
||||
@@ -60,8 +67,11 @@ const blockedTabs = ref<string[]>([]);
|
||||
<div class="flex items-center gap-1 overflow-x-auto px-3 pt-2">
|
||||
<div v-for="t in TABS" :key="t.id" @click="tab = t.id"
|
||||
class="flex items-center gap-1.5 whitespace-nowrap px-3 py-2 text-sm transition cursor-pointer border hover:bg-white/60 rounded-t-lg"
|
||||
:class="tab === t.id ? 'border-b-transparent border-[#ACC6E5] bg-white text-[var(--active-color)]':'border-transparent text-slate-500'"
|
||||
:style="{'--active-color': themeStore.themeColor}"
|
||||
:class="tab === t.id ? 'border-b-transparent border-[var(--border-color)] bg-white text-[var(--active-color)]':'border-transparent text-slate-500'"
|
||||
:style="{
|
||||
'--border-color': getColorWithOpacity(themeStore.themeColor, 0.3),
|
||||
'--active-color': themeStore.themeColor
|
||||
}"
|
||||
>
|
||||
<Icon :icon="t.icon" class="size-14px" />
|
||||
{{ t.name }}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChangeStatus } from "../demo/model";
|
||||
const props = defineProps<{ status: ChangeStatus }>();
|
||||
const props = defineProps<{ status: any }>();
|
||||
const BIG_STAGES = ["申请审批", "实施与关闭", "变更关闭"];
|
||||
const bigStageOf = (s: ChangeStatus) => (s === "审批中" ? 0 : s === "已关闭" ? 2 : 1);
|
||||
const bigStageOf = (s: any) => (s === "审批中" ? 0 : s === "已关闭" ? 2 : 1);
|
||||
const cur = bigStageOf(props.status);
|
||||
const pct = ((cur + 1) / BIG_STAGES.length) * 100;
|
||||
const size = 34, stroke = 4, r = (size - stroke) / 2, c = 2 * Math.PI * r;
|
||||
|
||||
@@ -2,32 +2,14 @@
|
||||
import { computed } from 'vue';
|
||||
import type { Component } from 'vue';
|
||||
import { getPaletteColorByNumber, mixColor } from '@sa/color';
|
||||
import { loginModuleRecord } from '@/constants/app';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import PwdLogin from './modules/pwd-login.vue';
|
||||
|
||||
interface Props {
|
||||
/** The login module */
|
||||
module?: UnionKey.LoginModule;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
|
||||
const appStore = useAppStore();
|
||||
const themeStore = useThemeStore();
|
||||
|
||||
interface LoginModule {
|
||||
label: App.I18n.I18nKey;
|
||||
component: Component;
|
||||
}
|
||||
|
||||
const moduleMap: Record<UnionKey.LoginModule, LoginModule> = {
|
||||
'pwd-login': { label: loginModuleRecord['pwd-login'], component: PwdLogin },
|
||||
};
|
||||
|
||||
const activeModule = computed(() => moduleMap[props.module || 'pwd-login']);
|
||||
const activeModule = computed(() => ({ label: '密码登录', component: PwdLogin }));
|
||||
|
||||
const bgThemeColor = computed(() =>
|
||||
themeStore.darkMode ? getPaletteColorByNumber(themeStore.themeColor, 600) : themeStore.themeColor
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<LookForward />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user