Merge pull request #78 from kailong321200875/node-admin

This commit is contained in:
Archer 2022-06-25 20:08:57 +08:00 committed by GitHub
commit 43518532f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 441 additions and 7392 deletions

View File

@ -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'
} }
}) })

1
.gitignore vendored
View File

@ -4,4 +4,5 @@ dist
dist-ssr dist-ssr
*.local *.local
/dist* /dist*
*-lock.*
pnpm-debug pnpm-debug

View File

@ -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",

View File

@ -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)

View File

@ -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",

File diff suppressed because it is too large Load Diff

9
src/api-types/user.ts Normal file
View File

@ -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
}

View File

@ -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 = () => {

21
src/api/register/index.ts Normal file
View File

@ -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
}

View File

@ -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>

View File

@ -14,7 +14,7 @@ const config: {
*/ */
base_url: { base_url: {
// 开发环境接口前缀 // 开发环境接口前缀
base: '', base: '/api',
// 打包开发环境接口前缀 // 打包开发环境接口前缀
dev: '', dev: '',

View File

@ -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)
} }

View File

@ -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 = () => {

View File

@ -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
}
}

View File

@ -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',

View File

@ -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: '登录',

View File

@ -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>

View File

@ -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,17 +123,21 @@ 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) { if (result) {
const { wsCache } = useCache() wsCache.set(appStore.getUserInfo, result)
wsCache.set(appStore.getUserInfo, res.data) 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>
<ElButton :loading="loading" type="primary" class="w-[100%]" @click="signIn"> <div class="w-[100%]">
{{ t('login.login') }} <ElButton :loading="loading" type="primary" class="w-[100%]" @click="signIn">
</ElButton> {{ t('login.login') }}
</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>

View File

@ -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>

View File

@ -1,3 +1,4 @@
import LoginForm from './LoginForm.vue' import LoginForm from './LoginForm.vue'
import RegisterForm from './RegisterForm.vue'
export { LoginForm } export { LoginForm, RegisterForm }

5
types/global.d.ts vendored
View File

@ -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
}

View File

@ -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'
] ]
} }
} }