2
0

Merge remote-tracking branch 'origin/wfc-modules-user'

This commit is contained in:
lai
2024-11-29 18:32:00 +08:00
70 changed files with 1286 additions and 3960 deletions

View File

@@ -1,8 +1,8 @@
export const REG_USER_NAME = /^[\u4E00-\u9FA5a-zA-Z0-9_-]{4,16}$/;
/** Phone reg */
export const REG_PHONE =
/^[1](([3][0-9])|([4][01456789])|([5][012356789])|([6][2567])|([7][0-8])|([8][0-9])|([9][012356789]))[0-9]{8}$/;
export const REG_PHONE = /^.{3,}$/;
///^[1](([3][0-9])|([4][01456789])|([5][012356789])|([6][2567])|([7][0-8])|([8][0-9])|([9][012356789]))[0-9]{8}$/;
/**
* Password reg
@@ -12,7 +12,8 @@ export const REG_PHONE =
export const REG_PWD = /^\w{6,18}$/;
/** Email reg */
export const REG_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
export const REG_EMAIL = ///^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
/^(([^<>()\\.,;:\s@"]+(\.[^<>()\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/;
/** Six digit code reg */
export const REG_CODE_SIX = /^\d{6}$/;

View File

@@ -1,12 +1,13 @@
import { computed } from 'vue';
import { useCountDown, useLoading } from '@sa/hooks';
import { $t } from '@/locales';
import { REG_PHONE } from '@/constants/reg';
import {REG_EMAIL} from '@/constants/reg';
import {useAuthStore} from "@/store/modules/auth";
export function useCaptcha() {
const { loading, startLoading, endLoading } = useLoading();
const { count, start, stop, isCounting } = useCountDown(10);
const authStore = useAuthStore();
const label = computed(() => {
let text = $t('page.login.codeLogin.getCode');
@@ -23,14 +24,14 @@ export function useCaptcha() {
return text;
});
function isPhoneValid(phone: string) {
if (phone.trim() === '') {
function isEmailValid(email: string) {
if (email.trim() === '') {
$message?.error?.($t('form.phone.required'));
return false;
}
if (!REG_PHONE.test(phone)) {
if (!REG_EMAIL.test(email)) {
$message?.error?.($t('form.phone.invalid'));
return false;
@@ -38,17 +39,20 @@ export function useCaptcha() {
return true;
}
async function getCaptcha(phone: string) {
const valid = isPhoneValid(phone);
//获取验证码方法
async function getCaptcha(email: string) {
console.log(email)
//const valid = isPhoneValid(phone);
const valid = isEmailValid(email);
if (!valid || loading.value) {
return;
}
startLoading();
// request
await authStore.captcha(
email,
);
await new Promise(resolve => {
setTimeout(resolve, 500);
});

View File

@@ -1,7 +1,7 @@
import { ref, toValue } from 'vue';
import type { ComputedRef, Ref } from 'vue';
import type { FormInstance } from 'ant-design-vue';
import { REG_CODE_SIX, REG_EMAIL, REG_PHONE, REG_PWD, REG_USER_NAME } from '@/constants/reg';
import {REG_CODE_FOUR, REG_EMAIL, REG_PHONE, REG_PWD, REG_USER_NAME} from '@/constants/reg';
import { $t } from '@/locales';
export function useFormRules() {
@@ -22,7 +22,7 @@ export function useFormRules() {
trigger: 'change'
},
code: {
pattern: REG_CODE_SIX,
pattern: REG_CODE_FOUR,
message: $t('form.code.invalid'),
trigger: 'change'
},

View File

@@ -1,6 +1,6 @@
const local: App.I18n.Schema = {
const local: any = {
system: {
title: 'Vue-AntD-Web'
title: 'WANFi Platform'
},
common: {
action: 'Action',
@@ -178,8 +178,10 @@ const local: App.I18n.Schema = {
back: 'Back',
validateSuccess: 'Verification passed',
loginSuccess: 'Login successfully',
registerSuccess:'Register successfully',
welcomeBack: 'Welcome back, {username} !',
checkCode: 'Please check the verification code'
checkCode: 'Please check the verification code',
emailPlaceholder:'Please enter the email'
},
pwdLogin: {
title: 'Password Login',
@@ -203,7 +205,29 @@ const local: App.I18n.Schema = {
title: 'Register',
agreement: 'I have read and agree to',
protocol: '《User Agreement》',
policy: '《Privacy Policy》'
policy: '《Privacy Policy》',
agreeTermsFirst: 'Please agree to the User Agreement and Privacy Policy first',
agreeTerms: 'I have read and agree to the User Agreement and Privacy Policy',
code:'Code',
password:'Password',
confirmPassword:'ConfirmPassword',
basicInfo: 'BasicInfo',
terms: 'Terms',
security: 'Security',
username: 'User Name',
fullName: 'Full Name',
age: 'Age',
gender: 'Gender',
male: 'Male',
female: 'Female',
phone: 'Phone',
email: 'Email',
address: 'Address',
next: 'Next',
prev: 'Prev',
birthDate: 'Birth Date',
birthDatePlaceholder: 'Please select birth date',
birthDateRequired: 'Please select birth date',
},
resetPwd: {
title: 'Reset Password'
@@ -212,6 +236,7 @@ const local: App.I18n.Schema = {
title: 'Bind WeChat'
}
},
about: {
title: 'About',
introduction: `Soybean Admin is an elegant and powerful admin template, based on the latest front-end technology stack, including Vue3, Vite5, TypeScript, Pinia and UnoCSS. It has built-in rich theme configuration and components, strict code specifications, and an automated file routing system. In addition, it also uses the online mock data solution based on ApiFox. Soybean Admin provides you with a one-stop admin solution, no additional configuration, and out of the box. It is also a best practice for learning cutting-edge technologies quickly.`,

View File

@@ -1,6 +1,6 @@
const local: App.I18n.Schema = {
const local:any = {
system: {
title: 'Vue-AntD-Web'
title: 'WANFi 平台',
},
common: {
action: '操作',
@@ -178,8 +178,10 @@ const local: App.I18n.Schema = {
back: '返回',
validateSuccess: '验证成功',
loginSuccess: '登录成功',
registerSuccess:'注册成功',
welcomeBack: '欢迎回来,{username} ',
checkCode: '请输入验证码'
checkCode: '请输入验证码',
emailPlaceholder:'请输入邮箱'
},
pwdLogin: {
title: '密码登录',
@@ -203,7 +205,29 @@ const local: App.I18n.Schema = {
title: '注册账号',
agreement: '我已经仔细阅读并接受',
protocol: '《用户协议》',
policy: '《隐私权政策》'
policy: '《隐私权政策》',
agreeTermsFirst: '请先同意用户协议和隐私政策',
agreeTerms: '我已阅读并同意用户协议和隐私政策',
code:'验证码',
password:'密码',
confirmPassword:'再次输入密码',
basicInfo: '基本信息',
terms: '协议条款',
security: '安全信息',
username: '用户名',
fullName: '姓名',
age: '年龄',
gender: '性别',
male: '男',
female: '女',
phone: '电话',
email: '邮箱',
address: '地址',
next: '下一步',
prev: '上一步',
birthDate: '出生日期',
birthDatePlaceholder: '请选择出生日期',
birthDateRequired: '请选择出生日期',
},
resetPwd: {
title: '重置密码'
@@ -212,6 +236,7 @@ const local: App.I18n.Schema = {
title: '绑定微信'
}
},
about: {
title: '关于',
introduction: `Soybean Admin 是一个优雅且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。它内置了丰富的主题配置和组件代码规范严谨实现了自动化的文件路由系统。此外它还采用了基于 ApiFox 的在线Mock数据方案。Soybean Admin 为您提供了一站式的后台管理解决方案,无需额外配置,开箱即用。同样是一个快速学习前沿技术的最佳实践。`,

View File

@@ -1,41 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// Generated by elegant-router
// Read more: https://github.com/soybeanjs/elegant-router
import type { RouteComponent } from "vue-router";
import type { LastLevelRouteKey, RouteLayout } from "@elegant-router/types";
import BaseLayout from "@/layouts/base-layout/index.vue";
import BlankLayout from "@/layouts/blank-layout/index.vue";
export const layouts: Record<RouteLayout, RouteComponent | (() => Promise<RouteComponent>)> = {
base: BaseLayout,
blank: BlankLayout,
};
export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<RouteComponent>)> = {
403: () => import("@/views/_builtin/403/index.vue"),
404: () => import("@/views/_builtin/404/index.vue"),
500: () => import("@/views/_builtin/500/index.vue"),
login: () => import("@/views/_builtin/login/index.vue"),
about: () => import("@/views/about/index.vue"),
"function_hide-child_one": () => import("@/views/function/hide-child/one/index.vue"),
"function_hide-child_three": () => import("@/views/function/hide-child/three/index.vue"),
"function_hide-child_two": () => import("@/views/function/hide-child/two/index.vue"),
"function_multi-tab": () => import("@/views/function/multi-tab/index.vue"),
function_request: () => import("@/views/function/request/index.vue"),
"function_super-page": () => import("@/views/function/super-page/index.vue"),
function_tab: () => import("@/views/function/tab/index.vue"),
"function_toggle-auth": () => import("@/views/function/toggle-auth/index.vue"),
home: () => import("@/views/home/index.vue"),
manage_dept: () => import("@/views/manage/dept/index.vue"),
manage_dict: () => import("@/views/manage/dict/index.vue"),
manage_menu: () => import("@/views/manage/menu/index.vue"),
manage_post: () => import("@/views/manage/post/index.vue"),
manage_role: () => import("@/views/manage/role/index.vue"),
manage_route: () => import("@/views/manage/route/index.vue"),
"manage_user-detail": () => import("@/views/manage/user-detail/[id].vue"),
manage_user: () => import("@/views/manage/user/index.vue"),
"user-center": () => import("@/views/user-center/index.vue"),
};

View File

@@ -9,7 +9,7 @@ export const generatedRoutes: GeneratedRoute[] = [
{
name: '403',
path: '/403',
component: 'layout.blank$view.403',
component: 'layout.blank$view._builtin_403',
meta: {
title: '403',
i18nKey: 'route.403',
@@ -20,7 +20,7 @@ export const generatedRoutes: GeneratedRoute[] = [
{
name: '404',
path: '/404',
component: 'layout.blank$view.404',
component: 'layout.blank$view._builtin_404',
meta: {
title: '404',
i18nKey: 'route.404',
@@ -31,7 +31,7 @@ export const generatedRoutes: GeneratedRoute[] = [
{
name: '500',
path: '/500',
component: 'layout.blank$view.500',
component: 'layout.blank$view._builtin_500',
meta: {
title: '500',
i18nKey: 'route.500',
@@ -182,7 +182,7 @@ export const generatedRoutes: GeneratedRoute[] = [
{
name: 'login',
path: '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
component: 'layout.blank$view.login',
component: 'layout.blank$view._builtin_login',
props: true,
meta: {
title: 'login',
@@ -191,106 +191,6 @@ export const generatedRoutes: GeneratedRoute[] = [
hideInMenu: true
}
},
{
name: 'manage',
path: '/manage',
component: 'layout.base',
meta: {
title: 'manage',
i18nKey: 'route.manage',
icon: 'carbon:cloud-service-management',
order: 9,
roles: ['R_ADMIN']
},
children: [
{
name: 'manage_dept',
path: '/manage/dept',
component: 'view.manage_dept',
meta: {
title: 'manage_dept',
i18nKey: 'route.manage_dept'
}
},
{
name: 'manage_dict',
path: '/manage/dict',
component: 'view.manage_dict',
meta: {
title: 'manage_dict',
i18nKey: 'route.manage_dict'
}
},
{
name: 'manage_menu',
path: '/manage/menu',
component: 'view.manage_menu',
meta: {
title: 'manage_menu',
i18nKey: 'route.manage_menu',
icon: 'material-symbols:route',
order: 3,
roles: ['R_ADMIN'],
keepAlive: true
}
},
{
name: 'manage_post',
path: '/manage/post',
component: 'view.manage_post',
meta: {
title: 'manage_post',
i18nKey: 'route.manage_post'
}
},
{
name: 'manage_role',
path: '/manage/role',
component: 'view.manage_role',
meta: {
title: 'manage_role',
i18nKey: 'route.manage_role',
icon: 'carbon:user-role',
order: 2,
roles: ['R_SUPER']
}
},
{
name: 'manage_route',
path: '/manage/route',
component: 'view.manage_route',
meta: {
title: 'manage_route',
i18nKey: 'route.manage_route'
}
},
{
name: 'manage_user',
path: '/manage/user',
component: 'view.manage_user',
meta: {
title: 'manage_user',
i18nKey: 'route.manage_user',
icon: 'ic:round-manage-accounts',
order: 1,
roles: ['R_ADMIN']
}
},
{
name: 'manage_user-detail',
path: '/manage/user-detail/:id',
component: 'view.manage_user-detail',
props: true,
meta: {
title: 'manage_user-detail',
i18nKey: 'route.manage_user-detail',
hideInMenu: true,
roles: ['R_ADMIN'],
activeMenu: 'manage_user'
}
}
]
},
{
name: 'user-center',
path: '/user-center',

View File

@@ -3,34 +3,28 @@
// Generated by elegant-router
// Read more: https://github.com/soybeanjs/elegant-router
import type { RouteRecordRaw, RouteComponent } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';
import type { ElegantConstRoute } from '@elegant-router/vue';
import type { RouteMap, RouteKey, RoutePath } from '@elegant-router/types';
import type { RouteKey, RouteMap, RoutePath } from '@elegant-router/types';
import BaseLayout from '@/layouts/base-layout/index.vue';
import BlankLayout from '@/layouts/blank-layout/index.vue';
/**
* transform elegant const routes to vue routes
* @param routes elegant const routes
* @param layouts layout components
* @param views view components
*/
export function transformElegantRoutesToVueRoutes(
routes: ElegantConstRoute[],
layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>,
views: Record<string, RouteComponent | (() => Promise<RouteComponent>)>
routes: ElegantConstRoute[]
) {
return routes.flatMap(route => transformElegantRouteToVueRoute(route, layouts, views));
return routes.flatMap(route => transformElegantRouteToVueRoute(route));
}
/**
* transform elegant route to vue route
* @param route elegant const route
* @param layouts layout components
* @param views view components
*/
function transformElegantRouteToVueRoute(
route: ElegantConstRoute,
layouts: Record<string, RouteComponent | (() => Promise<RouteComponent>)>,
views: Record<string, RouteComponent | (() => Promise<RouteComponent>)>
route: ElegantConstRoute
) {
const LAYOUT_PREFIX = 'layout.';
const VIEW_PREFIX = 'view.';
@@ -43,12 +37,13 @@ function transformElegantRouteToVueRoute(
function getLayoutName(component: string) {
const layout = component.replace(LAYOUT_PREFIX, '');
if(!layouts[layout]) {
throw new Error(`Layout component "${layout}" not found`);
if (layout === 'base') {
return BaseLayout;
}
return layout;
if (layout === 'blank') {
return BlankLayout;
}
throw new Error(`Layout component "${layout}" not found`);
}
function isView(component: string) {
@@ -57,12 +52,12 @@ function transformElegantRouteToVueRoute(
function getViewName(component: string) {
const view = component.replace(VIEW_PREFIX, '');
if(!views[view]) {
const v = findView(view);
if (!v) {
throw new Error(`View component "${view}" not found`);
}
return view;
return v;
}
function isFirstLevelRoute(item: ElegantConstRoute) {
@@ -97,53 +92,49 @@ function transformElegantRouteToVueRoute(
if (component) {
if (isSingleLevelRoute(route)) {
const { layout, view } = getSingleLevelRouteComponent(component);
const singleLevelRoute: RouteRecordRaw = {
path,
component: layouts[layout],
component: layout,
children: [
{
name,
path: '',
component: views[view],
component: view,
...rest
} as RouteRecordRaw
]
};
return [singleLevelRoute];
}
if (isLayout(component)) {
const layoutName = getLayoutName(component);
vueRoute.component = layouts[layoutName];
vueRoute.component = getLayoutName(component);
}
if (isView(component)) {
const viewName = getViewName(component);
vueRoute.component = views[viewName];
vueRoute.component = getViewName(component);
}
}
} catch (error: any) {
console.error(`Error transforming route "${route.name}": ${error.toString()}`);
return [];
}
// add redirect to child
if (children?.length && !vueRoute.redirect) {
vueRoute.redirect = {
name: children[0].name
};
}
if (children?.length) {
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
if(isFirstLevelRoute(route)) {
if (children?.length) {
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child));
if (isFirstLevelRoute(route)) {
vueRoute.children = childRoutes;
} else {
vueRoutes.push(...childRoutes);
@@ -155,42 +146,61 @@ function transformElegantRouteToVueRoute(
return vueRoutes;
}
/**匹配views里面所有的.vue或.tsx文件 */
const views = import.meta.glob('./../../views/**/*.{vue,tsx}');
/**
* 查找页面模块
*
* 查找 `/views/system/menu/index.vue` 或 `/views/system/menu/index.tsx`
*
* 参数值为 `system/menu/index`
*
* @param dirName 组件路径
* @returns 路由懒加载函数
*/
function findView(dirName: string) {
for (const dir in views) {
let viewDirName = '';
const component = dir.match(/views\/(.+)\.(vue|tsx)/);
if (component && component.length === 3) {
viewDirName = component[1];
}
viewDirName = viewDirName.replaceAll('/', '_').replace('_index', '');
if (viewDirName === dirName) {
return () => views[dir]();
}
}
return () => import('@/views/_builtin/404/index.vue');
}
/**
* map of route name and route path
*/
const routeMap: RouteMap = {
"root": "/",
"not-found": "/:pathMatch(.*)*",
"exception": "/exception",
"exception_403": "/exception/403",
"exception_404": "/exception/404",
"exception_500": "/exception/500",
"403": "/403",
"404": "/404",
"500": "/500",
"about": "/about",
"function": "/function",
"function_hide-child": "/function/hide-child",
"function_hide-child_one": "/function/hide-child/one",
"function_hide-child_three": "/function/hide-child/three",
"function_hide-child_two": "/function/hide-child/two",
"function_multi-tab": "/function/multi-tab",
"function_request": "/function/request",
"function_super-page": "/function/super-page",
"function_tab": "/function/tab",
"function_toggle-auth": "/function/toggle-auth",
"home": "/home",
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?",
"manage": "/manage",
"manage_dept": "/manage/dept",
"manage_dict": "/manage/dict",
"manage_menu": "/manage/menu",
"manage_post": "/manage/post",
"manage_role": "/manage/role",
"manage_route": "/manage/route",
"manage_user": "/manage/user",
"manage_user-detail": "/manage/user-detail/:id",
"user-center": "/user-center"
'root': '/',
'not-found': '/:pathMatch(.*)*',
'exception': '/exception',
'exception_403': '/exception/403',
'exception_404': '/exception/404',
'exception_500': '/exception/500',
'403': '/403',
'404': '/404',
'500': '/500',
'about': '/about',
'function': '/function',
'function_hide-child': '/function/hide-child',
'function_hide-child_one': '/function/hide-child/one',
'function_hide-child_three': '/function/hide-child/three',
'function_hide-child_two': '/function/hide-child/two',
'function_multi-tab': '/function/multi-tab',
'function_request': '/function/request',
'function_super-page': '/function/super-page',
'function_tab': '/function/tab',
'function_toggle-auth': '/function/toggle-auth',
'home': '/home',
'login': '/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?',
'user-center': '/user-center'
};
/**
@@ -198,6 +208,7 @@ const routeMap: RouteMap = {
* @param name route name
*/
export function getRoutePath<T extends RouteKey>(name: T) {
console.log(name);
return routeMap[name];
}

View File

@@ -1,11 +1,10 @@
import type { CustomRoute } from '@elegant-router/types';
import { layouts, views } from '../elegant/imports';
import { getRoutePath, transformElegantRoutesToVueRoutes } from '../elegant/transform';
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
export const ROOT_ROUTE: CustomRoute = {
name: 'root',
path: '/',
redirect: getRoutePath(import.meta.env.VITE_ROUTE_HOME) || 'manage_user',
redirect: '/home',
meta: {
title: 'root',
constant: true
@@ -15,7 +14,7 @@ export const ROOT_ROUTE: CustomRoute = {
const NOT_FOUND_ROUTE: CustomRoute = {
name: 'not-found',
path: '/:pathMatch(.*)*',
component: 'layout.blank$view.404',
component: 'layout.blank$view._builtin_404',
meta: {
title: 'not-found',
constant: true
@@ -27,5 +26,5 @@ const builtinRoutes: CustomRoute[] = [ROOT_ROUTE, NOT_FOUND_ROUTE];
/** create builtin vue routes */
export function createBuiltinVueRoutes() {
return transformElegantRoutesToVueRoutes(builtinRoutes, layouts, views);
return transformElegantRoutesToVueRoutes(builtinRoutes);
}

View File

@@ -1,6 +1,5 @@
import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
import type { ElegantConstRoute } from '@elegant-router/types';
import { generatedRoutes } from '../elegant/routes';
import { layouts, views } from '../elegant/imports';
import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
/**
@@ -8,7 +7,7 @@ import { transformElegantRoutesToVueRoutes } from '../elegant/transform';
*
* @link https://github.com/soybeanjs/elegant-router?tab=readme-ov-file#custom-route
*/
const customRoutes: CustomRoute[] = [
const customRoutes: any[] = [
{
name: 'exception',
path: '/exception',
@@ -23,7 +22,7 @@ const customRoutes: CustomRoute[] = [
{
name: 'exception_403',
path: '/exception/403',
component: 'view.403',
component: 'view._builtin_403',
meta: {
title: 'exception_403',
i18nKey: 'route.exception_403',
@@ -33,7 +32,7 @@ const customRoutes: CustomRoute[] = [
{
name: 'exception_404',
path: '/exception/404',
component: 'view.404',
component: 'view._builtin_404',
meta: {
title: 'exception_404',
i18nKey: 'route.exception_404',
@@ -43,7 +42,7 @@ const customRoutes: CustomRoute[] = [
{
name: 'exception_500',
path: '/exception/500',
component: 'view.500',
component: 'view._builtin_500',
meta: {
title: 'exception_500',
i18nKey: 'route.exception_500',
@@ -56,9 +55,9 @@ const customRoutes: CustomRoute[] = [
/** create routes when the auth route mode is static */
export function createStaticRoutes() {
const constantRoutes: ElegantRoute[] = [];
const constantRoutes: any[] = [];
const authRoutes: ElegantRoute[] = [];
const authRoutes: any[] = [];
[...customRoutes, ...generatedRoutes].forEach(item => {
if (item.meta?.constant) {
@@ -80,5 +79,5 @@ export function createStaticRoutes() {
* @param routes Elegant routes
*/
export function getAuthVueRoutes(routes: ElegantConstRoute[]) {
return transformElegantRoutesToVueRoutes(routes, layouts, views);
return transformElegantRoutesToVueRoutes(routes);
}

View File

@@ -13,7 +13,23 @@ export function fetchLogin(body: Api.Auth.LoginBody) {
data: body
});
}
//邮箱验证码接口
export function sendCaptcha(body:Api.Auth.EmailCaptcha){
return request({
url:`/system/email/code?email=${body.email}`,
method:'get',
})
}
//验证注册
//添加注册
export function fetchRegister(body: Api.Auth.RegisterBody) {
return request({
url: '/auth/register',
method: 'post',
data: body
});
}
/** logout */
export function doDeleteLogout() {
return request<App.Service.Response<null>>({
@@ -24,7 +40,7 @@ export function doDeleteLogout() {
/** Get user info */
export function doGetUserInfo() {
return request<Api.Auth.UserInfo>({ url: '/system/user/getInfo' });
return request<Api.Auth.UserInfo>({ url: '/u/user/getInfo' });
}
/**

View File

@@ -8,7 +8,7 @@ export type DeptFormType = Pick<
/** 获取部门列表 */
export function doGetDeptList(params: Api.SystemManage.DeptSearchParams) {
return request<Api.SystemManage.DeptList>({
url: '/system/dept/list',
url: '/u/dept/list',
params
});
}
@@ -20,7 +20,7 @@ export function doGetDeptList(params: Api.SystemManage.DeptSearchParams) {
*/
export function doGetDeptInfo(deptId: number) {
return request<Api.SystemManage.Dept>({
url: `/system/dept/${deptId}`
url: `/u/dept/${deptId}`
});
}
@@ -31,7 +31,7 @@ export function doGetDeptInfo(deptId: number) {
*/
export function doAddDept(body: DeptFormType) {
return request({
url: '/system/dept',
url: '/u/dept',
method: 'post',
data: body
});
@@ -44,7 +44,7 @@ export function doAddDept(body: DeptFormType) {
*/
export function doEditDept(body: DeptFormType & { deptId: number }) {
return request({
url: '/system/dept',
url: '/u/dept',
method: 'put',
data: body
});
@@ -57,7 +57,7 @@ export function doEditDept(body: DeptFormType & { deptId: number }) {
*/
export function doDeleteDept(deptId: string | number) {
return request({
url: `/system/dept/${deptId}`,
url: `/u/dept/${deptId}`,
method: 'delete'
});
}

View File

@@ -6,7 +6,7 @@ export type DictSubmitModel = Partial<
export const doGetDictList = (params: Api.SystemManage.DictSearchParams) => {
return request<Api.SystemManage.DictList>({
url: '/system/dict/type/list',
url: '/u/dict/type/list',
method: 'get',
params
});
@@ -14,7 +14,7 @@ export const doGetDictList = (params: Api.SystemManage.DictSearchParams) => {
export const doAddDict = (data: DictSubmitModel) => {
return request({
url: '/system/dict/type',
url: '/u/dict/type',
method: 'post',
data
});
@@ -22,7 +22,7 @@ export const doAddDict = (data: DictSubmitModel) => {
export const doEditDict = (data: DictSubmitModel) => {
return request({
url: '/system/dict/type',
url: '/u/dict/type',
method: 'put',
data
});
@@ -30,7 +30,7 @@ export const doEditDict = (data: DictSubmitModel) => {
export const doDeleteDict = (dictId: string | number) => {
return request({
url: `/system/dict/type/${dictId}`,
url: `/u/dict/type/${dictId}`,
method: 'post'
});
};

View File

@@ -7,13 +7,13 @@ export type MenuListQuery = Partial<
Api.SystemManage.CommonSearchParams;
export function doGetMenuList(params: MenuListQuery) {
return request<Api.SystemManage.MenuList>({ url: '/system/menu/list', method: 'get', params });
return request<Api.SystemManage.MenuList>({ url: '/u/menu/list', method: 'get', params });
}
/** get all pages */
export function fetchGetAllPages() {
return request<Pick<Api.SystemManage.Menu, 'menuId' | 'menuName' | 'path'>[]>({
url: '/system/menu/list',
url: '/u/menu/list',
method: 'get',
params: {
menuType: 'C'
@@ -24,7 +24,7 @@ export function fetchGetAllPages() {
/** get menu tree */
export function fetchGetMenuTree() {
return request<Api.SystemManage.MenuTree[]>({
url: '/system/menu/treeselect',
url: '/u/menu/treeselect',
method: 'get'
});
}
@@ -35,27 +35,27 @@ export function doGetRoleMenuList(roleId: number) {
checkedKeys: number[];
menus: Api.SystemManage.MenuTree[];
}>({
url: `/system/menu/roleMenuTreeselect/${roleId}`,
url: `/u/menu/roleMenuTreeselect/${roleId}`,
method: 'get'
});
}
/** add menu */
export function doAddMenu(data: MenuModelType) {
return request({ url: '/system/menu', method: 'post', data });
return request({ url: '/u/menu', method: 'post', data });
}
/** delete menu */
export function doDeleteMenu(menuId: number) {
return request({ url: `/system/menu/${menuId}`, method: 'delete' });
return request({ url: `/u/menu/${menuId}`, method: 'delete' });
}
/** get menu detail */
export function doGetMenuDetail(menuId: number) {
return request<MenuModelType>({ url: `/system/menu/${menuId}`, method: 'get' });
return request<MenuModelType>({ url: `/u/menu/${menuId}`, method: 'get' });
}
/** edit menu */
export function doEditMenu(data: MenuModelType) {
return request({ url: '/system/menu', method: 'put', data });
return request({ url: '/u/menu', method: 'put', data });
}

View File

@@ -6,7 +6,7 @@ export type PostSubmitModel = Partial<
export function doGetPostList(params: Api.SystemManage.PostSearchParams) {
return request<Api.SystemManage.PostList>({
url: '/system/post/list',
url: '/u/post/list',
method: 'get',
params
});
@@ -14,14 +14,14 @@ export function doGetPostList(params: Api.SystemManage.PostSearchParams) {
export function doGetPostDetail(postId: number) {
return request({
url: `/system/post/${postId}`,
url: `/u/post/${postId}`,
method: 'get'
});
}
export function doAddPost(data: PostSubmitModel) {
return request({
url: '/system/post',
url: '/u/post',
method: 'post',
data
});
@@ -29,7 +29,7 @@ export function doAddPost(data: PostSubmitModel) {
export function doEditPost(data: PostSubmitModel) {
return request({
url: '/system/post',
url: '/u/post',
method: 'put',
data
});
@@ -37,7 +37,7 @@ export function doEditPost(data: PostSubmitModel) {
export function doDeletePost(postId: string | number) {
return request({
url: `/system/post/${postId}`,
url: `/u/post/${postId}`,
method: 'delete'
});
}

View File

@@ -7,7 +7,7 @@ import { request } from '../request';
* @returns
*/
export function doPutRole(role: Api.SystemManage.Role) {
return request({ url: '/system/role', method: 'put', data: role });
return request({ url: '/u/role', method: 'put', data: role });
}
/**
@@ -17,7 +17,7 @@ export function doPutRole(role: Api.SystemManage.Role) {
* @returns
*/
export function doPostRole(role: Api.SystemManage.Role) {
return request({ url: '/system/role', method: 'post', data: role });
return request({ url: '/u/role', method: 'post', data: role });
}
/**
@@ -27,12 +27,12 @@ export function doPostRole(role: Api.SystemManage.Role) {
* @returns
*/
export function doDeleteRole(roleId: number | string) {
return request({ url: `/system/role/${roleId}`, method: 'delete' });
return request({ url: `/u/role/${roleId}`, method: 'delete' });
}
export function doGetRoleList(params?: Api.SystemManage.RoleSearchParams) {
return request<Api.SystemManage.RoleList>({
url: '/system/role/list',
url: '/u/role/list',
method: 'get',
params
});

View File

@@ -7,7 +7,7 @@ export function fetchGetConstantRoutes() {
/** get user routes */
export function doGetUserRoutes() {
return request<Api.Route.MenuRoute[]>({ url: '/system/menu/getRouters' });
return request<Api.Route.MenuRoute[]>({ url: '/u/menu/getRouters' });
}
/**

View File

@@ -2,20 +2,20 @@ import { request } from '../request';
// user api
export function doPutUser(user: Api.Auth.User) {
return request({ url: '/system/user', method: 'put', data: user });
return request({ url: '/u/user', method: 'put', data: user });
}
export function doPostUser(user: Api.Auth.User) {
return request({ url: '/system/user', method: 'post', data: user });
return request({ url: '/u/user', method: 'post', data: user });
}
export function doDeleteUser(userId: number | string) {
return request({ url: `/system/user/${userId}`, method: 'delete' });
return request({ url: `/u/user/${userId}`, method: 'delete' });
}
export function doGetUserList(params?: Api.SystemManage.UserSearchParams) {
return request<Api.SystemManage.UserList>({
url: '/system/user/list',
url: '/u/user/list',
method: 'get',
params
});
@@ -29,26 +29,26 @@ export function doGetUserList(params?: Api.SystemManage.UserSearchParams) {
export function doGetUserPostsAndRoles(userId: number | string | undefined) {
if (!userId) {
return request<Api.SystemManage.UserPostsAndRoles>({
url: '/system/user/',
url: '/u/user/',
method: 'get'
});
}
return request<Api.SystemManage.UserPostsAndRoles>({
url: `/system/user/${userId}`,
url: `/u/user/${userId}`,
method: 'get'
});
}
export function doGetAdminUserPostsAndRoles() {
return request<Api.SystemManage.UserPostsAndRoles>({
url: `/system/user`,
url: `/u/user`,
method: 'get'
});
}
export function doGetUserDeptTree() {
return request<Api.Common.CommonTree>({
url: '/system/user/deptTree',
url: '/u/user/deptTree',
method: 'get'
});
}

View File

@@ -7,6 +7,7 @@ import { localStg } from '@/utils/storage';
import { $t } from '@/locales';
import { useRouteStore } from '../route';
import { clearAuthStorage, emptyInfo, getToken } from './shared';
import {sendCaptcha} from "@/service/api/auth";
export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
const routeStore = useRouteStore();
@@ -121,6 +122,34 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
}
return false;
}
/**
* Register new user
*/
async function register(registerForm: Api.Auth.RegisterBody) {
startLoading();
const { error } = await fetchRegister(registerForm);
if (!error) {
$message?.success($t('page.login.common.registerSuccess'));
// 注册成功后跳转到登录页
await toLogin();
}
endLoading();
return !error;
}
async function captcha(email:string){
if (!email) {
return;
}
try {
await sendCaptcha({ email }); // 这里调用后端接口发送验证码
} catch (error) {
}
}
return {
token,
@@ -131,6 +160,8 @@ export const useAuthStore = defineStore(SetupStoreId.Auth, () => {
resetStore,
permissions,
login,
refreshUserInfo
refreshUserInfo,
register,
captcha,
};
});

View File

@@ -2,12 +2,12 @@ import { computed, ref, shallowRef } from 'vue';
import type { RouteRecordRaw } from 'vue-router';
import { defineStore } from 'pinia';
import { useBoolean } from '@sa/hooks';
import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types';
import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey } from '@elegant-router/types';
import { SetupStoreId } from '@/enum';
import { router } from '@/router';
import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes';
import { ROOT_ROUTE } from '@/router/routes/builtin';
import { getRouteName, getRoutePath } from '@/router/elegant/transform';
import { getRoutePath } from '@/router/elegant/transform';
import { useAppStore } from '../app';
import { useAuthStore } from '../auth';
import { useTabStore } from '../tab';
@@ -17,7 +17,6 @@ import {
getCacheRouteNames,
getGlobalMenusByAuthRoutes,
getSelectedMenuKeyPathByKey,
isRouteExistByRouteName,
sortRoutesByOrder,
transformMenuToSearchMenus,
updateLocaleOfGlobalMenus
@@ -30,17 +29,9 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
const { bool: isInitConstantRoute, setBool: setIsInitConstantRoute } = useBoolean();
const { bool: isInitAuthRoute, setBool: setIsInitAuthRoute } = useBoolean();
/**
* Auth route mode
*
* It recommends to use static mode in the development environment, and use dynamic mode in the production
* environment, if use static mode in development environment, the auth routes will be auto generated by plugin
* "@elegant-router/vue"
*/
const authRouteMode = ref(import.meta.env.VITE_AUTH_ROUTE_MODE);
/** Home route key */
const routeHome = ref(import.meta.env.VITE_ROUTE_HOME);
const routeHome = ref('home');
/**
* Set route home
@@ -129,16 +120,6 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
addCacheRoutes(routeKey);
}
/**
* Re cache routes by route keys
*
* @param routeKeys
*/
async function reCacheRoutesByKeys(routeKeys: RouteKey[]) {
for await (const key of routeKeys) {
await reCacheRoutesByKey(key);
}
}
/** Global breadcrumbs */
const breadcrumbs = computed(() => getBreadcrumbsByRoute(router.currentRoute.value, menus.value));
@@ -184,19 +165,14 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
/** Init auth route */
async function initAuthRoute() {
if (authRouteMode.value === 'static') {
await initStaticAuthRoute();
} else {
await initDynamicAuthRoute();
}
await initStaticAuthRoute();
await initDynamicAuthRoute();
tabStore.initHomeTab();
}
/** Init static auth route */
async function initStaticAuthRoute() {
const { authRoutes: staticAuthRoutes } = createStaticRoutes();
if (authStore.isStaticSuper) {
addAuthRoutes(staticAuthRoutes);
} else {
@@ -213,13 +189,12 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
/** Init dynamic auth route */
async function initDynamicAuthRoute() {
const { data: routes, error } = await doGetUserRoutes();
if (!error) {
addAuthRoutes(routes);
handleAuthRoutes();
setRouteHome('manage_role');
setRouteHome('home');
handleUpdateRootRouteRedirect('manage_role');
@@ -284,28 +259,6 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
}
}
/**
* Get is auth route exist
*
* @param routePath Route path
*/
async function getIsAuthRouteExist(routePath: RouteMap[RouteKey]) {
const routeName = getRouteName(routePath);
if (!routeName) {
return false;
}
if (authRouteMode.value === 'static') {
const { authRoutes: staticAuthRoutes } = createStaticRoutes();
return isRouteExistByRouteName(routeName, staticAuthRoutes);
}
const { data } = await fetchIsRouteExist(routeName);
return data;
}
/**
* Get selected menu key path
*
@@ -335,14 +288,12 @@ export const useRouteStore = defineStore(SetupStoreId.Route, () => {
updateGlobalMenusByLocale,
cacheRoutes,
reCacheRoutesByKey,
reCacheRoutesByKeys,
breadcrumbs,
initConstantRoute,
isInitConstantRoute,
initAuthRoute,
isInitAuthRoute,
setIsInitAuthRoute,
getIsAuthRouteExist,
getSelectedMenuKeyPath,
getSelectedMenuMetaByKey
};

View File

@@ -20,12 +20,7 @@ export function initThemeSettings() {
const settings = localStg.get('themeSettings') || themeSettings;
const isOverride = localStg.get('overrideThemeFlag') === BUILD_TIME;
if (!isOverride) {
Object.assign(settings, overrideThemeSettings);
localStg.set('overrideThemeFlag', BUILD_TIME);
}
Object.assign(settings, overrideThemeSettings);
return settings;
}

View File

@@ -2,34 +2,12 @@
export const themeSettings: App.Theme.ThemeSetting = {
themeScheme: 'light',
themeColor: '#646cff',
otherColor: {
info: '#2080f0',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d'
},
otherColor: { info: '#2080f0', success: '#52c41a', warning: '#faad14', error: '#f5222d' },
isInfoFollowPrimary: true,
layout: {
mode: 'vertical',
scrollMode: 'content'
},
page: {
animate: true,
animateMode: 'fade-slide'
},
header: {
height: 56,
breadcrumb: {
visible: true,
showIcon: true
}
},
tab: {
visible: true,
cache: true,
height: 44,
mode: 'chrome'
},
layout: { mode: 'horizontal-mix', scrollMode: 'content' },
page: { animate: true, animateMode: 'fade-slide' },
header: { height: 56, breadcrumb: { visible: true, showIcon: true } },
tab: { visible: false, cache: true, height: 44, mode: 'chrome' },
fixedHeaderAndTab: true,
sider: {
inverted: false,
@@ -39,12 +17,7 @@ export const themeSettings: App.Theme.ThemeSetting = {
mixCollapsedWidth: 64,
mixChildMenuWidth: 200
},
footer: {
visible: false,
fixed: false,
height: 48,
right: true
}
footer: { visible: true, fixed: false, height: 36, right: true }
};
/**

14
src/typings/api.d.ts vendored
View File

@@ -138,6 +138,20 @@ declare namespace Api {
uuid: string;
authType: string;
}
interface RegisterBody{
username: string;
password: string;
authType:string;
email: string;
fullName: string;
age: number;
address: string;
sex: string;
phonenumber: string;
}
interface EmailCaptcha{
email:string;
}
}
/**

View File

@@ -358,6 +358,7 @@ declare namespace App {
back: string;
validateSuccess: string;
loginSuccess: string;
registerSuccess: string;
welcomeBack: string;
checkCode: string;
};

View File

@@ -20,6 +20,7 @@ declare global {
const beforeAll: typeof import('vitest')['beforeAll']
const beforeEach: typeof import('vitest')['beforeEach']
const chai: typeof import('vitest')['chai']
const checkReport: typeof import('../service/api/auth')['checkReport']
const clearAuthStorage: typeof import('../store/modules/auth/shared')['clearAuthStorage']
const cloneDeep: typeof import('lodash-es')['cloneDeep']
const computed: typeof import('vue')['computed']
@@ -96,6 +97,7 @@ declare global {
const fetchIsRouteExist: typeof import('../service/api/route')['fetchIsRouteExist']
const fetchLogin: typeof import('../service/api/auth')['fetchLogin']
const fetchRefreshToken: typeof import('../service/api/auth')['fetchRefreshToken']
const fetchRegister: typeof import('../service/api/auth')['fetchRegister']
const filterAuthRoutesByRoles: typeof import('../store/modules/route/shared')['filterAuthRoutesByRoles']
const filterTabsById: typeof import('../store/modules/tab/shared')['filterTabsById']
const filterTabsByIds: typeof import('../store/modules/tab/shared')['filterTabsByIds']
@@ -182,6 +184,7 @@ declare global {
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const sendCaptcha: typeof import('../service/api/auth')['sendCaptcha']
const sessionStg: typeof import('../utils/storage')['sessionStg']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']

View File

@@ -13,6 +13,7 @@ declare module 'vue' {
ACard: typeof import('ant-design-vue/es')['Card']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACol: typeof import('ant-design-vue/es')['Col']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADescriptions: typeof import('ant-design-vue/es')['Descriptions']
ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem']
ADivider: typeof import('ant-design-vue/es')['Divider']
@@ -45,6 +46,8 @@ declare module 'vue' {
ASpace: typeof import('ant-design-vue/es')['Space']
ASpin: typeof import('ant-design-vue/es')['Spin']
AStatistic: typeof import('ant-design-vue/es')['Statistic']
AStep: typeof import('ant-design-vue/es')['Step']
ASteps: typeof import('ant-design-vue/es')['Steps']
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATable: typeof import('ant-design-vue/es')['Table']
ATag: typeof import('ant-design-vue/es')['Tag']

View File

@@ -11,139 +11,47 @@ declare module "@elegant-router/types" {
*/
export type RouteLayout = "base" | "blank";
/**
* route map
*/
export type RouteMap = {
"root": "/";
"not-found": "/:pathMatch(.*)*";
"exception": "/exception";
"exception_403": "/exception/403";
"exception_404": "/exception/404";
"exception_500": "/exception/500";
"403": "/403";
"404": "/404";
"500": "/500";
"about": "/about";
"function": "/function";
"function_hide-child": "/function/hide-child";
"function_hide-child_one": "/function/hide-child/one";
"function_hide-child_three": "/function/hide-child/three";
"function_hide-child_two": "/function/hide-child/two";
"function_multi-tab": "/function/multi-tab";
"function_request": "/function/request";
"function_super-page": "/function/super-page";
"function_tab": "/function/tab";
"function_toggle-auth": "/function/toggle-auth";
"home": "/home";
"login": "/login/:module(pwd-login|code-login|register|reset-pwd|bind-wechat)?";
"manage": "/manage";
"manage_dept": "/manage/dept";
"manage_dict": "/manage/dict";
"manage_menu": "/manage/menu";
"manage_post": "/manage/post";
"manage_role": "/manage/role";
"manage_route": "/manage/route";
"manage_user": "/manage/user";
"manage_user-detail": "/manage/user-detail/:id";
"user-center": "/user-center";
};
/**
* route key
*/
export type RouteKey = keyof RouteMap;
export type RouteKey = string;
/**
* route path
*/
export type RoutePath = RouteMap[RouteKey];
export type RoutePath = string;
export type RouteMap =Record<string,string>;
/**
* custom route key
*/
export type CustomRouteKey = Extract<
RouteKey,
| "root"
| "not-found"
| "exception"
| "exception_403"
| "exception_404"
| "exception_500"
>;
*/
export type CustomRouteKey = string;
/**
* the generated route key
*/
*/
export type GeneratedRouteKey = Exclude<RouteKey, CustomRouteKey>;
/**
* the first level route key, which contain the layout of the route
*/
export type FirstLevelRouteKey = Extract<
RouteKey,
| "403"
| "404"
| "500"
| "about"
| "function"
| "home"
| "login"
| "manage"
| "user-center"
>;
export type FirstLevelRouteKey = string;
/**
* the custom first level route key
*/
export type CustomFirstLevelRouteKey = Extract<
CustomRouteKey,
| "root"
| "not-found"
| "exception"
>;
export type CustomFirstLevelRouteKey = string;
/**
* the last level route key, which has the page file
*/
export type LastLevelRouteKey = Extract<
RouteKey,
| "403"
| "404"
| "500"
| "login"
| "about"
| "function_hide-child_one"
| "function_hide-child_three"
| "function_hide-child_two"
| "function_multi-tab"
| "function_request"
| "function_super-page"
| "function_tab"
| "function_toggle-auth"
| "home"
| "manage_dept"
| "manage_dict"
| "manage_menu"
| "manage_post"
| "manage_role"
| "manage_route"
| "manage_user-detail"
| "manage_user"
| "user-center"
>;
export type LastLevelRouteKey = string
/**
* the custom last level route key
*/
export type CustomLastLevelRouteKey = Extract<
CustomRouteKey,
| "root"
| "not-found"
| "exception_403"
| "exception_404"
| "exception_500"
>;
export type CustomLastLevelRouteKey = string;
/**
* the single level route key
@@ -190,8 +98,10 @@ declare module "@elegant-router/types" {
type SingleLevelRoute<K extends SingleLevelRouteKey = SingleLevelRouteKey> = K extends string
? Omit<ElegantConstRoute, 'children'> & {
name: K;
path: RouteMap[K];
component: `layout.${RouteLayout}$view.${K}`;
path: K;
meta?: Record<K, any>;
component?: `layout.${RouteLayout}$view.${K}` | `layout.${RouteLayout}` | `view.${K}` ;
children?: SingleLevelRoute[];
}
: never;
@@ -201,18 +111,18 @@ declare module "@elegant-router/types" {
type LastLevelRoute<K extends GeneratedRouteKey> = K extends LastLevelRouteKey
? Omit<ElegantConstRoute, 'children'> & {
name: K;
path: RouteMap[K];
path: string;
component: `view.${K}`;
}
: never;
/**
* the center level route
*/
type CenterLevelRoute<K extends GeneratedRouteKey> = K extends CenterLevelRouteKey
? Omit<ElegantConstRoute, 'component'> & {
name: K;
path: RouteMap[K];
path: string;
children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[];
}
: never;
@@ -223,19 +133,19 @@ declare module "@elegant-router/types" {
type MultiLevelRoute<K extends FirstLevelRouteNotSingleKey = FirstLevelRouteNotSingleKey> = K extends string
? ElegantConstRoute & {
name: K;
path: RouteMap[K];
path: K;
component: `layout.${RouteLayout}`;
children: (CenterLevelRoute<GetChildRouteKey<K>> | LastLevelRoute<GetChildRouteKey<K>>)[];
}
: never;
/**
* the custom first level route
*/
type CustomSingleLevelRoute<K extends CustomFirstLevelRouteKey = CustomFirstLevelRouteKey> = K extends string
? Omit<ElegantConstRoute, 'children'> & {
name: K;
path: RouteMap[K];
path: string;
component?: `layout.${RouteLayout}$view.${LastLevelRouteKey}`;
}
: never;
@@ -246,7 +156,7 @@ declare module "@elegant-router/types" {
type CustomLastLevelRoute<K extends CustomRouteKey> = K extends CustomLastLevelRouteKey
? Omit<ElegantConstRoute, 'children'> & {
name: K;
path: RouteMap[K];
path: string;
component?: `view.${LastLevelRouteKey}`;
}
: never;
@@ -257,7 +167,7 @@ declare module "@elegant-router/types" {
type CustomCenterLevelRoute<K extends CustomRouteKey> = K extends CustomCenterLevelRouteKey
? Omit<ElegantConstRoute, 'component'> & {
name: K;
path: RouteMap[K];
path: string;
children: (CustomCenterLevelRoute<GetChildRouteKey<K>> | CustomLastLevelRoute<GetChildRouteKey<K>>)[];
}
: never;
@@ -269,7 +179,7 @@ declare module "@elegant-router/types" {
K extends string
? ElegantConstRoute & {
name: K;
path: RouteMap[K];
path: string;
component: `layout.${RouteLayout}`;
children: (CustomCenterLevelRoute<GetChildRouteKey<K>> | CustomLastLevelRoute<GetChildRouteKey<K>>)[];
}
@@ -278,7 +188,7 @@ declare module "@elegant-router/types" {
/**
* the custom route
*/
type CustomRoute = CustomSingleLevelRoute | CustomMultiLevelRoute;
type CustomRoute = CustomSingleLevelRoute | CustomMultiLevelRoute | any;
/**
* the generated route

View File

@@ -13,8 +13,6 @@ declare namespace Env {
readonly VITE_BASE_URL: string;
/** The title of the application */
readonly VITE_APP_TITLE: string;
/** The description of the application */
readonly VITE_APP_DESC: string;
/** The router history mode */
readonly VITE_ROUTER_HISTORY_MODE?: RouterHistoryMode;
/** The prefix of the iconify icon */
@@ -78,13 +76,6 @@ declare namespace Env {
* - Dynamic: the auth routes is generated in back-end
*/
readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic';
/**
* The home route key
*
* It only has effect when the auth route mode is static, if the route mode is dynamic, the home route key is
* defined in the back-end
*/
readonly VITE_ROUTE_HOME: import('@elegant-router/types').LastLevelRouteKey;
/**
* Default menu icon if menu icon is not set
*

View File

@@ -20,6 +20,3 @@ interface Document {
interface ImportMeta {
readonly env: Env.ImportMeta;
}
/** Build time of the project */
declare const BUILD_TIME: string;

View File

@@ -2,7 +2,6 @@
import { computed } from 'vue';
import type { Component } from 'vue';
import { getColorPalette, mixColor } from '@sa/utils';
import { $t } from '@/locales';
import { useThemeStore } from '@/store/modules/theme';
import { loginModuleRecord } from '@/constants/app';
import PwdLogin from './modules/pwd-login.vue';
@@ -10,14 +9,16 @@ import CodeLogin from './modules/code-login.vue';
import Register from './modules/register.vue';
import ResetPwd from './modules/reset-pwd.vue';
import BindWechat from './modules/bind-wechat.vue';
import { WifiOutlined } from '@ant-design/icons-vue';
import {useI18n} from "vue-i18n";
//import { $t } from '@/locales';
interface Props {
/** The login module */
module?: UnionKey.LoginModule;
}
const props = defineProps<Props>();
const {t} = useI18n();
const themeStore = useThemeStore();
interface LoginModule {
@@ -54,12 +55,12 @@ const bgColor = computed(() => {
<ACard class="relative z-4">
<div class="w-400px lt-sm:w-300px">
<header class="flex-y-center justify-between">
<SystemLogo class="text-64px text-primary lt-sm:text-48px" />
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ $t('system.title') }}</h3>
<WifiOutlined class="text-64px text-primary lt-sm:text-48px" style="margin-left: 9px"/>
<h3 class="text-18px text-primary font-medium">{{ t(activeModule.label) }}</h3>
</header>
<main class="pt-24px">
<h3 class="text-18px text-primary font-medium">{{ $t(activeModule.label) }}</h3>
<div class="animation-slide-in-left pt-24px">
<main >
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ "WANFi" }}</h3>
<div class="animation-slide-in-left ">
<Transition :name="themeStore.page.animateMode" mode="out-in" appear>
<component :is="activeModule.component" />
</Transition>

View File

@@ -1,25 +1,27 @@
<script setup lang="ts">
import { $t } from '@/locales';
import { loginModuleRecord } from '@/constants/app';
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { useAuthStore } from '@/store/modules/auth';
import {useI18n} from "vue-i18n";
defineOptions({
name: 'PwdLogin'
});
const { t } = useI18n();
const authStore = useAuthStore();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { patternRules } = useFormRules();
const codeImg = ref('');
getCheckCode();
const model = reactive({
username: 'ryadmin',
password: 'admin123',
username: '123456',
password: '123456',
code: '',
uuid: '',
authType: 'sys'
@@ -31,11 +33,11 @@ const rules = {
};
async function handleSubmit() {
await validate();
await validate();//验证表单内容
await authStore.login({
loginForm: model,
loginForm: model,//发送表单的数据
onError() {
getCheckCode();
getCheckCode();//重新获取验证码
}
});
}
@@ -45,6 +47,9 @@ async function getCheckCode() {
if (!error) {
codeImg.value = `data:image/png;base64,${data.img}`;
model.uuid = data.uuid;
if (data?.text) {
model.code = data.text;
}
}
}
</script>
@@ -73,18 +78,18 @@ async function getCheckCode() {
<ASpace direction="vertical" size="large" class="w-full">
<div class="flex-y-center justify-between">
<ACheckbox>{{ $t('page.login.pwdLogin.rememberMe') }}</ACheckbox>
<AButton type="text" @click="toggleLoginModule('reset-pwd')">{{ $t('page.login.pwdLogin.forgetPassword') }}</AButton>
<AButton type="text" @click="toggleLoginModule('reset-pwd')">{{ t('page.login.pwdLogin.forgetPassword') }}</AButton>
</div>
<AButton type="primary" block size="large" shape="round" :loading="authStore.loginLoading" @click="handleSubmit">
{{ $t('common.confirm') }}
</AButton>
<div class="flex-y-center justify-between">
<AButton class="h-34px flex-1" block @click="toggleLoginModule('code-login')">
{{ $t(loginModuleRecord['code-login']) }}
</AButton>
<!-- <AButton class="h-34px flex-1" block @click="toggleLoginModule('code-login')">-->
<!-- {{ t(loginModuleRecord['code-login']) }}-->
<!-- </AButton>-->
<div class="w-12px"></div>
<AButton class="h-34px flex-1" block @click="toggleLoginModule('register')">
{{ $t(loginModuleRecord.register) }}
{{ t(loginModuleRecord.register) }}
</AButton>
</div>
</ASpace>

View File

@@ -1,86 +1,445 @@
<script setup lang="ts">
import { computed, reactive } from 'vue';
import { $t } from '@/locales';
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { useCaptcha } from '@/hooks/business/captcha';
import {computed, reactive, ref} from 'vue';
import {useI18n} from 'vue-i18n'; // 添加这行
import {useAuthStore} from '@/store/modules/auth';
import {useRouterPush} from '@/hooks/common/router';
import {useAntdForm, useFormRules} from '@/hooks/common/form';
import {useCaptcha} from '@/hooks/business/captcha';
import {useWindowSize} from '@vueuse/core';
import {registerTerms} from '@/views/_builtin/login/modules/terms';
import dayjs from 'dayjs';
import type {Rule} from 'ant-design-vue/es/form';
const { t } = useI18n();
const authStore = useAuthStore();
defineOptions({
name: 'CodeLogin'
name: 'Register'
});
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { label, isCounting, loading, getCaptcha } = useCaptcha();
interface FormModel {
const { width } = useWindowSize();
const isMobile = computed(() => width.value <= 640);
// 当前步骤
const currentStep = ref(0);
// 是否同意协议
const agreeTerms = ref(false);
// 定义一个统一的数据模型
interface RegisterModel {
username: string;
password: string;
email: string;
fullName: string;
age:0,
gender: string;
phone: string;
address: string;
code: string;
authType: string;
}
// 使用一个统一的 model
const model = reactive<RegisterModel>({
username: '',
password: '',
email: '',
fullName: '',
age:0,
gender: '',
phone: '',
address: '',
code: '',
authType: 'u'
});
// 第一步表单数据
interface BasicFormModel {
username: string;
fullName: string;
birthDate: string;
gender: string;
phone: string;
email: string;
address: string;
}
const basicModel = reactive<BasicFormModel>({
username: '',
fullName: '',
birthDate: '',
gender: '',
phone: '',
email: '',
address: ''
});
// 第三表单数据
interface SecurityFormModel {
email: string;
code: string;
password: string;
confirmPassword: string;
}
const model: FormModel = reactive({
phone: '',
const securityModel = reactive<SecurityFormModel>({
email: '',
code: '',
password: '',
confirmPassword: ''
});
const rules = computed<Record<keyof FormModel, App.Global.FormRule[]>>(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
// 第一步表单验证规则
const basicRules = computed<Record<string, Rule | Rule[]>>(() => {
const { formRules } = useFormRules();
return {
phone: formRules.phone,
code: formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(model.password)
username: formRules.username,
email: formRules.email,
birthDate: [{ required: true, message: t('page.login.register.birthDateRequired') }],
phone: [{ pattern: /^1[3-9]\d{9}$/, message: t('form.phone.invalid'), trigger: 'blur' }]
};
});
async function handleSubmit() {
await validate();
// request to register
$message?.success($t('page.login.common.validateSuccess'));
// 第三步表单验证规则
const securityRules = computed<Record<string, Rule | Rule[]>>(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
return {
code: formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(securityModel.password)
};
});
// 协议内容
const terms = computed(() => {
const locale = useI18n().locale.value;
return registerTerms[locale === 'zh-CN' ? 'zh-CN' : 'en-US'];
});
// 步骤控制函数
async function nextStep() {
if (currentStep.value === 0) {
await validate();
// 复制电话号码到第三步
securityModel.email = basicModel.email;
}
if (currentStep.value === 1) {
if (!agreeTerms.value) {
window.$message?.error(t('page.login.register.agreeTermsFirst'));
return;
}
}
currentStep.value += 1;
}
function prevStep() {
currentStep.value -= 1;
}
//注册按钮
async function handleSubmit() {
try {
await validate();
// 整合表单数据
model.username = basicModel.username;
model.password = securityModel.password;
model.email = securityModel.email;
model.fullName = basicModel.fullName;
model.gender = basicModel.gender; // 直接使用 gender 值
model.phone = basicModel.phone;
model.address = basicModel.address;
model.code = securityModel.code;
const success = await authStore.register({
...model,
age: dayjs().diff(dayjs(basicModel.birthDate), 'year'),
sex: model.gender, // 直接使用 gender 值,不需要转换
phonenumber: model.phone,
});
if (success) {
window.$message?.success(t('page.login.register.registerSuccess'));
toggleLoginModule('pwd-login');
}
} catch (error) {
console.error('Form validation failed:', error);
}
}
// 在script setup部分添加一个新的计算属性
const showSteps = computed(() => !isMobile.value);
</script>
<template>
<AForm ref="formRef" :model="model" :rules="rules">
<AFormItem name="phone">
<AInput v-model:value="model.phone" size="large" :placeholder="$t('page.login.common.phonePlaceholder')" />
</AFormItem>
<AFormItem name="code">
<div class="w-full flex-y-center gap-16px">
<AInput v-model:value="model.code" size="large" :placeholder="$t('page.login.common.codePlaceholder')" />
<AButton size="large" :disabled="isCounting" :loading="loading" @click="getCaptcha(model.phone)">
{{ label }}
</AButton>
<ASteps
v-if="showSteps"
:current="currentStep"
size="small"
class="max-w-full mb-16px"
direction="horizontal"
:responsive="false"
>
<AStep :title="t('page.login.register.basicInfo')" />
<AStep :title="t('page.login.register.terms')" />
<AStep :title="t('page.login.register.security')" />
</ASteps>
<div v-else class="mobile-step-indicator mb-16px text-center">
{{ currentStep + 1 }}/3: {{
currentStep === 0
? t('page.login.register.basicInfo')
: currentStep === 1
? t('page.login.register.terms')
: t('page.login.register.security')
}}
</div>
<div class="step-content">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0">
<AForm
ref="formRef"
:model="basicModel"
:rules="basicRules"
:label-wrap="true"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
>
<ARow :gutter="[8,2]">
<ACol :span="24" :lg="24">
<AFormItem name="username" :label="t('page.login.register.username')">
<AInput v-model:value="basicModel.username" />
</AFormItem>
</ACol>
<ACol :span="24" :lg="24">
<AFormItem name="fullName" :label="t('page.login.register.fullName')">
<AInput v-model:value="basicModel.fullName" />
</AFormItem>
</ACol>
<ACol :xs="12" :sm="12" :lg="24">
<AFormItem name="birthDate" :label="t('page.login.register.birthDate')">
<ADatePicker
v-model:value="basicModel.birthDate"
class="!w-full birth-date-picker"
:placeholder="t('page.login.register.birthDatePlaceholder')"
:disabled-date="(current: dayjs.Dayjs) => current && current.isAfter(dayjs())"
/>
</AFormItem>
</ACol>
<ACol :xs="12" :sm="12" :lg="24">
<AFormItem name="gender" :label="t('page.login.register.gender')">
<ASelect v-model:value="basicModel.gender">
<ASelectOption value="male">{{ t('page.login.register.male') }}</ASelectOption>
<ASelectOption value="female">{{ t('page.login.register.female') }}</ASelectOption>
</ASelect>
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="phone" :label="t('page.login.register.phone')">
<AInput v-model:value="basicModel.phone" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="email" :label="t('page.login.register.email')">
<AInput v-model:value="basicModel.email" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<AFormItem name="address" :label="t('page.login.register.address')">
<ATextarea v-model:value="basicModel.address" :rows="2" />
</AFormItem>
</ACol>
<ACol :lg="24" :span="24">
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" @click="nextStep">
{{ t('page.login.register.next') }}
</AButton>
<AButton block size="small" @click="toggleLoginModule('pwd-login')">
{{ t('page.login.common.back') }}
</AButton>
</ASpace>
</ACol>
</ARow>
</AForm>
</div>
<!-- 第二步:协议 -->
<div v-if="currentStep === 1">
<ATextarea
:value="terms"
:rows="12"
readonly
class="mb-16px"
size="small"
:style="{ fontSize: '14px', lineHeight: '1.6' }"
/>
<div class="mb-16px">
<ACheckbox
v-model:checked="agreeTerms"
class="terms-checkbox"
>
{{ t('page.login.register.agreeTerms') }}
</ACheckbox>
</div>
</AFormItem>
<AFormItem name="password">
<AInputPassword
v-model:value="model.password"
size="large"
:placeholder="$t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword">
<AInputPassword
v-model:value="model.confirmPassword"
size="large"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<ASpace direction="vertical" size="large" class="w-full">
<AButton type="primary" block size="large" shape="round" @click="handleSubmit">
{{ $t('common.confirm') }}
</AButton>
<AButton block size="large" shape="round" @click="toggleLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
</AButton>
</ASpace>
</AForm>
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" :disabled="!agreeTerms" @click="nextStep">
{{ t('page.login.register.next') }}
</AButton>
<AButton block size="small" @click="prevStep">
{{ t('page.login.register.prev') }}
</AButton>
</ASpace>
</div>
<!-- 第三步:安全信息 -->
<div v-if="currentStep === 2">
<AForm
ref="formRef"
:model="securityModel"
:rules="securityRules"
:label-wrap="true"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
class="compact-form"
>
<AFormItem name="email" :label="t('page.login.register.email')">
<AInput
v-model:value="securityModel.email"
:placeholder="t('page.login.common.emailPlaceholder')"
/>
</AFormItem>
<AFormItem name="code" :label="t('page.login.register.code')">
<div class="w-full flex-y-center gap-8px">
<AInput
v-model:value="securityModel.code"
:placeholder="t('page.login.common.codePlaceholder')"
/>
<AButton
size="small"
:disabled="isCounting"
:loading="loading"
@click="getCaptcha(securityModel.email)"
>
{{ label }}
</AButton>
</div>
</AFormItem>
<AFormItem name="password" :label="t('page.login.register.password')">
<AInputPassword
v-model:value="securityModel.password"
:placeholder="t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword" :label="t('page.login.register.confirmPassword')">
<AInputPassword
v-model:value="securityModel.confirmPassword"
:placeholder="t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<AFormItem :wrapper-col="{ xs: { span: 24 }, sm: { span: 24 } }">
<ASpace direction="vertical" size="small" class="w-full">
<AButton type="primary" block size="small" @click="handleSubmit">
{{ t('common.confirm') }}
</AButton>
<AButton block size="small" @click="prevStep">
{{ t('page.login.register.prev') }}
</AButton>
</ASpace>
</AFormItem>
</AForm>
</div>
</div>
</template>
<style scoped></style>
<style scoped>
@media (max-width: 640px) {
:deep(.ant-form) {
.ant-form-item {
margin-bottom: 4px !important;
}
.ant-form-item-label {
padding: 0 !important;
line-height: 28px !important;
> label {
font-size: 13px !important;
height: 28px !important;
padding-bottom: 0 !important;
}
}
.ant-input,
.ant-input-password,
.ant-select,
.ant-input-number,
.ant-btn {
font-size: 13px !important;
height: 28px !important;
line-height: 28px !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.ant-select-selector {
height: 28px !important;
padding: 0 8px !important;
.ant-select-selection-item {
line-height: 26px !important;
}
}
.ant-input-number {
height: 32px !important;
input {
height: 30px !important;
padding: 0 8px !important;
}
}
}
.mobile-step-indicator {
font-size: 13px;
padding: 4px 8px;
margin-bottom: 8px;
text-align: center;
width: 100%;
border-radius: 4px;
}
:deep(.ant-space) {
gap: 4px !important;
}
}
:deep(.terms-checkbox) {
.ant-checkbox + span {
font-size: 14px;
color: rgba(0, 0, 0, 0.65);
}
}
:deep(.ant-input[readonly]) {
background-color: #f5f5f5;
cursor: default;
}
:deep(.ant-form-item-label) {
white-space: normal;
text-align: left;
> label {
height: auto !important;
padding-bottom: 4px;
}
}
.birth-date-picker {
margin-top: 2px !important;
}
</style>

View File

@@ -1,25 +1,27 @@
<script setup lang="ts">
import { computed, reactive } from 'vue';
import { $t } from '@/locales';
import { useRouterPush } from '@/hooks/common/router';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import {useCaptcha} from "@/hooks/business/captcha";
import {useI18n} from "vue-i18n";
defineOptions({
name: 'ResetPwd'
});
const { t } = useI18n();
const { toggleLoginModule } = useRouterPush();
const { formRef, validate } = useAntdForm();
const { label, isCounting, loading, getCaptcha } = useCaptcha();
interface FormModel {
phone: string;
email: string;
code: string;
password: string;
confirmPassword: string;
}
const model: FormModel = reactive({
phone: '',
email: '',
code: '',
password: '',
confirmPassword: ''
@@ -31,7 +33,9 @@ const rules = computed<RuleRecord>(() => {
const { formRules, createConfirmPwdRule } = useFormRules();
return {
phone: formRules.phone,
//phone: formRules.phone,
email:formRules.email,
code:formRules.code,
password: formRules.pwd,
confirmPassword: createConfirmPwdRule(model.password)
};
@@ -40,38 +44,48 @@ const rules = computed<RuleRecord>(() => {
async function handleSubmit() {
await validate();
// request to reset password
$message?.success($t('page.login.common.validateSuccess'));
$message?.success(t('page.login.common.validateSuccess'));
}
</script>
<template>
<AForm ref="formRef" :model="model" :rules="rules">
<AFormItem name="phone">
<AInput v-model:value="model.phone" size="large" :placeholder="$t('page.login.common.phonePlaceholder')" />
<AFormItem name="email">
<AInput v-model:value="model.email" size="large" :placeholder="t('page.login.common.emailPlaceholder')" />
</AFormItem>
<AFormItem name="code">
<AInput v-model:value="model.code" size="large" :placeholder="$t('page.login.common.codePlaceholder')" />
<AFormItem name="code" >
<div class="w-full flex-y-center gap-8px">
<AInput v-model:value="model.code" size="large" :placeholder="t('page.login.common.codePlaceholder')" />
<AButton
size="small"
:disabled="isCounting"
:loading="loading"
@click="getCaptcha(model.email)"
>
{{ label }}
</AButton>
</div>
</AFormItem>
<AFormItem name="password">
<AInputPassword
v-model:value="model.password"
size="large"
:placeholder="$t('page.login.common.passwordPlaceholder')"
:placeholder="t('page.login.common.passwordPlaceholder')"
/>
</AFormItem>
<AFormItem name="confirmPassword">
<AInputPassword
v-model:value="model.confirmPassword"
size="large"
:placeholder="$t('page.login.common.confirmPasswordPlaceholder')"
:placeholder="t('page.login.common.confirmPasswordPlaceholder')"
/>
</AFormItem>
<ASpace direction="vertical" size="large" class="w-full">
<AButton type="primary" block size="large" shape="round" @click="handleSubmit">
{{ $t('common.confirm') }}
{{ t('common.confirm') }}
</AButton>
<AButton block size="large" shape="round" @click="toggleLoginModule('pwd-login')">
{{ $t('page.login.common.back') }}
{{ t('page.login.common.back') }}
</AButton>
</ASpace>
</AForm>

View File

@@ -0,0 +1,50 @@
export const registerTerms = {
'zh-CN': `
用户协议和隐私政策
1. 总则
本协议是您与我们之间关于使用我们的服务所订立的协议。您注册成为用户,表示您已充分阅读、理解并同意接受本协议的全部内容。
2. 服务内容
2.1 我们将为您提供安全、可靠的服务。
2.2 我们保留随时修改或中断服务的权利。
3. 用户义务
3.1 用户应当遵守相关法律法规。
3.2 用户应当遵守社区规范和道德准则。
3.3 用户应当保护账号安全,不得将账号借给他人使用。
4. 隐私保护
4.1 我们将依法保护您的个人信息安全。
4.2 未经您的同意,我们不会向第三方披露您的个人信息。
4.3 我们采用业界标准的安全技术来保护您的个人信息。
5. 免责声明
5.1 因不可抗力导致的服务中断,本平台不承担责任。
5.2 用户因违反本协议造成的损失由用户自行承担。
`,
'en-US': `
User Agreement and Privacy Policy
1. General Provisions
This agreement is established between you and us regarding the use of our services. By registering as a user, you indicate that you have fully read, understood and agreed to accept all contents of this agreement.
2. Service Content
2.1 We will provide you with secure and reliable services.
2.2 We reserve the right to modify or interrupt services at any time.
3. User Obligations
3.1 Users shall comply with relevant laws and regulations.
3.2 Users shall comply with community standards and ethical guidelines.
3.3 Users shall protect account security and not lend their accounts to others.
4. Privacy Protection
4.1 We will protect your personal information security in accordance with law.
4.2 We will not disclose your personal information to third parties without your consent.
4.3 We use industry-standard security technology to protect your personal information.
5. Disclaimer
5.1 The platform is not liable for service interruptions caused by force majeure.
5.2 Users shall bear losses caused by their violation of this agreement.
`
};

View File

@@ -31,7 +31,7 @@ const pkgJson: PkgJson = {
devDependencies: Object.entries(devDependencies).map(item => transformVersionData(item))
};
const latestBuildTime = BUILD_TIME;
const latestBuildTime = "----";
</script>
<template>

View File

@@ -1,6 +1,4 @@
<script lang="ts" setup>
import type {} from 'ant-design-vue';
import { generatedRoutes } from '@/router/elegant/routes';
import { $t } from '@/locales';
import type { MenuModelType } from './form';
import { formRules, menuStatusOptions, menuTypeOptions, resetAddForm } from './form';
@@ -30,24 +28,6 @@ const title = computed(() => {
return titles[props.operateType];
});
// TODO: 根据菜单类型动态加载组件路径、目录类型只允许选择带有子元素的,菜单类型只允许选择没有子元素的
const componentOptions = computed(() => {
const excludePaths = ['/404', '/403', '/500'];
function transformRoutes(routes: any[]): any[] {
return routes.filter(route => {
if (route.children) {
route.children = transformRoutes(route.children);
return true;
}
if (!route.hideInMenu && !excludePaths.includes(route.path) && !route.path.startsWith('/login')) {
return true;
}
return false;
});
}
return transformRoutes(generatedRoutes);
});
watch(visible, val => {
if (val) {
@@ -95,10 +75,6 @@ async function getTreeData() {
}
}
function handleTreeSelect(node: any) {
model.value.component = node.component;
model.value.name = node.name;
}
getTreeData();
</script>
@@ -139,20 +115,9 @@ getTreeData();
</ARadioGroup>
</AFormItem>
<AFormItem v-if="model.menuType !== 'F'" label="菜单路径" name="path">
<div>
<!-- @vue-ignore -->
<ATreeSelect
v-if="model.isFrame === '1'"
v-model:value="model.path"
show-search
:field-names="{ value: 'path', label: 'path' }"
allow-clear
:tree-data="componentOptions"
tree-node-filter-prop="label"
@select="(_val, node) => handleTreeSelect(node)"
/>
<AInput v-else v-model:value="model.path" />
</div>
path <AInput v-model:value="model.path" />
component <AInput v-model:value="model.component" />
name <AInput v-model:value="model.name" />
</AFormItem>
<AFormItem v-if="model.menuType === 'C'" label="隐藏菜单" name="hideInMenu">
<ASwitch v-model:checked="model.hideInMenu" checked-value="0" un-checked-value="1" />

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>user_vip</div>
</template>
<style scoped></style>