page: 📄 用户可以用邮箱或者用户名登录,修改部分缺陷
This commit is contained in:
parent
35957e7b7d
commit
f2c809a142
|
@ -57,7 +57,7 @@ onMounted(() => {
|
||||||
<el-image :src="userinfo.avatar" style="width: 100px; height: 100px" />
|
<el-image :src="userinfo.avatar" style="width: 100px; height: 100px" />
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('username')" :width="100">{{ userinfo.username }}</el-descriptions-item>
|
<el-descriptions-item :label="$t('username')" :width="100">{{ userinfo.username }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('nickName')" :width="100">{{ userinfo.nickName }}</el-descriptions-item>
|
<el-descriptions-item :label="$t('nickname')" :width="100">{{ userinfo.nickname }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item :label="$t('email')"> {{ userinfo.email }}</el-descriptions-item>
|
<el-descriptions-item :label="$t('email')"> {{ userinfo.email }}</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('phone')">{{ userinfo.phone }}</el-descriptions-item>
|
<el-descriptions-item :label="$t('phone')">{{ userinfo.phone }}</el-descriptions-item>
|
||||||
|
@ -100,7 +100,7 @@ onMounted(() => {
|
||||||
<el-descriptions-item :label="$t('username')" :width="100">
|
<el-descriptions-item :label="$t('username')" :width="100">
|
||||||
<el-skeleton :rows="1" animated />
|
<el-skeleton :rows="1" animated />
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item :label="$t('nickName')" :width="100">
|
<el-descriptions-item :label="$t('nickname')" :width="100">
|
||||||
<el-skeleton :rows="1" animated />
|
<el-skeleton :rows="1" animated />
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ export const useAdminUserStore = defineStore('adminUserStore', {
|
||||||
// 用户名
|
// 用户名
|
||||||
username: undefined,
|
username: undefined,
|
||||||
// 昵称
|
// 昵称
|
||||||
nickName: undefined,
|
nickname: undefined,
|
||||||
// 邮箱
|
// 邮箱
|
||||||
email: undefined,
|
email: undefined,
|
||||||
// 手机号
|
// 手机号
|
||||||
|
|
|
@ -49,9 +49,8 @@ export const useUserStore = defineStore({
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 前端登出(不调用接口) */
|
/** 前端登出 */
|
||||||
async logOut() {
|
async logOut() {
|
||||||
// 登出
|
|
||||||
const result = await fetchLogout();
|
const result = await fetchLogout();
|
||||||
if (result.code == 200) {
|
if (result.code == 200) {
|
||||||
this.username = '';
|
this.username = '';
|
||||||
|
@ -83,7 +82,9 @@ export const useUserStore = defineStore({
|
||||||
async getUserinfo() {
|
async getUserinfo() {
|
||||||
const result = await fetchGetUserinfo();
|
const result = await fetchGetUserinfo();
|
||||||
if (result.code === 200) {
|
if (result.code === 200) {
|
||||||
return result.data;
|
const data = result.data;
|
||||||
|
setToken(data);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
|
|
@ -47,7 +47,11 @@ const onSubmit = async (formEl: FormInstance) => {
|
||||||
const avatar = uploadAvatarSrc.value;
|
const avatar = uploadAvatarSrc.value;
|
||||||
if (avatar) userInfos.avatar = avatar;
|
if (avatar) userInfos.avatar = avatar;
|
||||||
|
|
||||||
await adminUserStore.updateAdminUserByLocalUser(userInfos);
|
// 更新用户信息
|
||||||
|
const result = await adminUserStore.updateAdminUserByLocalUser(userInfos);
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
// 重新加载用户信息
|
||||||
await onSearchByUserinfo();
|
await onSearchByUserinfo();
|
||||||
} else {
|
} else {
|
||||||
message($t('required_fields'), { type: 'warning' });
|
message($t('required_fields'), { type: 'warning' });
|
||||||
|
@ -81,12 +85,12 @@ onMounted(() => {
|
||||||
|
|
||||||
<!-- 用户名 -->
|
<!-- 用户名 -->
|
||||||
<el-form-item :label="$t('adminUser_username')" prop="username">
|
<el-form-item :label="$t('adminUser_username')" prop="username">
|
||||||
<el-input v-model="userInfos.username" :placeholder="$t('adminUser_username')" autocomplete="off" type="text" />
|
<el-input v-model="userInfos.username" :placeholder="$t('adminUser_username')" autocomplete="off" disabled type="text" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 昵称 -->
|
<!-- 昵称 -->
|
||||||
<el-form-item :label="$t('adminUser_nickName')" prop="nickName">
|
<el-form-item :label="$t('adminUser_nickname')" prop="nickname">
|
||||||
<el-input v-model="userInfos.nickName" :placeholder="$t('adminUser_nickName')" autocomplete="off" type="text" />
|
<el-input v-model="userInfos.nickname" :placeholder="$t('adminUser_nickname')" autocomplete="off" type="text" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 邮箱 -->
|
<!-- 邮箱 -->
|
||||||
|
|
|
@ -13,8 +13,8 @@ export const columns: TableColumnList = [
|
||||||
{ type: 'index', index: (index: number) => index + 1, label: '序号', width: 60 },
|
{ type: 'index', index: (index: number) => index + 1, label: '序号', width: 60 },
|
||||||
// 用户名
|
// 用户名
|
||||||
{ label: $t('userLoginLog_username'), prop: 'username', width: 180 },
|
{ label: $t('userLoginLog_username'), prop: 'username', width: 180 },
|
||||||
// // 登录Ip
|
// 登录Ip
|
||||||
// { label: $t('userLoginLog_ipAddress'), prop: 'ipAddress', width: 140 },
|
{ label: $t('userLoginLog_ipAddress'), prop: 'ipAddress', width: 140 },
|
||||||
// 登录Ip归属地
|
// 登录Ip归属地
|
||||||
{ label: $t('userLoginLog_ipRegion'), prop: 'ipRegion' },
|
{ label: $t('userLoginLog_ipRegion'), prop: 'ipRegion' },
|
||||||
// // 登录时代理
|
// // 登录时代理
|
||||||
|
@ -35,8 +35,7 @@ export const columns: TableColumnList = [
|
||||||
|
|
||||||
// 修改用户信息规则校验
|
// 修改用户信息规则校验
|
||||||
export const rules = reactive<FormRules<any>>({
|
export const rules = reactive<FormRules<any>>({
|
||||||
username: [{ required: true, message: '昵称必填', trigger: 'blur' }],
|
nickname: [{ required: true, message: '昵称必填', trigger: 'blur' }],
|
||||||
nickName: [{ required: true, message: '昵称必填', trigger: 'blur' }],
|
|
||||||
email: [{ required: true, message: '昵称必填', trigger: 'blur' }],
|
email: [{ required: true, message: '昵称必填', trigger: 'blur' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ export const uploadAvatarSrc = ref();
|
||||||
|
|
||||||
// 剪裁头像是否显示
|
// 剪裁头像是否显示
|
||||||
export const isShow = ref(false);
|
export const isShow = ref(false);
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
// 用户信息内容
|
// 用户信息内容
|
||||||
|
@ -23,7 +22,7 @@ export const userInfos = reactive({
|
||||||
email: '',
|
email: '',
|
||||||
phone: '',
|
phone: '',
|
||||||
summary: '',
|
summary: '',
|
||||||
nickName: '',
|
nickname: '',
|
||||||
password: '',
|
password: '',
|
||||||
sex: '',
|
sex: '',
|
||||||
});
|
});
|
||||||
|
@ -31,7 +30,15 @@ export const userInfos = reactive({
|
||||||
/** 获取用户信息内容 */
|
/** 获取用户信息内容 */
|
||||||
export const onSearchByUserinfo = async () => {
|
export const onSearchByUserinfo = async () => {
|
||||||
const data = await userStore.getUserinfo();
|
const data = await userStore.getUserinfo();
|
||||||
Object.assign(userInfos, data);
|
userInfos.summary = data.personDescription;
|
||||||
|
userInfos.avatar = data.avatar;
|
||||||
|
userInfos.username = data.username;
|
||||||
|
userInfos.nickname = data.nickname;
|
||||||
|
userInfos.email = data.email;
|
||||||
|
userInfos.phone = data.phone;
|
||||||
|
userInfos.nickname = data.nickname;
|
||||||
|
userInfos.password = data.password;
|
||||||
|
userInfos.sex = data.sex;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 修改头像 */
|
/** 修改头像 */
|
||||||
|
|
|
@ -2,22 +2,20 @@
|
||||||
import Motion from './utils/motion';
|
import Motion from './utils/motion';
|
||||||
import { useNav } from '@/layout/hooks/useNav';
|
import { useNav } from '@/layout/hooks/useNav';
|
||||||
import { useLayout } from '@/layout/hooks/useLayout';
|
import { useLayout } from '@/layout/hooks/useLayout';
|
||||||
import { avatar, bg, illustration } from './utils/static';
|
import bg from '@/assets/login/bg.png';
|
||||||
|
import avatar from '@/assets/login/avatar.svg?component';
|
||||||
|
import illustration from '@/assets/login/illustration.svg?component';
|
||||||
import { onMounted, toRaw } from 'vue';
|
import { onMounted, toRaw } from 'vue';
|
||||||
import { useTranslationLang } from '@/layout/hooks/useTranslationLang';
|
import { useTranslationLang } from '@/layout/hooks/useTranslationLang';
|
||||||
import { useDataThemeChange } from '@/layout/hooks/useDataThemeChange';
|
import { useDataThemeChange } from '@/layout/hooks/useDataThemeChange';
|
||||||
|
|
||||||
import dayIcon from '@/assets/svg/day.svg?component';
|
import dayIcon from '@/assets/svg/day.svg?component';
|
||||||
import darkIcon from '@/assets/svg/dark.svg?component';
|
import darkIcon from '@/assets/svg/dark.svg?component';
|
||||||
import globalization from '@/assets/svg/globalization.svg?component';
|
import globalization from '@/assets/svg/globalization.svg?component';
|
||||||
import Check from '@iconify-icons/ep/check';
|
import Check from '@iconify-icons/ep/check';
|
||||||
|
|
||||||
import LoginForm from '@/views/login/login-form.vue';
|
import LoginForm from '@/views/login/login-form.vue';
|
||||||
|
import LoginEmail from '@/views/login/login-email.vue';
|
||||||
import { userI18nTypeStore } from '@/store/i18n/i18nType';
|
import { userI18nTypeStore } from '@/store/i18n/i18nType';
|
||||||
|
import { currentPage } from '@/views/login/utils/hooks';
|
||||||
defineOptions({
|
|
||||||
name: 'Login',
|
|
||||||
});
|
|
||||||
|
|
||||||
const { initStorage } = useLayout();
|
const { initStorage } = useLayout();
|
||||||
initStorage();
|
initStorage();
|
||||||
|
@ -73,7 +71,8 @@ onMounted(() => {
|
||||||
</Motion>
|
</Motion>
|
||||||
|
|
||||||
<!-- 登录表单 -->
|
<!-- 登录表单 -->
|
||||||
<login-form />
|
<login-form v-if="currentPage === 0" />
|
||||||
|
<login-email v-if="currentPage === 1" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { emailRules } from '@/views/login/utils/rule';
|
||||||
|
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||||
|
import User from '@iconify-icons/ri/user-3-fill';
|
||||||
|
import Lock from '@iconify-icons/ri/lock-fill';
|
||||||
|
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useUserStore } from '@/store/system/user';
|
||||||
|
import { message } from '@/utils/message';
|
||||||
|
import { getTopMenu, initRouter } from '@/router/utils';
|
||||||
|
import Motion from './utils/motion';
|
||||||
|
import { ElMessage, FormInstance } from 'element-plus';
|
||||||
|
import { onBack } from '@/views/login/utils/hooks';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const ruleFormRef = ref<FormInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
const sendSecond = ref(60);
|
||||||
|
const timer = ref(null);
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const ruleForm = reactive({
|
||||||
|
username: '1319900154@qq.com',
|
||||||
|
password: 'admin123',
|
||||||
|
emailCode: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 发送邮箱验证码
|
||||||
|
*/
|
||||||
|
const onSendEmailCode = async () => {
|
||||||
|
// 判断是否填写邮箱,如果没有填写邮箱不给发送验证码
|
||||||
|
if (ruleForm.username === '' || ruleForm.username === void 0) {
|
||||||
|
message('请先填写邮箱地址', { type: 'warning' });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const result = await userStore.postEmailCode(ruleForm.username);
|
||||||
|
if (result) {
|
||||||
|
// 开始倒计时,之后发送邮箱验证码
|
||||||
|
onSendEmailTimer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 发送邮箱倒计时点击
|
||||||
|
*/
|
||||||
|
const onSendEmailTimer = () => {
|
||||||
|
// 开始倒计时
|
||||||
|
timer.value = setInterval(() => {
|
||||||
|
// 当定时小于0时清除定时器
|
||||||
|
if (sendSecond.value <= 0) {
|
||||||
|
clearInterval(timer.value);
|
||||||
|
timer.value = null;
|
||||||
|
sendSecond.value = 60;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 之后每秒减去时间
|
||||||
|
sendSecond.value--;
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
* @param formEl
|
||||||
|
*/
|
||||||
|
const onLogin = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
// 开始登录
|
||||||
|
await formEl.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
const result = await userStore.loginByUsername(ruleForm);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
// 获取后端路由
|
||||||
|
await initRouter();
|
||||||
|
router.push(getTopMenu(true).path).then(() => {
|
||||||
|
ElMessage.closeAll();
|
||||||
|
message(t('login.loginSuccess'), { type: 'success' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 使用公共函数,避免`removeEventListener`失效 */
|
||||||
|
function onkeypress({ code }: KeyboardEvent) {
|
||||||
|
if (['Enter', 'NumpadEnter'].includes(code)) {
|
||||||
|
onLogin(ruleFormRef.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.document.addEventListener('keypress', onkeypress);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.document.removeEventListener('keypress', onkeypress);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-form ref="ruleFormRef" :model="ruleForm" :rules="emailRules" size="large">
|
||||||
|
<Motion :delay="100">
|
||||||
|
<el-form-item prop="username">
|
||||||
|
<el-input v-model="ruleForm.username" :placeholder="t('login.username')" :prefix-icon="useRenderIcon(User)" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
|
<Motion :delay="150">
|
||||||
|
<el-form-item prop="password">
|
||||||
|
<el-input v-model="ruleForm.password" :placeholder="t('login.password')" :prefix-icon="useRenderIcon(Lock)" clearable show-password />
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
|
<Motion :delay="150">
|
||||||
|
<el-form-item prop="emailCode">
|
||||||
|
<el-input v-model="ruleForm.emailCode" :placeholder="t('login.emailCode')" :prefix-icon="useRenderIcon('ic:outline-email')" clearable @keydown.enter="onLogin(ruleFormRef)">
|
||||||
|
<template v-slot:append>
|
||||||
|
<el-link v-if="sendSecond === 60" :underline="false" class="px-2" type="primary" @click="onSendEmailCode">
|
||||||
|
{{ t('login.getEmailCode') }}
|
||||||
|
</el-link>
|
||||||
|
<el-link v-else :underline="false" class="px-2" type="primary">
|
||||||
|
{{ sendSecond }}
|
||||||
|
{{ t('login.getCodeInfo') }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<el-checkbox v-model="userStore.isRemembered">
|
||||||
|
<el-text size="small" type="primary">{{ userStore.readMeDay }}天免登录(邮箱验证码随便输入,后端校验验证码已注释) </el-text>
|
||||||
|
</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
|
<Motion :delay="250">
|
||||||
|
<el-button :loading="loading" class="w-full" size="default" type="primary" @click="onLogin(ruleFormRef)">
|
||||||
|
{{ t('login.login') }}
|
||||||
|
</el-button>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
|
<Motion :delay="200">
|
||||||
|
<el-form-item>
|
||||||
|
<el-button class="w-full mt-4" size="default" @click="onBack">
|
||||||
|
{{ t('login.pureBack') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { loginRules } from '@/views/login/utils/rule';
|
|
||||||
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
import { useRenderIcon } from '@/components/CommonIcon/src/hooks';
|
||||||
import User from '@iconify-icons/ri/user-3-fill';
|
import User from '@iconify-icons/ri/user-3-fill';
|
||||||
import Lock from '@iconify-icons/ri/lock-fill';
|
import Lock from '@iconify-icons/ri/lock-fill';
|
||||||
|
@ -11,56 +10,20 @@ import { message } from '@/utils/message';
|
||||||
import { getTopMenu, initRouter } from '@/router/utils';
|
import { getTopMenu, initRouter } from '@/router/utils';
|
||||||
import Motion from './utils/motion';
|
import Motion from './utils/motion';
|
||||||
import { ElMessage, FormInstance } from 'element-plus';
|
import { ElMessage, FormInstance } from 'element-plus';
|
||||||
|
import { currentPage } from '@/views/login/utils/hooks';
|
||||||
|
import { formRules } from '@/views/login/utils/rule';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const ruleFormRef = ref<FormInstance>();
|
const ruleFormRef = ref<FormInstance>();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const sendSecond = ref(60);
|
|
||||||
const timer = ref(null);
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const ruleForm = reactive({
|
const ruleForm = reactive({
|
||||||
username: '1319900154@qq.com',
|
username: 'bunny',
|
||||||
password: 'admin123',
|
password: 'admin123',
|
||||||
emailCode: '1',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* * 发送邮箱验证码
|
|
||||||
*/
|
|
||||||
const onSendEmailCode = async () => {
|
|
||||||
// 判断是否填写邮箱,如果没有填写邮箱不给发送验证码
|
|
||||||
if (ruleForm.username === '' || ruleForm.username === void 0) {
|
|
||||||
message('请先填写邮箱地址', { type: 'warning' });
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const result = await userStore.postEmailCode(ruleForm.username);
|
|
||||||
if (result) {
|
|
||||||
// 开始倒计时,之后发送邮箱验证码
|
|
||||||
onSendEmailTimer();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * 发送邮箱倒计时点击
|
|
||||||
*/
|
|
||||||
const onSendEmailTimer = () => {
|
|
||||||
// 开始倒计时
|
|
||||||
timer.value = setInterval(() => {
|
|
||||||
// 当定时小于0时清除定时器
|
|
||||||
if (sendSecond.value <= 0) {
|
|
||||||
clearInterval(timer.value);
|
|
||||||
timer.value = null;
|
|
||||||
sendSecond.value = 60;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 之后每秒减去时间
|
|
||||||
sendSecond.value--;
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 登录
|
||||||
* @param formEl
|
* @param formEl
|
||||||
|
@ -103,8 +66,8 @@ onBeforeUnmount(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="ruleFormRef" :model="ruleForm" :rules="loginRules" size="large">
|
<el-form ref="ruleFormRef" :model="ruleForm" :rules="formRules" size="large">
|
||||||
<Motion :delay="100">
|
<Motion>
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<el-input v-model="ruleForm.username" :placeholder="t('login.username')" :prefix-icon="useRenderIcon(User)" clearable />
|
<el-input v-model="ruleForm.username" :placeholder="t('login.username')" :prefix-icon="useRenderIcon(User)" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -113,32 +76,25 @@ onBeforeUnmount(() => {
|
||||||
<Motion :delay="150">
|
<Motion :delay="150">
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<el-input v-model="ruleForm.password" :placeholder="t('login.password')" :prefix-icon="useRenderIcon(Lock)" clearable show-password />
|
<el-input v-model="ruleForm.password" :placeholder="t('login.password')" :prefix-icon="useRenderIcon(Lock)" clearable show-password />
|
||||||
</el-form-item>
|
|
||||||
</Motion>
|
|
||||||
|
|
||||||
<Motion :delay="150">
|
|
||||||
<el-form-item prop="emailCode">
|
|
||||||
<el-input v-model="ruleForm.emailCode" :placeholder="t('login.emailCode')" :prefix-icon="useRenderIcon('ic:outline-email')" clearable @keydown.enter="onLogin(ruleFormRef)">
|
|
||||||
<template v-slot:append>
|
|
||||||
<el-link v-if="sendSecond === 60" :underline="false" class="px-2" type="primary" @click="onSendEmailCode">
|
|
||||||
{{ t('login.getEmailCode') }}
|
|
||||||
</el-link>
|
|
||||||
<el-link v-else :underline="false" class="px-2" type="primary">
|
|
||||||
{{ sendSecond }}
|
|
||||||
{{ t('login.getCodeInfo') }}
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<el-checkbox v-model="userStore.isRemembered">
|
<el-checkbox v-model="userStore.isRemembered">
|
||||||
<el-text size="small" type="primary">{{ userStore.readMeDay }}天免登录(邮箱验证码随便输入,后端校验验证码已注释) </el-text>
|
<el-text size="small" type="primary">{{ userStore.readMeDay }}天免登录(邮箱验证码随便输入,后端校验验证码已注释) </el-text>
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</Motion>
|
</Motion>
|
||||||
|
|
||||||
<Motion :delay="250">
|
<Motion :delay="150">
|
||||||
<el-button :loading="loading" class="w-full mt-4" size="default" type="primary" @click="onLogin(ruleFormRef)">
|
<el-form-item>
|
||||||
{{ t('login.login') }}
|
<el-button :loading="loading" class="w-full" size="default" type="primary" @click="onLogin(ruleFormRef)">
|
||||||
</el-button>
|
{{ t('login.login') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
|
<!-- 邮箱登录 -->
|
||||||
|
<Motion :delay="300">
|
||||||
|
<el-form-item>
|
||||||
|
<el-button class="w-full" size="default" @click="currentPage = 1"> 邮箱登录</el-button>
|
||||||
|
</el-form-item>
|
||||||
</Motion>
|
</Motion>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
// 0普通登录,1邮箱登录,其中0为普通登录
|
||||||
|
export const currentPage = ref(0);
|
||||||
|
|
||||||
|
/** 返回到默认登录页面 */
|
||||||
|
export const onBack = () => {
|
||||||
|
currentPage.value = 0;
|
||||||
|
};
|
|
@ -5,8 +5,8 @@ import { $t } from '@/plugins/i18n';
|
||||||
/** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */
|
/** 密码正则(密码格式应为8-18位数字、字母、符号的任意两种组合) */
|
||||||
export const REGEXP_PWD = /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
|
export const REGEXP_PWD = /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
|
||||||
|
|
||||||
/** 登录校验 */
|
/** 邮箱登录校验 */
|
||||||
const loginRules = reactive(<FormRules>{
|
export const emailRules = reactive(<FormRules>{
|
||||||
username: [{ required: true, message: $t('login.usernameRegex'), trigger: 'blur' }],
|
username: [{ required: true, message: $t('login.usernameRegex'), trigger: 'blur' }],
|
||||||
password: [
|
password: [
|
||||||
{
|
{
|
||||||
|
@ -25,4 +25,21 @@ const loginRules = reactive(<FormRules>{
|
||||||
emailCode: [{ required: true, trigger: 'blur', type: 'string' }],
|
emailCode: [{ required: true, trigger: 'blur', type: 'string' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
export { loginRules };
|
/** 登录校验 */
|
||||||
|
export const formRules = reactive(<FormRules>{
|
||||||
|
username: [{ required: true, message: $t('login.usernameRegex'), trigger: 'blur' }],
|
||||||
|
password: [
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === '') {
|
||||||
|
callback(new Error($t('login.purePassWordReg')));
|
||||||
|
} else if (!REGEXP_PWD.test(value)) {
|
||||||
|
callback(new Error($t('login.purePassWordRuleReg')));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import bg from '@/assets/login/bg.png';
|
|
||||||
import avatar from '@/assets/login/avatar.svg?component';
|
|
||||||
import illustration from '@/assets/login/illustration.svg?component';
|
|
||||||
|
|
||||||
export { bg, avatar, illustration };
|
|
|
@ -15,7 +15,7 @@ const props = withDefaults(defineProps<FormProps>(), {
|
||||||
// 用户名
|
// 用户名
|
||||||
username: undefined,
|
username: undefined,
|
||||||
// 昵称
|
// 昵称
|
||||||
nickName: undefined,
|
nickname: undefined,
|
||||||
// 邮箱
|
// 邮箱
|
||||||
email: undefined,
|
email: undefined,
|
||||||
// 手机号
|
// 手机号
|
||||||
|
@ -55,8 +55,8 @@ defineExpose({ formRef });
|
||||||
|
|
||||||
<!-- 昵称 -->
|
<!-- 昵称 -->
|
||||||
<re-col :sm="24" :value="12" :xs="24">
|
<re-col :sm="24" :value="12" :xs="24">
|
||||||
<el-form-item :label="$t('adminUser_nickName')" prop="nickName">
|
<el-form-item :label="$t('adminUser_nickname')" prop="nickname">
|
||||||
<el-input v-model="form.nickName" :placeholder="$t('adminUser_nickName')" autocomplete="off" type="text" />
|
<el-input v-model="form.nickname" :placeholder="$t('adminUser_nickname')" autocomplete="off" type="text" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
|
|
||||||
|
|
|
@ -96,8 +96,8 @@ onMounted(() => {
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 查询昵称 -->
|
<!-- 查询昵称 -->
|
||||||
<el-form-item :label="$t('adminUser_nickName')" prop="nickName">
|
<el-form-item :label="$t('adminUser_nickname')" prop="nickname">
|
||||||
<el-input v-model="adminUserStore.form.nickName" :placeholder="`${$t('input')}${$t('adminUser_nickName')}`" class="!w-[180px]" clearable />
|
<el-input v-model="adminUserStore.form.nickname" :placeholder="`${$t('input')}${$t('adminUser_nickname')}`" class="!w-[180px]" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 查询邮箱 -->
|
<!-- 查询邮箱 -->
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const columns: TableColumnList = [
|
||||||
// 状态
|
// 状态
|
||||||
{ label: $t('adminUser_status'), prop: 'status', slot: 'status' },
|
{ label: $t('adminUser_status'), prop: 'status', slot: 'status' },
|
||||||
// 昵称
|
// 昵称
|
||||||
{ label: $t('adminUser_nickName'), prop: 'nickName', width: 260 },
|
{ label: $t('adminUser_nickname'), prop: 'nickname', width: 260 },
|
||||||
// 邮箱
|
// 邮箱
|
||||||
{ label: $t('adminUser_email'), prop: 'email', width: 260 },
|
{ label: $t('adminUser_email'), prop: 'email', width: 260 },
|
||||||
// 手机号
|
// 手机号
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function onAdd() {
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
username: undefined,
|
username: undefined,
|
||||||
nickName: undefined,
|
nickname: undefined,
|
||||||
email: undefined,
|
email: undefined,
|
||||||
phone: undefined,
|
phone: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
|
@ -100,7 +100,7 @@ export function onUpdate(row: any) {
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
username: row.username,
|
username: row.username,
|
||||||
nickName: row.nickName,
|
nickname: row.nickname,
|
||||||
email: row.email,
|
email: row.email,
|
||||||
phone: row.phone,
|
phone: row.phone,
|
||||||
password: row.password,
|
password: row.password,
|
||||||
|
|
|
@ -3,7 +3,7 @@ export interface FormItemProps {
|
||||||
// 用户名
|
// 用户名
|
||||||
username: string;
|
username: string;
|
||||||
// 昵称
|
// 昵称
|
||||||
nickName: string;
|
nickname: string;
|
||||||
// 邮箱
|
// 邮箱
|
||||||
email: string;
|
email: string;
|
||||||
// 手机号
|
// 手机号
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { $t } from '@/plugins/i18n';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
routerId: { type: String as PropType<String> },
|
routerId: { type: String as PropType<String> },
|
||||||
|
warning: { type: String as PropType<String> },
|
||||||
});
|
});
|
||||||
|
|
||||||
const roleStore = useRoleStore();
|
const roleStore = useRoleStore();
|
||||||
|
@ -36,7 +37,8 @@ defineExpose({ assignRoles });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center">
|
<div class="flex flex-col items-center">
|
||||||
|
<el-text v-show="warning" class="mx-1" type="warning">{{ warning }}</el-text>
|
||||||
<el-transfer
|
<el-transfer
|
||||||
v-model="assignRoles"
|
v-model="assignRoles"
|
||||||
:button-texts="[$t('take_back'), $t('add')]"
|
:button-texts="[$t('take_back'), $t('add')]"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import editForm from '../menu-dialog.vue';
|
import editForm from '../menu-dialog.vue';
|
||||||
import { $t } from '@/plugins/i18n';
|
import { $t } from '@/plugins/i18n';
|
||||||
import { addDialog } from '@/components/BaseDialog/index';
|
import { addDialog, closeAllDialog } from '@/components/BaseDialog/index';
|
||||||
import { h, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
import type { FormItemProps } from './types';
|
import type { FormItemProps } from './types';
|
||||||
import { cloneDeep, deviceDetection } from '@pureadmin/utils';
|
import { cloneDeep, deviceDetection } from '@pureadmin/utils';
|
||||||
|
@ -8,6 +8,7 @@ import { userMenuStore } from '@/store/system/menu';
|
||||||
import AssignRouterToRole from '@/views/system/menu/assign-router-to-role.vue';
|
import AssignRouterToRole from '@/views/system/menu/assign-router-to-role.vue';
|
||||||
import { messageBox } from '@/utils/message';
|
import { messageBox } from '@/utils/message';
|
||||||
import { formatHigherMenuOptions } from '@/views/system/menu/utils/columns';
|
import { formatHigherMenuOptions } from '@/views/system/menu/utils/columns';
|
||||||
|
import { ElText } from 'element-plus';
|
||||||
|
|
||||||
// 用户是否停用加载集合
|
// 用户是否停用加载集合
|
||||||
export const switchLoadMap = ref({});
|
export const switchLoadMap = ref({});
|
||||||
|
@ -234,6 +235,7 @@ export const assignBatchRolesToRouter = () => {
|
||||||
draggable: true,
|
draggable: true,
|
||||||
closeOnClickModal: false,
|
closeOnClickModal: false,
|
||||||
fullscreenIcon: true,
|
fullscreenIcon: true,
|
||||||
|
props: { warning: $t('assignBatchRolesToRouterTip') },
|
||||||
contentRenderer: () => <AssignRouterToRole ref={assignRouterToRolesRef} />,
|
contentRenderer: () => <AssignRouterToRole ref={assignRouterToRolesRef} />,
|
||||||
beforeSure: async (done: any) => {
|
beforeSure: async (done: any) => {
|
||||||
// 表格功能
|
// 表格功能
|
||||||
|
@ -253,23 +255,34 @@ export const assignBatchRolesToRouter = () => {
|
||||||
|
|
||||||
/** 清除选中所以角色 */
|
/** 清除选中所以角色 */
|
||||||
export const clearAllRolesSelect = async () => {
|
export const clearAllRolesSelect = async () => {
|
||||||
// 表格功能
|
addDialog({
|
||||||
const { clearSelection } = tableRef.value.getTableRef();
|
|
||||||
|
|
||||||
const confirm = await messageBox({
|
|
||||||
title: $t('batchUpdates'),
|
title: $t('batchUpdates'),
|
||||||
showMessage: false,
|
width: '35%',
|
||||||
confirmMessage: undefined,
|
draggable: true,
|
||||||
cancelMessage: $t('cancel'),
|
closeOnClickModal: false,
|
||||||
|
fullscreenIcon: true,
|
||||||
|
contentRenderer: () => <ElText type={'warning'}>{$t('clearAllRolesSelectTip')}</ElText>,
|
||||||
|
beforeSure: async () => {
|
||||||
|
// 表格功能
|
||||||
|
const { clearSelection } = tableRef.value.getTableRef();
|
||||||
|
|
||||||
|
addDialog({
|
||||||
|
title: $t('doubleCheck'),
|
||||||
|
width: '30%',
|
||||||
|
draggable: true,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
fullscreenIcon: true,
|
||||||
|
contentRenderer: () => <ElText type={'warning'}>{$t('clearAllRolesSelectTip')}</ElText>,
|
||||||
|
beforeSure: async () => {
|
||||||
|
// 清除所有角色
|
||||||
|
const result = await menuStore.clearAllRolesSelect(selectIds.value);
|
||||||
|
|
||||||
|
// 更新成功关闭弹窗
|
||||||
|
if (!result) return;
|
||||||
|
clearSelection();
|
||||||
|
closeAllDialog();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 取消修改
|
|
||||||
if (!confirm) return;
|
|
||||||
|
|
||||||
// 分配用户角色
|
|
||||||
const result = await menuStore.clearAllRolesSelect(selectIds.value);
|
|
||||||
|
|
||||||
// 更新成功关闭弹窗
|
|
||||||
if (!result) return;
|
|
||||||
clearSelection();
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue