Merge pull request #78 from kailong321200875/node-admin
This commit is contained in:
commit
43518532f1
|
@ -38,8 +38,8 @@ module.exports = defineConfig({
|
||||||
'@typescript-eslint/ban-types': 'off',
|
'@typescript-eslint/ban-types': 'off',
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': 'error',
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
'no-unused-vars': 'error',
|
'no-unused-vars': 'off',
|
||||||
'space-before-function-paren': 'off',
|
'space-before-function-paren': 'off',
|
||||||
|
|
||||||
'vue/attributes-order': 'off',
|
'vue/attributes-order': 'off',
|
||||||
|
@ -63,6 +63,7 @@ module.exports = defineConfig({
|
||||||
math: 'always'
|
math: 'always'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'vue/multi-word-component-names': 'off'
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/no-v-html': 'off'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,4 +4,5 @@ dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
/dist*
|
/dist*
|
||||||
|
*-lock.*
|
||||||
pnpm-debug
|
pnpm-debug
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"i18n-ally.localesPaths": ["src/locales"],
|
"i18n-ally.localesPaths": ["src/locales"],
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
"i18n-ally.sortKeys": true,
|
"i18n-ally.sortKeys": true,
|
||||||
"i18n-ally.namespace": true,
|
"i18n-ally.namespace": false,
|
||||||
"i18n-ally.enabledParsers": ["ts"],
|
"i18n-ally.enabledParsers": ["ts"],
|
||||||
"i18n-ally.sourceLanguage": "en",
|
"i18n-ally.sourceLanguage": "en",
|
||||||
"i18n-ally.displayLanguage": "zh-CN",
|
"i18n-ally.displayLanguage": "zh-CN",
|
||||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -2,6 +2,26 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.4.5](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.4...v1.4.5) (2022-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix tagsview not work ([d88e051](https://github.com/kailong321200875/vue-element-plus-admin/commit/d88e0514349e877f1c5280a48f9b1bd2bfd622bf))
|
||||||
|
* fix tagsview not work ([1bf2d4c](https://github.com/kailong321200875/vue-element-plus-admin/commit/1bf2d4c77287fdca7ed1cb8c9998a53f1375dc6f))
|
||||||
|
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
* fix types error ([586486a](https://github.com/kailong321200875/vue-element-plus-admin/commit/586486a68d4bf2a024e50a79945b4007324f642d))
|
||||||
|
|
||||||
|
## [1.4.4](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.3...v1.4.4) (2022-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
* fix type error ([d66f12e](https://github.com/kailong321200875/vue-element-plus-admin/commit/d66f12e0e77f6acf485bae06509d9ea4abcd1eaa))
|
||||||
|
|
||||||
### [1.4.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.2...v1.4.3) (2022-06-01)
|
### [1.4.3](https://github.com/kailong321200875/vue-element-plus-admin/compare/v1.4.2...v1.4.3) (2022-06-01)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "vue-element-plus-admin",
|
"name": "vue-element-plus-admin",
|
||||||
"version": "1.4.3",
|
"version": "1.4.5",
|
||||||
"description": "一套基于vue3、element-plus、typesScript、vite2的后台集成方案。",
|
"description": "一套基于vue3、element-plus、typesScript、vite2的后台集成方案。",
|
||||||
"author": "Archer <502431556@qq.com>",
|
"author": "Archer <502431556@qq.com>",
|
||||||
"private": false,
|
"private": false,
|
||||||
|
@ -37,6 +37,7 @@
|
||||||
"echarts-wordcloud": "^2.0.0",
|
"echarts-wordcloud": "^2.0.0",
|
||||||
"element-plus": "2.2.6",
|
"element-plus": "2.2.6",
|
||||||
"intro.js": "^5.1.0",
|
"intro.js": "^5.1.0",
|
||||||
|
"js-md5": "^0.7.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
"@intlify/vite-plugin-vue-i18n": "^3.4.0",
|
"@intlify/vite-plugin-vue-i18n": "^3.4.0",
|
||||||
"@purge-icons/generated": "^0.8.1",
|
"@purge-icons/generated": "^0.8.1",
|
||||||
"@types/intro.js": "^3.0.2",
|
"@types/intro.js": "^3.0.2",
|
||||||
|
"@types/js-md5": "^0.4.3",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
|
|
7331
pnpm-lock.yaml
7331
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
||||||
|
export interface IUserModel {
|
||||||
|
user_name: string
|
||||||
|
password: string
|
||||||
|
check_password: string
|
||||||
|
is_admin: number
|
||||||
|
code?: string | number
|
||||||
|
token?: string
|
||||||
|
refreshToken?: string
|
||||||
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
import { useAxios } from '@/hooks/web/useAxios'
|
import { useAxios } from '@/hooks/web/useAxios'
|
||||||
import type { UserLoginType, UserType } from './types'
|
import type { UserType } from './types'
|
||||||
|
import { IUserModel } from '@/api-types/user'
|
||||||
|
|
||||||
const request = useAxios()
|
const request = useAxios()
|
||||||
|
|
||||||
export const loginApi = (data: UserLoginType) => {
|
export const loginApi = async (data: Pick<IUserModel, 'user_name' | 'password'>) => {
|
||||||
return request.post({
|
const res = await request.post<IResponse<IUserModel>>({
|
||||||
url: '/user/login',
|
url: '/user/login',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
|
return res && res.data
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loginOutApi = () => {
|
export const loginOutApi = () => {
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { useAxios } from '@/hooks/web/useAxios'
|
||||||
|
import { IUserModel } from '@/api-types/user'
|
||||||
|
|
||||||
|
const request = useAxios()
|
||||||
|
|
||||||
|
interface ICodeModel {
|
||||||
|
url: string
|
||||||
|
uuid: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCodeApi = async (): Promise<IResponse<ICodeModel>> => {
|
||||||
|
const res = await request.get({ url: 'user/captcha' })
|
||||||
|
return res && res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registerApi = async (
|
||||||
|
data: Omit<IUserModel, 'is_admin'>
|
||||||
|
): Promise<IResponse<IUserModel>> => {
|
||||||
|
const res = await request.post({ url: 'user/register', data })
|
||||||
|
return res && res.data
|
||||||
|
}
|
|
@ -219,7 +219,7 @@ export default defineComponent({
|
||||||
const { autoSetPlaceholder } = unref(getProps)
|
const { autoSetPlaceholder } = unref(getProps)
|
||||||
|
|
||||||
return slots[item.field] ? (
|
return slots[item.field] ? (
|
||||||
getSlot(slots, item.field, { item })
|
getSlot(slots, item.field, formModel.value)
|
||||||
) : (
|
) : (
|
||||||
<Com
|
<Com
|
||||||
vModel={formModel.value[item.field]}
|
vModel={formModel.value[item.field]}
|
||||||
|
@ -293,7 +293,7 @@ export default defineComponent({
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.@{elNamespace}-form.@{namespace}-form .@{elNamespace}-row {
|
.@{elNamespace}-form.@{namespace}-form .@{elNamespace}-row {
|
||||||
margin-left: 0 !important;
|
|
||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -14,7 +14,7 @@ const config: {
|
||||||
*/
|
*/
|
||||||
base_url: {
|
base_url: {
|
||||||
// 开发环境接口前缀
|
// 开发环境接口前缀
|
||||||
base: '',
|
base: '/api',
|
||||||
|
|
||||||
// 打包开发环境接口前缀
|
// 打包开发环境接口前缀
|
||||||
dev: '',
|
dev: '',
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
import axios, {
|
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
|
||||||
AxiosInstance,
|
|
||||||
AxiosRequestConfig,
|
|
||||||
AxiosRequestHeaders,
|
|
||||||
AxiosResponse,
|
|
||||||
AxiosError
|
|
||||||
} from 'axios'
|
|
||||||
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
@ -27,8 +21,7 @@ service.interceptors.request.use(
|
||||||
(config: AxiosRequestConfig) => {
|
(config: AxiosRequestConfig) => {
|
||||||
if (
|
if (
|
||||||
config.method === 'post' &&
|
config.method === 'post' &&
|
||||||
(config.headers as AxiosRequestHeaders)['Content-Type'] ===
|
config!.headers!['Content-Type'] === 'application/x-www-form-urlencoded'
|
||||||
'application/x-www-form-urlencoded'
|
|
||||||
) {
|
) {
|
||||||
config.data = qs.stringify(config.data)
|
config.data = qs.stringify(config.data)
|
||||||
}
|
}
|
||||||
|
@ -59,7 +52,7 @@ service.interceptors.request.use(
|
||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
(response: AxiosResponse<Recordable>) => {
|
(response: AxiosResponse<Recordable>) => {
|
||||||
if (response.data.code === result_code) {
|
if (response.data.code === result_code) {
|
||||||
return response.data
|
return response
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(response.data.message)
|
ElMessage.error(response.data.message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { config } from '@/config/axios/config'
|
||||||
|
|
||||||
const { default_headers } = config
|
const { default_headers } = config
|
||||||
|
|
||||||
const request = <T>(option: AxiosConfig): AxiosPromise<T> => {
|
const request = (option: AxiosConfig) => {
|
||||||
const { url, method, params, data, headersType, responseType } = option
|
const { url, method, params, data, headersType, responseType } = option
|
||||||
return service({
|
return service({
|
||||||
url: url,
|
url: url,
|
||||||
|
@ -21,19 +21,19 @@ const request = <T>(option: AxiosConfig): AxiosPromise<T> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
function getFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
||||||
return request<T>({ method: 'get', ...option })
|
return request({ method: 'get', ...option })
|
||||||
}
|
}
|
||||||
|
|
||||||
function postFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
function postFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
||||||
return request<T>({ method: 'post', ...option })
|
return request({ method: 'post', ...option })
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
function deleteFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
||||||
return request<T>({ method: 'delete', ...option })
|
return request({ method: 'delete', ...option })
|
||||||
}
|
}
|
||||||
|
|
||||||
function putFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
function putFn<T = any>(option: AxiosConfig): AxiosPromise<T> {
|
||||||
return request<T>({ method: 'put', ...option })
|
return request({ method: 'put', ...option })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAxios = () => {
|
export const useAxios = () => {
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
type Callback = (error?: string | Error | undefined) => void
|
||||||
|
|
||||||
|
interface LengthRange {
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useValidator = () => {
|
||||||
|
const required = (message: string) => {
|
||||||
|
return {
|
||||||
|
required: true,
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lengthRange = (val: any, callback: Callback, options: LengthRange) => {
|
||||||
|
const { min, max, message } = options
|
||||||
|
if (val.length < min || val.length > max) {
|
||||||
|
callback(new Error(message))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notSpace = (val: any, callback: Callback, message: string) => {
|
||||||
|
// 用户名不能有空格
|
||||||
|
if (val.indexOf(' ') !== -1) {
|
||||||
|
callback(new Error(message))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notSpecialCharacters = (val: any, callback: Callback, message: string) => {
|
||||||
|
// 密码不能是特殊字符
|
||||||
|
if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) {
|
||||||
|
callback(new Error(message))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 两个字符串是否想等
|
||||||
|
const isEqual = (val1: string, val2: string, callback: Callback, message: string) => {
|
||||||
|
if (val1 === val2) {
|
||||||
|
callback()
|
||||||
|
} else {
|
||||||
|
callback(new Error(message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
required,
|
||||||
|
lengthRange,
|
||||||
|
notSpace,
|
||||||
|
notSpecialCharacters,
|
||||||
|
isEqual
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,12 +86,17 @@ export default {
|
||||||
message: 'Backstage management system',
|
message: 'Backstage management system',
|
||||||
username: 'Username',
|
username: 'Username',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
|
register: 'Register',
|
||||||
|
checkPassword: 'Confirm password',
|
||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
otherLogin: 'Sign in with',
|
otherLogin: 'Sign in with',
|
||||||
remember: 'Remember me',
|
remember: 'Remember me',
|
||||||
|
hasUser: 'Existing account? Go to login',
|
||||||
forgetPassword: 'Forget password',
|
forgetPassword: 'Forget password',
|
||||||
usernamePlaceholder: 'username is admin or test',
|
usernamePlaceholder: 'Please input username',
|
||||||
passwordPlaceholder: 'password is admin or test'
|
passwordPlaceholder: 'Please input password',
|
||||||
|
code: 'Verification code',
|
||||||
|
codePlaceholder: 'Please input verification code'
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
login: 'Login',
|
login: 'Login',
|
||||||
|
|
|
@ -86,12 +86,17 @@ export default {
|
||||||
message: '开箱即用的中后台管理系统',
|
message: '开箱即用的中后台管理系统',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
password: '密码',
|
password: '密码',
|
||||||
|
register: '注册',
|
||||||
|
checkPassword: '确认密码',
|
||||||
login: '登录',
|
login: '登录',
|
||||||
otherLogin: '其他登录方式',
|
otherLogin: '其他登录方式',
|
||||||
remember: '记住我',
|
remember: '记住我',
|
||||||
|
hasUser: '已有账号?去登录',
|
||||||
forgetPassword: '忘记密码',
|
forgetPassword: '忘记密码',
|
||||||
usernamePlaceholder: '用户名为 admin 或者 test ',
|
usernamePlaceholder: '请输入用户名',
|
||||||
passwordPlaceholder: '密码为 admin 或者 test '
|
passwordPlaceholder: '请输入密码',
|
||||||
|
code: '验证码',
|
||||||
|
codePlaceholder: '请输入验证码'
|
||||||
},
|
},
|
||||||
router: {
|
router: {
|
||||||
login: '登录',
|
login: '登录',
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { LoginForm } from './components'
|
import { LoginForm, RegisterForm } from './components'
|
||||||
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
import { ThemeSwitch } from '@/components/ThemeSwitch'
|
||||||
import { LocaleDropdown } from '@/components/LocaleDropdown'
|
import { LocaleDropdown } from '@/components/LocaleDropdown'
|
||||||
import { useI18n } from '@/hooks/web/useI18n'
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
import { underlineToHump } from '@/utils'
|
import { underlineToHump } from '@/utils'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
const { getPrefixCls } = useDesign()
|
const { getPrefixCls } = useDesign()
|
||||||
|
|
||||||
|
@ -14,12 +15,22 @@ const prefixCls = getPrefixCls('login')
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const isLogin = ref(true)
|
||||||
|
|
||||||
|
const toRegister = () => {
|
||||||
|
isLogin.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const toLogin = () => {
|
||||||
|
isLogin.value = true
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="prefixCls"
|
:class="prefixCls"
|
||||||
class="h-[100%] relative overflow-hidden <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
|
class="h-[100%] relative <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
|
||||||
>
|
>
|
||||||
<div class="relative h-full flex mx-auto">
|
<div class="relative h-full flex mx-auto">
|
||||||
<div
|
<div
|
||||||
|
@ -59,7 +70,16 @@ const { t } = useI18n()
|
||||||
<div
|
<div
|
||||||
class="h-full flex items-center m-auto w-[100%] @2xl:max-w-500px @xl:max-w-500px @md:max-w-500px @lg:max-w-500px"
|
class="h-full flex items-center m-auto w-[100%] @2xl:max-w-500px @xl:max-w-500px @md:max-w-500px @lg:max-w-500px"
|
||||||
>
|
>
|
||||||
<LoginForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
|
<LoginForm
|
||||||
|
v-if="isLogin"
|
||||||
|
class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)"
|
||||||
|
@to-register="toRegister"
|
||||||
|
/>
|
||||||
|
<RegisterForm
|
||||||
|
v-else
|
||||||
|
class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)"
|
||||||
|
@to-login="toLogin"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,12 +6,16 @@ import { ElButton, ElCheckbox, ElLink } from 'element-plus'
|
||||||
import { required } from '@/utils/formRules'
|
import { required } from '@/utils/formRules'
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
import { loginApi, getTestRoleApi, getAdminRoleApi } from '@/api/login'
|
import { loginApi, getTestRoleApi, getAdminRoleApi } from '@/api/login'
|
||||||
import type { UserLoginType } from '@/api/login/types'
|
|
||||||
import { useCache } from '@/hooks/web/useCache'
|
import { useCache } from '@/hooks/web/useCache'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { usePermissionStore } from '@/store/modules/permission'
|
import { usePermissionStore } from '@/store/modules/permission'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
||||||
|
import { IUserModel } from '@/api-types/user'
|
||||||
|
import md5 from 'js-md5'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
|
const emit = defineEmits(['to-register'])
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
@ -19,10 +23,12 @@ const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
const { currentRoute, addRoute, push } = useRouter()
|
const { currentRoute, addRoute, push } = useRouter()
|
||||||
|
|
||||||
|
const { wsCache } = useCache()
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
username: [required],
|
user_name: [required],
|
||||||
password: [required]
|
password: [required]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +40,7 @@ const schema = reactive<FormSchema[]>([
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'username',
|
field: 'user_name',
|
||||||
label: t('login.username'),
|
label: t('login.username'),
|
||||||
value: 'admin',
|
value: 'admin',
|
||||||
component: 'Input',
|
component: 'Input',
|
||||||
|
@ -117,18 +123,22 @@ const signIn = async () => {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const { getFormData } = methods
|
const { getFormData } = methods
|
||||||
const formData = await getFormData<UserLoginType>()
|
const formData = await getFormData<IUserModel>()
|
||||||
|
|
||||||
const res = await loginApi(formData)
|
try {
|
||||||
.catch(() => {})
|
const { result } = await loginApi(
|
||||||
.finally(() => (loading.value = false))
|
Object.assign(cloneDeep(formData), {
|
||||||
|
password: md5(formData.password)
|
||||||
if (res) {
|
})
|
||||||
const { wsCache } = useCache()
|
)
|
||||||
wsCache.set(appStore.getUserInfo, res.data)
|
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
wsCache.set(appStore.getUserInfo, result)
|
||||||
getRole()
|
getRole()
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -136,14 +146,14 @@ const signIn = async () => {
|
||||||
// 获取角色信息
|
// 获取角色信息
|
||||||
const getRole = async () => {
|
const getRole = async () => {
|
||||||
const { getFormData } = methods
|
const { getFormData } = methods
|
||||||
const formData = await getFormData<UserLoginType>()
|
const formData = await getFormData<IUserModel>()
|
||||||
const params = {
|
const params = {
|
||||||
roleName: formData.username
|
roleName: formData.user_name
|
||||||
}
|
}
|
||||||
// admin - 模拟后端过滤菜单
|
// admin - 模拟后端过滤菜单
|
||||||
// test - 模拟前端过滤菜单
|
// test - 模拟前端过滤菜单
|
||||||
const res =
|
const res =
|
||||||
formData.username === 'admin'
|
formData.user_name === 'admin'
|
||||||
? await getAdminRoleApi({ params })
|
? await getAdminRoleApi({ params })
|
||||||
: await getTestRoleApi({ params })
|
: await getTestRoleApi({ params })
|
||||||
if (res) {
|
if (res) {
|
||||||
|
@ -151,7 +161,7 @@ const getRole = async () => {
|
||||||
const routers = res.data.list || []
|
const routers = res.data.list || []
|
||||||
wsCache.set('roleRouters', routers)
|
wsCache.set('roleRouters', routers)
|
||||||
|
|
||||||
formData.username === 'admin'
|
formData.user_name === 'admin'
|
||||||
? await permissionStore.generateRoutes('admin', routers).catch(() => {})
|
? await permissionStore.generateRoutes('admin', routers).catch(() => {})
|
||||||
: await permissionStore.generateRoutes('test', routers).catch(() => {})
|
: await permissionStore.generateRoutes('test', routers).catch(() => {})
|
||||||
|
|
||||||
|
@ -162,6 +172,11 @@ const getRole = async () => {
|
||||||
push({ path: redirect.value || permissionStore.addRouters[0].path })
|
push({ path: redirect.value || permissionStore.addRouters[0].path })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 去注册页面
|
||||||
|
const toRegister = () => {
|
||||||
|
emit('to-register')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -186,9 +201,16 @@ const getRole = async () => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #login>
|
<template #login>
|
||||||
|
<div class="w-[100%]">
|
||||||
<ElButton :loading="loading" type="primary" class="w-[100%]" @click="signIn">
|
<ElButton :loading="loading" type="primary" class="w-[100%]" @click="signIn">
|
||||||
{{ t('login.login') }}
|
{{ t('login.login') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<div class="w-[100%] mt-15px">
|
||||||
|
<ElButton class="w-[100%]" @click="toRegister">
|
||||||
|
{{ t('login.register') }}
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #otherIcon>
|
<template #otherIcon>
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Form } from '@/components/Form'
|
||||||
|
import { reactive, ref, unref } from 'vue'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
|
import { ElButton, ElInput, FormRules, ElMessage } from 'element-plus'
|
||||||
|
import { getCodeApi, registerApi } from '@/api/register'
|
||||||
|
import { useValidator } from '@/hooks/web/useValidator'
|
||||||
|
import { IUserModel } from '@/api-types/user'
|
||||||
|
import md5 from 'js-md5'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
|
||||||
|
const emit = defineEmits(['to-login'])
|
||||||
|
|
||||||
|
const { register, methods, elFormRef } = useForm()
|
||||||
|
|
||||||
|
const { getFormData } = methods
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const { required, lengthRange, notSpace, notSpecialCharacters, isEqual } = useValidator()
|
||||||
|
|
||||||
|
const schema = reactive<FormSchema[]>([
|
||||||
|
{
|
||||||
|
field: 'title',
|
||||||
|
colProps: {
|
||||||
|
span: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'user_name',
|
||||||
|
label: t('login.username'),
|
||||||
|
value: '',
|
||||||
|
component: 'Input',
|
||||||
|
colProps: {
|
||||||
|
span: 24
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
placeholder: t('login.usernamePlaceholder')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'password',
|
||||||
|
label: t('login.password'),
|
||||||
|
value: '',
|
||||||
|
component: 'InputPassword',
|
||||||
|
colProps: {
|
||||||
|
span: 24
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
style: {
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
strength: true,
|
||||||
|
placeholder: t('login.passwordPlaceholder')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'check_password',
|
||||||
|
label: t('login.checkPassword'),
|
||||||
|
value: '',
|
||||||
|
component: 'InputPassword',
|
||||||
|
colProps: {
|
||||||
|
span: 24
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
style: {
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
strength: true,
|
||||||
|
placeholder: t('login.passwordPlaceholder')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'code',
|
||||||
|
label: t('login.code'),
|
||||||
|
colProps: {
|
||||||
|
span: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'register',
|
||||||
|
colProps: {
|
||||||
|
span: 24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const rules: FormRules = {
|
||||||
|
user_name: [
|
||||||
|
required('用户名不能为空'),
|
||||||
|
{
|
||||||
|
validator: (_, value, callback) =>
|
||||||
|
lengthRange(value, callback, { min: 2, max: 10, message: '用户名长度必须在2-10之间' })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (_, value, callback) => notSpace(value, callback, '用户名不能有空格')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
required('密码不能为空'),
|
||||||
|
{
|
||||||
|
validator: (_, value, callback) =>
|
||||||
|
lengthRange(value, callback, { min: 5, max: 20, message: '密码长度必须在5-20之间' })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (_, value, callback) => notSpecialCharacters(value, callback, '密码不能是特殊字符')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
check_password: [
|
||||||
|
required('确认密码不能为空'),
|
||||||
|
{
|
||||||
|
validator: (_, value, callback) =>
|
||||||
|
lengthRange(value, callback, { min: 5, max: 20, message: '确认密码长度必须在5-20之间' })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (_, value, callback) =>
|
||||||
|
notSpecialCharacters(value, callback, '确认密码不能是特殊字符')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: async (_, value, callback) => {
|
||||||
|
const formData = await getFormData<Omit<IUserModel, 'is_admin'>>()
|
||||||
|
return isEqual(value, formData.password, callback, '两次密码不一致')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
code: [required('验证码不能为空')]
|
||||||
|
}
|
||||||
|
|
||||||
|
const toLogin = () => {
|
||||||
|
emit('to-login')
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeUrl = ref('')
|
||||||
|
const codeUuid = ref('')
|
||||||
|
const getCode = async () => {
|
||||||
|
const { result } = await getCodeApi()
|
||||||
|
if (result) {
|
||||||
|
const { url, uuid } = result
|
||||||
|
codeUrl.value = url
|
||||||
|
codeUuid.value = uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getCode()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const loginRegister = async () => {
|
||||||
|
const formRef = unref(elFormRef)
|
||||||
|
formRef?.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const formData = await getFormData<Omit<IUserModel, 'is_admin'>>()
|
||||||
|
const { result } = await registerApi(
|
||||||
|
Object.assign(cloneDeep(formData), {
|
||||||
|
uuid: codeUuid.value,
|
||||||
|
password: md5(formData.password),
|
||||||
|
check_password: md5(formData.check_password)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (result) {
|
||||||
|
ElMessage.success('注册成功')
|
||||||
|
toLogin()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
:schema="schema"
|
||||||
|
:rules="rules"
|
||||||
|
label-position="top"
|
||||||
|
hide-required-asterisk
|
||||||
|
size="large"
|
||||||
|
class="dark:(border-1 border-[var(--el-border-color)] border-solid)"
|
||||||
|
@register="register"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<h2 class="text-2xl font-bold text-center w-[100%]">{{ t('login.register') }}</h2>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #code="form">
|
||||||
|
<div class="w-[100%] flex">
|
||||||
|
<ElInput
|
||||||
|
v-model="form['code']"
|
||||||
|
:placeholder="t('login.codePlaceholder')"
|
||||||
|
class="!w-[calc(100%-150px)]"
|
||||||
|
/>
|
||||||
|
<div v-html="codeUrl" class="h-38px cursor-pointer w-150px" @click="getCode"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #register>
|
||||||
|
<div class="w-[100%]">
|
||||||
|
<ElButton type="primary" class="w-[100%]" :loading="loading" @click="loginRegister">
|
||||||
|
{{ t('login.register') }}
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
<div class="w-[100%] mt-15px">
|
||||||
|
<ElButton class="w-[100%]" @click="toLogin">
|
||||||
|
{{ t('login.hasUser') }}
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
|
</template>
|
|
@ -1,3 +1,4 @@
|
||||||
import LoginForm from './LoginForm.vue'
|
import LoginForm from './LoginForm.vue'
|
||||||
|
import RegisterForm from './RegisterForm.vue'
|
||||||
|
|
||||||
export { LoginForm }
|
export { LoginForm, RegisterForm }
|
||||||
|
|
|
@ -33,3 +33,8 @@ declare type AxiosConfig = {
|
||||||
headersType?: string
|
headersType?: string
|
||||||
responseType?: AxiosResponseType
|
responseType?: AxiosResponseType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare interface IResponse<T = any> {
|
||||||
|
code: string
|
||||||
|
result: T extends any ? T : T & any
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ function pathResolve(dir: string) {
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
let env = null
|
let env = {} as any
|
||||||
const isBuild = command === 'build'
|
const isBuild = command === 'build'
|
||||||
if (!isBuild) {
|
if (!isBuild) {
|
||||||
env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
|
env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
|
||||||
|
@ -119,11 +119,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
port: 4000,
|
port: 4000,
|
||||||
proxy: {
|
proxy: {
|
||||||
// 选项写法
|
// 选项写法
|
||||||
// '/api': {
|
'/api': {
|
||||||
// target: 'http://localhost:3000',
|
target: 'http://127.0.0.1:8000',
|
||||||
// changeOrigin: true,
|
changeOrigin: true,
|
||||||
// rewrite: path => path.replace(/^\/api/, '')
|
rewrite: path => path.replace(/^\/api/, '')
|
||||||
// }
|
}
|
||||||
},
|
},
|
||||||
hmr: {
|
hmr: {
|
||||||
overlay: false
|
overlay: false
|
||||||
|
@ -146,7 +146,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
|
||||||
'intro.js',
|
'intro.js',
|
||||||
'qrcode',
|
'qrcode',
|
||||||
'@wangeditor/editor',
|
'@wangeditor/editor',
|
||||||
'@wangeditor/editor-for-vue'
|
'@wangeditor/editor-for-vue',
|
||||||
|
'js-md5'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue