|
@@ -13,33 +13,97 @@
|
|
:rules="rules"
|
|
:rules="rules"
|
|
layout="vertical"
|
|
layout="vertical"
|
|
>
|
|
>
|
|
- <a-form-item label="账号" name="username">
|
|
|
|
- <a-input
|
|
|
|
- v-model:value="formData.username"
|
|
|
|
- placeholder="请输入账号"
|
|
|
|
- size="large"
|
|
|
|
- >
|
|
|
|
- <template #prefix>
|
|
|
|
- <UserOutlined style="color: rgba(0,0,0,.25)" />
|
|
|
|
- </template>
|
|
|
|
- </a-input>
|
|
|
|
- </a-form-item>
|
|
|
|
-
|
|
|
|
- <a-form-item label="密码" name="password">
|
|
|
|
- <a-input-password
|
|
|
|
- v-model:value="formData.password"
|
|
|
|
- placeholder="请输入密码"
|
|
|
|
- size="large"
|
|
|
|
- >
|
|
|
|
- <template #prefix>
|
|
|
|
- <LockOutlined style="color: rgba(0,0,0,.25)" />
|
|
|
|
- </template>
|
|
|
|
- </a-input-password>
|
|
|
|
- </a-form-item>
|
|
|
|
|
|
+ <!-- 账号密码登录表单 -->
|
|
|
|
+ <template v-if="loginType === 'account'">
|
|
|
|
+ <a-form-item label="账号" name="username">
|
|
|
|
+ <a-input
|
|
|
|
+ v-model:value="formData.username"
|
|
|
|
+ placeholder="请输入账号"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <template #prefix>
|
|
|
|
+ <UserOutlined style="color: rgba(0,0,0,.25)" />
|
|
|
|
+ </template>
|
|
|
|
+ </a-input>
|
|
|
|
+ </a-form-item>
|
|
|
|
+
|
|
|
|
+ <a-form-item label="密码" name="password">
|
|
|
|
+ <a-input-password
|
|
|
|
+ v-model:value="formData.password"
|
|
|
|
+ placeholder="请输入密码"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <template #prefix>
|
|
|
|
+ <LockOutlined style="color: rgba(0,0,0,.25)" />
|
|
|
|
+ </template>
|
|
|
|
+ </a-input-password>
|
|
|
|
+ </a-form-item>
|
|
|
|
+ <a-form-item label="商户编码" name="merchant_code">
|
|
|
|
+ <a-input
|
|
|
|
+ v-model:value="formData.merchant_code"
|
|
|
|
+ placeholder="请输入商户编码"
|
|
|
|
+ size="large"
|
|
|
|
+ ></a-input>
|
|
|
|
+ </a-form-item>
|
|
|
|
+ </template>
|
|
|
|
+
|
|
|
|
+ <!-- 手机号登录表单 -->
|
|
|
|
+ <template v-else-if="loginType === 'mobile'">
|
|
|
|
+ <a-form-item label="手机号" name="mobile">
|
|
|
|
+ <a-input
|
|
|
|
+ v-model:value="formData.mobile"
|
|
|
|
+ placeholder="请输入手机号"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <template #prefix>
|
|
|
|
+ <MobileOutlined style="color: rgba(0,0,0,.25)" />
|
|
|
|
+ </template>
|
|
|
|
+ </a-input>
|
|
|
|
+ </a-form-item>
|
|
|
|
+
|
|
|
|
+ <a-form-item label="验证码" name="verification_code">
|
|
|
|
+ <div class="verification-code-input">
|
|
|
|
+ <a-input
|
|
|
|
+ v-model:value="formData.verification_code"
|
|
|
|
+ placeholder="请输入验证码"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ <template #prefix>
|
|
|
|
+ <SafetyCertificateOutlined style="color: rgba(0,0,0,.25)" />
|
|
|
|
+ </template>
|
|
|
|
+ </a-input>
|
|
|
|
+ <a-button
|
|
|
|
+ :disabled="!!countdown || !formData.mobile"
|
|
|
|
+ @click="sendVerificationCode"
|
|
|
|
+ size="large"
|
|
|
|
+ >
|
|
|
|
+ {{ countdown ? `${countdown}秒后重试` : '获取验证码' }}
|
|
|
|
+ </a-button>
|
|
|
|
+ </div>
|
|
|
|
+ </a-form-item>
|
|
|
|
+ </template>
|
|
|
|
+
|
|
|
|
+ <!-- 微信登录表单 -->
|
|
|
|
+ <template v-if="loginType === 'wechat'">
|
|
|
|
+ <div class="wechat-login-container">
|
|
|
|
+ <div v-if="wechatQrCode" class="wechat-qrcode-wrapper">
|
|
|
|
+ <img
|
|
|
|
+ :src="wechatQrCode"
|
|
|
|
+ alt="微信登录二维码"
|
|
|
|
+ class="wechat-qrcode"
|
|
|
|
+ />
|
|
|
|
+ <p class="qrcode-tip">请使用微信扫描二维码登录</p>
|
|
|
|
+ <p class="qrcode-sub-tip">扫码后请在微信中确认登录</p>
|
|
|
|
+ </div>
|
|
|
|
+ <div v-else class="qrcode-loading">
|
|
|
|
+ <a-spin tip="正在加载二维码..." />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
|
|
<div class="form-options">
|
|
<div class="form-options">
|
|
- <a-checkbox v-model:checked="rememberMe">记住密码</a-checkbox>
|
|
|
|
- <a href="#" class="forgot-password">忘记密码?</a>
|
|
|
|
|
|
+ <a-checkbox v-model:checked="rememberMe">记住账号</a-checkbox>
|
|
|
|
+ <router-link to="/forget-password" class="forgot-password">忘记密码?</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<a-form-item>
|
|
<a-form-item>
|
|
@@ -48,7 +112,7 @@
|
|
:loading="loading"
|
|
:loading="loading"
|
|
size="large"
|
|
size="large"
|
|
block
|
|
block
|
|
- @click="login"
|
|
|
|
|
|
+ @click="handleLogin"
|
|
>
|
|
>
|
|
登录
|
|
登录
|
|
</a-button>
|
|
</a-button>
|
|
@@ -56,16 +120,30 @@
|
|
</a-form>
|
|
</a-form>
|
|
|
|
|
|
<div class="login-footer">
|
|
<div class="login-footer">
|
|
- <p class="register-tip">
|
|
|
|
- 还没有账号?<a href="/register">立即注册</a>
|
|
|
|
- </p>
|
|
|
|
|
|
+ <!-- <p class="register-tip">
|
|
|
|
+ 还没有账号?<router-link to="/register" class="register-link">立即注册</router-link>
|
|
|
|
+ </p> -->
|
|
|
|
+
|
|
|
|
+ <!-- 登录方式切换移动到这里 -->
|
|
|
|
+ <!-- <div class="login-type-switch">
|
|
|
|
+ <p class="switch-title">切换登录方式</p>
|
|
|
|
+ <a-radio-group v-model:value="loginType" button-style="solid">
|
|
|
|
+ <a-radio-button value="account">账号密码登录</a-radio-button>
|
|
|
|
+ <a-radio-button value="mobile">手机验证码登录</a-radio-button>
|
|
|
|
+ </a-radio-group>
|
|
|
|
+ </div> -->
|
|
|
|
+
|
|
<div class="other-login">
|
|
<div class="other-login">
|
|
<div class="divider">
|
|
<div class="divider">
|
|
<span>其他登录方式</span>
|
|
<span>其他登录方式</span>
|
|
</div>
|
|
</div>
|
|
<div class="social-login">
|
|
<div class="social-login">
|
|
<a-tooltip title="微信登录">
|
|
<a-tooltip title="微信登录">
|
|
- <WechatOutlined class="social-icon" />
|
|
|
|
|
|
+ <WechatOutlined
|
|
|
|
+ class="social-icon"
|
|
|
|
+ :class="{ active: loginType === 'wechat' }"
|
|
|
|
+ @click="switchToWechatLogin"
|
|
|
|
+ />
|
|
</a-tooltip>
|
|
</a-tooltip>
|
|
<a-tooltip title="QQ登录">
|
|
<a-tooltip title="QQ登录">
|
|
<QqOutlined class="social-icon" />
|
|
<QqOutlined class="social-icon" />
|
|
@@ -73,24 +151,59 @@
|
|
<a-tooltip title="钉钉登录">
|
|
<a-tooltip title="钉钉登录">
|
|
<DingdingOutlined class="social-icon" />
|
|
<DingdingOutlined class="social-icon" />
|
|
</a-tooltip>
|
|
</a-tooltip>
|
|
|
|
+ <a-tooltip v-if="loginType === 'account'" title="手机号登录">
|
|
|
|
+ <MobileOutlined
|
|
|
|
+ class="social-icon"
|
|
|
|
+ @click="switchToMobileLogin"
|
|
|
|
+ />
|
|
|
|
+ </a-tooltip>
|
|
|
|
+ <a-tooltip v-else title="账号密码登录">
|
|
|
|
+ <UserOutlined
|
|
|
|
+ class="social-icon"
|
|
|
|
+ @click="switchToAccountLogin"
|
|
|
|
+ />
|
|
|
|
+ </a-tooltip>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
+
|
|
|
|
+ <!-- 微信登录弹窗 -->
|
|
|
|
+ <a-modal
|
|
|
|
+ v-model:visible="wechatLoginModal"
|
|
|
|
+ title="微信扫码登录"
|
|
|
|
+ :footer="null"
|
|
|
|
+ @cancel="handleWechatLoginCancel"
|
|
|
|
+ width="360px"
|
|
|
|
+ centered
|
|
|
|
+ >
|
|
|
|
+ <div class="wechat-qrcode-container">
|
|
|
|
+ <img
|
|
|
|
+ :src="wechatQrCode"
|
|
|
|
+ alt="微信登录二维码"
|
|
|
|
+ class="wechat-qrcode"
|
|
|
|
+ />
|
|
|
|
+ <p class="qrcode-tip">请使用微信扫描二维码登录</p>
|
|
|
|
+ </div>
|
|
|
|
+ </a-modal>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script>
|
|
<script>
|
|
-import { defineComponent, reactive, ref } from 'vue';
|
|
|
|
|
|
+import { defineComponent, reactive, ref, nextTick, onUnmounted } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { useRouter } from 'vue-router';
|
|
import { useStore } from 'vuex';
|
|
import { useStore } from 'vuex';
|
|
|
|
+import axios from 'axios';
|
|
|
|
+import { message, Modal } from 'ant-design-vue';
|
|
import {
|
|
import {
|
|
UserOutlined,
|
|
UserOutlined,
|
|
LockOutlined,
|
|
LockOutlined,
|
|
WechatOutlined,
|
|
WechatOutlined,
|
|
QqOutlined,
|
|
QqOutlined,
|
|
- DingdingOutlined
|
|
|
|
|
|
+ DingdingOutlined,
|
|
|
|
+ MobileOutlined,
|
|
|
|
+ SafetyCertificateOutlined
|
|
} from '@ant-design/icons-vue';
|
|
} from '@ant-design/icons-vue';
|
|
|
|
|
|
export default defineComponent({
|
|
export default defineComponent({
|
|
@@ -100,7 +213,9 @@ export default defineComponent({
|
|
LockOutlined,
|
|
LockOutlined,
|
|
WechatOutlined,
|
|
WechatOutlined,
|
|
QqOutlined,
|
|
QqOutlined,
|
|
- DingdingOutlined
|
|
|
|
|
|
+ DingdingOutlined,
|
|
|
|
+ MobileOutlined,
|
|
|
|
+ SafetyCertificateOutlined
|
|
},
|
|
},
|
|
setup() {
|
|
setup() {
|
|
const store = useStore();
|
|
const store = useStore();
|
|
@@ -108,10 +223,15 @@ export default defineComponent({
|
|
const loginForm = ref();
|
|
const loginForm = ref();
|
|
const loading = ref(false);
|
|
const loading = ref(false);
|
|
const rememberMe = ref(false);
|
|
const rememberMe = ref(false);
|
|
|
|
+ const loginType = ref('account'); // 登录方式:'account', 'mobile', 'wechat'
|
|
|
|
+ const countdown = ref(0); // 验证码倒计时
|
|
|
|
|
|
const formData = reactive({
|
|
const formData = reactive({
|
|
username: '',
|
|
username: '',
|
|
- password: ''
|
|
|
|
|
|
+ password: '',
|
|
|
|
+ merchant_code: 'E20250212000624fd2a92',
|
|
|
|
+ mobile: '',
|
|
|
|
+ verification_code: ''
|
|
});
|
|
});
|
|
|
|
|
|
const rules = {
|
|
const rules = {
|
|
@@ -122,59 +242,323 @@ export default defineComponent({
|
|
password: [
|
|
password: [
|
|
{ required: true, message: '请输入密码' },
|
|
{ required: true, message: '请输入密码' },
|
|
{ min: 6, message: '密码长度不能小于6位' }
|
|
{ min: 6, message: '密码长度不能小于6位' }
|
|
|
|
+ ],
|
|
|
|
+ merchant_code: [
|
|
|
|
+ { required: true, message: '请输入商户编码' },
|
|
|
|
+ { min: 1, message: '商户编码长度不能小于1位' }
|
|
|
|
+ ],
|
|
|
|
+ mobile: [
|
|
|
|
+ { required: true, message: '请输入手机号' },
|
|
|
|
+ { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
|
|
|
|
+ ],
|
|
|
|
+ verification_code: [
|
|
|
|
+ { required: true, message: '请输入验证码' },
|
|
|
|
+ { len: 6, message: '验证码长度必须为6位' }
|
|
]
|
|
]
|
|
};
|
|
};
|
|
|
|
|
|
- const login = async () => {
|
|
|
|
|
|
+ // 发送验证码
|
|
|
|
+ const sendVerificationCode = async () => {
|
|
|
|
+ try {
|
|
|
|
+ // 验证手机号
|
|
|
|
+ await loginForm.value.validateFields(['mobile']);
|
|
|
|
+
|
|
|
|
+ const response = await axios.post(`${import.meta.env.VITE_BASE_AI_API}/user/send_verification_code`, {
|
|
|
|
+ mobile: formData.mobile,
|
|
|
|
+ merchant_code: formData.merchant_code
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (response.data.code === 200) {
|
|
|
|
+ message.success('验证码发送成功');
|
|
|
|
+ // 开始倒计时
|
|
|
|
+ countdown.value = 60;
|
|
|
|
+ const timer = setInterval(() => {
|
|
|
|
+ countdown.value--;
|
|
|
|
+ if (countdown.value <= 0) {
|
|
|
|
+ clearInterval(timer);
|
|
|
|
+ }
|
|
|
|
+ }, 1000);
|
|
|
|
+ } else {
|
|
|
|
+ message.error(response.data.message || '验证码发送失败');
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('验证码发送失败:', error);
|
|
|
|
+ message.error(error.response?.data?.message || '验证码发送失败');
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 处理登录
|
|
|
|
+ const handleLogin = async () => {
|
|
try {
|
|
try {
|
|
loading.value = true;
|
|
loading.value = true;
|
|
await loginForm.value.validate();
|
|
await loginForm.value.validate();
|
|
|
|
|
|
- let permission = [
|
|
|
|
- { type: 'menu', path: '/report', key: '' },
|
|
|
|
- { type: 'btn', path: '/report', key: 'add' },
|
|
|
|
- { type: 'btn', path: '/report', key: 'edit' },
|
|
|
|
- { type: 'btn', path: '/report', key: 'delete' },
|
|
|
|
- { type: 'btn', path: '/report', key: 'export' },
|
|
|
|
- { type: 'menu', path: '/setting/user/add' },
|
|
|
|
- { type: 'menu', path: '/setting/user/edit' },
|
|
|
|
- { type: 'menu', path: '/setting/user/delete' },
|
|
|
|
- { type: 'menu', path: '/setting/role' },
|
|
|
|
- { type: 'menu', path: '/login' },
|
|
|
|
- ];
|
|
|
|
-
|
|
|
|
- const auth = {};
|
|
|
|
- permission.filter(item => item.type === 'btn').forEach(item => {
|
|
|
|
- if(auth[item.path]) {
|
|
|
|
- auth[item.path][item.key] = true;
|
|
|
|
- } else {
|
|
|
|
- auth[item.path] = {
|
|
|
|
- [item.key]: true
|
|
|
|
- };
|
|
|
|
|
|
+ let response;
|
|
|
|
+ if (loginType.value === 'account') {
|
|
|
|
+ // 账号密码登录
|
|
|
|
+ response = await axios.post(`${import.meta.env.VITE_BASE_AI_API}/user/login`, {
|
|
|
|
+ username: formData.username,
|
|
|
|
+ password: formData.password,
|
|
|
|
+ merchant_code: formData.merchant_code
|
|
|
|
+ });
|
|
|
|
+ } else if (loginType.value === 'mobile') {
|
|
|
|
+ // 手机验证码登录
|
|
|
|
+ response = await axios.post(`${import.meta.env.VITE_BASE_AI_API}/user/mobile_login`, {
|
|
|
|
+ mobile: formData.mobile,
|
|
|
|
+ verification_code: formData.verification_code,
|
|
|
|
+ merchant_code: formData.merchant_code
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ // 微信登录
|
|
|
|
+ response = await axios.post(`${import.meta.env.VITE_BASE_AI_API}/user/wechat_login`, {
|
|
|
|
+ wechat_state: formData.wechat_state
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (response.data.code === 200) {
|
|
|
|
+ // 保存token到localStorage
|
|
|
|
+ localStorage.setItem('token', response.data.data.token);
|
|
|
|
+
|
|
|
|
+ // 获取用户详细信息
|
|
|
|
+ try {
|
|
|
|
+ const userInfoResponse = await axios.get(`${import.meta.env.VITE_BASE_AI_API}/user/info`, {
|
|
|
|
+ headers: {
|
|
|
|
+ 'Authorization': `JWT ${response.data.data.token}`
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ console.log(userInfoResponse);
|
|
|
|
+ if (userInfoResponse.data.code === 200) {
|
|
|
|
+ // 存储用户信息和权限到 Vuex store
|
|
|
|
+ store.commit('app/setUser', {
|
|
|
|
+ user: userInfoResponse.data.data,
|
|
|
|
+ permission: userInfoResponse.data.data || [],
|
|
|
|
+
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 同时存储到 localStorage
|
|
|
|
+ localStorage.setItem('userInfo', JSON.stringify(userInfoResponse.data.data));
|
|
|
|
+ localStorage.setItem('permissions', JSON.stringify(userInfoResponse.data.data || []));
|
|
|
|
+ } else {
|
|
|
|
+ message.warning('获取用户信息失败');
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取用户信息失败:', error);
|
|
|
|
+ message.warning('获取用户信息失败,请重新登录');
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
- });
|
|
|
|
-
|
|
|
|
- store.commit('app/setUser', {
|
|
|
|
- user: { id: 1186, name: '张三' },
|
|
|
|
- permission,
|
|
|
|
- auth
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- router.push('/');
|
|
|
|
|
|
+
|
|
|
|
+ // 记住账号
|
|
|
|
+ if (rememberMe.value) {
|
|
|
|
+ if (loginType.value === 'account') {
|
|
|
|
+ localStorage.setItem('username', formData.username);
|
|
|
|
+ } else if (loginType.value === 'mobile') {
|
|
|
|
+ localStorage.setItem('mobile', formData.mobile);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ message.success('登录成功');
|
|
|
|
+ router.push('/');
|
|
|
|
+ } else {
|
|
|
|
+ message.error(response.data.message || '登录失败');
|
|
|
|
+ }
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.error('登录失败:', error);
|
|
console.error('登录失败:', error);
|
|
|
|
+ message.error(error.response?.data?.message || '登录失败,请检查网络连接');
|
|
} finally {
|
|
} finally {
|
|
loading.value = false;
|
|
loading.value = false;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ // 处理权限数据
|
|
|
|
+ const processPermissions = (permissions = []) => {
|
|
|
|
+ const auth = {};
|
|
|
|
+ permissions.filter(item => item.type === 'btn').forEach(item => {
|
|
|
|
+ if(auth[item.path]) {
|
|
|
|
+ auth[item.path][item.key] = true;
|
|
|
|
+ } else {
|
|
|
|
+ auth[item.path] = {
|
|
|
|
+ [item.key]: true
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return auth;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 初始化时从本地存储获取记住的信息
|
|
|
|
+ if (localStorage.getItem('username')) {
|
|
|
|
+ formData.username = localStorage.getItem('username');
|
|
|
|
+ rememberMe.value = true;
|
|
|
|
+ } else if (localStorage.getItem('mobile')) {
|
|
|
|
+ formData.mobile = localStorage.getItem('mobile');
|
|
|
|
+ loginType.value = 'mobile';
|
|
|
|
+ rememberMe.value = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 切换到手机号登录
|
|
|
|
+ const switchToMobileLogin = () => {
|
|
|
|
+ loginType.value = 'mobile';
|
|
|
|
+ // 清空之前的登录表单数据
|
|
|
|
+ formData.username = '';
|
|
|
|
+ formData.password = '';
|
|
|
|
+ // 如果之前保存过手机号,则自动填充
|
|
|
|
+ const savedMobile = localStorage.getItem('mobile');
|
|
|
|
+ if (savedMobile) {
|
|
|
|
+ formData.mobile = savedMobile;
|
|
|
|
+ }
|
|
|
|
+ // 重置表单验证状态
|
|
|
|
+ nextTick(() => {
|
|
|
|
+ loginForm.value?.resetFields();
|
|
|
|
+ });
|
|
|
|
+ // 显示切换提示
|
|
|
|
+ message.info('已切换到手机号登录模式');
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 切换到账号密码登录
|
|
|
|
+ const switchToAccountLogin = () => {
|
|
|
|
+ loginType.value = 'account';
|
|
|
|
+ // 清空之前的登录表单数据
|
|
|
|
+ formData.mobile = '';
|
|
|
|
+ formData.verification_code = '';
|
|
|
|
+ // 如果之前保存过手机号,则自动填充
|
|
|
|
+ const savedMobile = localStorage.getItem('mobile');
|
|
|
|
+ if (savedMobile) {
|
|
|
|
+ formData.mobile = savedMobile;
|
|
|
|
+ }
|
|
|
|
+ // 重置表单验证状态
|
|
|
|
+ nextTick(() => {
|
|
|
|
+ loginForm.value?.resetFields();
|
|
|
|
+ });
|
|
|
|
+ // 显示切换提示
|
|
|
|
+ message.info('已切换到账号密码登录模式');
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 切换到微信登录
|
|
|
|
+ const switchToWechatLogin = async () => {
|
|
|
|
+ loginType.value = 'wechat';
|
|
|
|
+ // 清空之前的登录表单数据
|
|
|
|
+ formData.username = '';
|
|
|
|
+ formData.password = '';
|
|
|
|
+ formData.mobile = '';
|
|
|
|
+ formData.verification_code = '';
|
|
|
|
+ // 获取微信二维码
|
|
|
|
+ await getWechatQrCode();
|
|
|
|
+ // 重置表单验证状态
|
|
|
|
+ nextTick(() => {
|
|
|
|
+ loginForm.value?.resetFields();
|
|
|
|
+ });
|
|
|
|
+ message.info('已切换到微信扫码登录模式');
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 微信登录相关状态
|
|
|
|
+ const wechatLoginModal = ref(false);
|
|
|
|
+ const wechatQrCode = ref('');
|
|
|
|
+ const wechatState = ref('');
|
|
|
|
+ let wechatLoginTimer = null;
|
|
|
|
+
|
|
|
|
+ // 获取微信登录二维码
|
|
|
|
+ const getWechatQrCode = async () => {
|
|
|
|
+ try {
|
|
|
|
+ const response = await axios.get(`${import.meta.env.VITE_BASE_AI_API}/api/user/wechat/qrcode`);
|
|
|
|
+ if (response.data.code === 200) {
|
|
|
|
+ wechatQrCode.value = response.data.data.qrcode_url;
|
|
|
|
+ wechatState.value = response.data.data.state;
|
|
|
|
+ startWechatLoginCheck();
|
|
|
|
+ } else {
|
|
|
|
+ message.error('获取微信二维码失败');
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取微信二维码失败:', error);
|
|
|
|
+ message.error('获取微信二维码失败,请稍后重试');
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 开始轮询检查微信登录状态
|
|
|
|
+ const startWechatLoginCheck = () => {
|
|
|
|
+ wechatLoginTimer = setInterval(async () => {
|
|
|
|
+ try {
|
|
|
|
+ const response = await axios.get(`${import.meta.env.VITE_BASE_AI_API}/api/user/wechat/check_login`, {
|
|
|
|
+ params: { state: wechatState.value }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (response.data.code === 200) {
|
|
|
|
+ // 登录成功
|
|
|
|
+ clearInterval(wechatLoginTimer);
|
|
|
|
+ wechatLoginModal.value = false;
|
|
|
|
+
|
|
|
|
+ // 保存token
|
|
|
|
+ localStorage.setItem('token', response.data.data.token);
|
|
|
|
+
|
|
|
|
+ // 获取并保存用户信息
|
|
|
|
+ await getUserInfo(response.data.data.token);
|
|
|
|
+
|
|
|
|
+ message.success('微信登录成功');
|
|
|
|
+ router.push('/');
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('检查微信登录状态失败:', error);
|
|
|
|
+ }
|
|
|
|
+ }, 2000); // 每2秒检查一次
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 关闭微信登录弹窗
|
|
|
|
+ const handleWechatLoginCancel = () => {
|
|
|
|
+ wechatLoginModal.value = false;
|
|
|
|
+ if (wechatLoginTimer) {
|
|
|
|
+ clearInterval(wechatLoginTimer);
|
|
|
|
+ wechatLoginTimer = null;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 获取用户信息的公共方法
|
|
|
|
+ const getUserInfo = async (token) => {
|
|
|
|
+ try {
|
|
|
|
+ const userInfoResponse = await axios.get(`${import.meta.env.VITE_BASE_AI_API}/user/info`, {
|
|
|
|
+ headers: {
|
|
|
|
+ 'Authorization': `JWT ${token}`
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (userInfoResponse.data.code === 200) {
|
|
|
|
+ store.commit('app/setUser', {
|
|
|
|
+ user: userInfoResponse.data.data.userInfo,
|
|
|
|
+ permission: userInfoResponse.data.data.permissions || [],
|
|
|
|
+ auth: processPermissions(userInfoResponse.data.data.permissions)
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ message.warning('获取用户信息失败');
|
|
|
|
+ }
|
|
|
|
+ } catch (error) {
|
|
|
|
+ console.error('获取用户信息失败:', error);
|
|
|
|
+ message.warning('获取用户信息失败,请重新登录');
|
|
|
|
+ throw error;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 组件卸载时清理定时器
|
|
|
|
+ onUnmounted(() => {
|
|
|
|
+ if (wechatLoginTimer) {
|
|
|
|
+ clearInterval(wechatLoginTimer);
|
|
|
|
+ wechatLoginTimer = null;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
return {
|
|
return {
|
|
loginForm,
|
|
loginForm,
|
|
formData,
|
|
formData,
|
|
rules,
|
|
rules,
|
|
loading,
|
|
loading,
|
|
rememberMe,
|
|
rememberMe,
|
|
- login
|
|
|
|
|
|
+ loginType,
|
|
|
|
+ countdown,
|
|
|
|
+ handleLogin,
|
|
|
|
+ sendVerificationCode,
|
|
|
|
+ switchToMobileLogin,
|
|
|
|
+ switchToAccountLogin,
|
|
|
|
+ switchToWechatLogin,
|
|
|
|
+ wechatLoginModal,
|
|
|
|
+ wechatQrCode,
|
|
|
|
+ getWechatQrCode,
|
|
|
|
+ handleWechatLoginCancel
|
|
};
|
|
};
|
|
}
|
|
}
|
|
});
|
|
});
|
|
@@ -237,6 +621,19 @@ export default defineComponent({
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ .verification-code-input {
|
|
|
|
+ display: flex;
|
|
|
|
+ gap: 8px;
|
|
|
|
+
|
|
|
|
+ .ant-input-affix-wrapper {
|
|
|
|
+ flex: 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .ant-btn {
|
|
|
|
+ min-width: 120px;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
.login-footer {
|
|
.login-footer {
|
|
margin-top: 32px;
|
|
margin-top: 32px;
|
|
text-align: center;
|
|
text-align: center;
|
|
@@ -246,7 +643,7 @@ export default defineComponent({
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
margin-bottom: 24px;
|
|
margin-bottom: 24px;
|
|
|
|
|
|
- a {
|
|
|
|
|
|
+ .register-link {
|
|
color: #667eea;
|
|
color: #667eea;
|
|
font-weight: 500;
|
|
font-weight: 500;
|
|
margin-left: 4px;
|
|
margin-left: 4px;
|
|
@@ -257,6 +654,35 @@ export default defineComponent({
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // 更新登录方式切换的样式
|
|
|
|
+ .login-type-switch {
|
|
|
|
+ margin-bottom: 24px;
|
|
|
|
+
|
|
|
|
+ .switch-title {
|
|
|
|
+ color: #666;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .ant-radio-group {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ gap: 12px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .ant-radio-button-wrapper {
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+
|
|
|
|
+ &:first-child {
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &:last-child {
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
.divider {
|
|
.divider {
|
|
position: relative;
|
|
position: relative;
|
|
text-align: center;
|
|
text-align: center;
|
|
@@ -304,6 +730,11 @@ export default defineComponent({
|
|
color: #667eea;
|
|
color: #667eea;
|
|
transform: scale(1.1);
|
|
transform: scale(1.1);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // 为手机图标添加特殊样式
|
|
|
|
+ &.mobile-active {
|
|
|
|
+ color: #667eea;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -330,4 +761,67 @@ export default defineComponent({
|
|
font-size: 24px;
|
|
font-size: 24px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+.wechat-qrcode-container {
|
|
|
|
+ text-align: center;
|
|
|
|
+ padding: 20px 0;
|
|
|
|
+
|
|
|
|
+ .wechat-qrcode {
|
|
|
|
+ width: 200px;
|
|
|
|
+ height: 200px;
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .qrcode-tip {
|
|
|
|
+ color: #666;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.wechat-login-container {
|
|
|
|
+ padding: 20px 0;
|
|
|
|
+ text-align: center;
|
|
|
|
+
|
|
|
|
+ .wechat-qrcode-wrapper {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ align-items: center;
|
|
|
|
+ padding: 20px;
|
|
|
|
+ background: #f5f5f5;
|
|
|
|
+ border-radius: 8px;
|
|
|
|
+
|
|
|
|
+ .wechat-qrcode {
|
|
|
|
+ width: 200px;
|
|
|
|
+ height: 200px;
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .qrcode-tip {
|
|
|
|
+ color: #333;
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ font-weight: 500;
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .qrcode-sub-tip {
|
|
|
|
+ color: #666;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .qrcode-loading {
|
|
|
|
+ padding: 60px 0;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 修改社交图标样式
|
|
|
|
+.social-login {
|
|
|
|
+ .social-icon {
|
|
|
|
+ &.active {
|
|
|
|
+ color: #667eea;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
</style>
|
|
</style>
|