手机号注册手机号登录

This commit is contained in:
Songsl 2026-05-09 10:47:10 +08:00
parent 8d2350f231
commit 5e8f91c3bb
4 changed files with 132 additions and 67 deletions

View File

@ -85,6 +85,11 @@ const formData = ref({
api_url: '', api_url: '',
api_appid: '', api_appid: '',
api_key: '', api_key: '',
beneficiary_name: '',
beneficiary_bank_name: '',
swift_bic_code: '',
beneficiary_bank_address: '',
channel_code: '',
status: 1, status: 1,
}) })
const formItems = ref(formItemsData) const formItems = ref(formItemsData)
@ -116,6 +121,11 @@ const resetFormData = () => {
api_appid: '', api_appid: '',
api_key: '', api_key: '',
status: 1, status: 1,
beneficiary_name: '',
beneficiary_bank_name: '',
swift_bic_code: '',
beneficiary_bank_address: '',
channel_code: '',
} }
} }
@ -130,20 +140,12 @@ const handleEdit = () => {
dialogVisible.value = true dialogVisible.value = true
dialogTitleType.value = 2 dialogTitleType.value = 2
formData.value = { formData.value = currentRow
id: currentRow.id,
name: currentRow.name,
channel_fee: currentRow.channel_fee,
type: currentRow.type,
api_url: currentRow.api_url,
api_appid: currentRow.api_appid,
api_key: currentRow.api_key,
status: currentRow.status,
}
} }
const handleDelete = () => { const handleDelete = () => {
console.log('handleDisable') console.log('handleDisable')
if (!currentRow.id) return ElMessage.warning('请先选择要删除的记录')
ElMessageBox.confirm('确认删除吗?', '提示', { ElMessageBox.confirm('确认删除吗?', '提示', {
type: 'warning', type: 'warning',
confirmButtonText: '确定', confirmButtonText: '确定',

View File

@ -34,6 +34,31 @@ export const formItemsData = [
{ label: '接口地址', prop: 'api_url', type: 'input', placeholder: '请输入接口地址' }, { label: '接口地址', prop: 'api_url', type: 'input', placeholder: '请输入接口地址' },
{ label: '接口appid', prop: 'api_appid', type: 'input', placeholder: '请输入接口appid' }, { label: '接口appid', prop: 'api_appid', type: 'input', placeholder: '请输入接口appid' },
{ label: '接口密钥', prop: 'api_key', type: 'input', placeholder: '请输入接口密钥' }, { label: '接口密钥', prop: 'api_key', type: 'input', placeholder: '请输入接口密钥' },
{
label: '账户持有人姓名',
prop: 'beneficiary_name',
type: 'input',
placeholder: '请输入账户持有人姓名',
},
{
label: '银行名称',
prop: 'beneficiary_bank_name',
type: 'input',
placeholder: '请输入银行名称',
},
{
label: '银行识别代码',
prop: 'swift_bic_code',
type: 'input',
placeholder: '请输入银行识别代码',
},
{
label: '银行地址',
prop: 'beneficiary_bank_address',
type: 'input',
placeholder: '请输入银行地址',
},
{ label: '渠道编码', prop: 'channel_code', type: 'input', placeholder: '请输入渠道编码' },
{ label: '状态', prop: 'status', type: 'radio' }, { label: '状态', prop: 'status', type: 'radio' },
] ]
@ -45,6 +70,11 @@ export const formRulesData = {
api_appid: [{ required: true, message: '请输入接口appid', trigger: ['change'] }], api_appid: [{ required: true, message: '请输入接口appid', trigger: ['change'] }],
api_key: [{ required: true, message: '请输入接口密钥', trigger: ['change'] }], api_key: [{ required: true, message: '请输入接口密钥', trigger: ['change'] }],
status: [{ required: true, message: '请选择状态', trigger: ['change'] }], status: [{ required: true, message: '请选择状态', trigger: ['change'] }],
beneficiary_name: [{ required: true, message: '请输入账户持有人姓名', trigger: ['change'] }],
beneficiary_bank_name: [{ required: true, message: '请输入银行名称', trigger: ['change'] }],
swift_bic_code: [{ required: true, message: '请输入银行识别代码', trigger: ['change'] }],
beneficiary_bank_address: [{ required: true, message: '请输入银行地址', trigger: ['change'] }],
channel_code: [{ required: true, message: '请输入渠道编码', trigger: ['change'] }],
} }
export const optionsMapData = { export const optionsMapData = {

View File

@ -10,9 +10,25 @@
<el-input v-model="loginForm.email" placeholder="请输入邮箱" clearable size="large" /> <el-input v-model="loginForm.email" placeholder="请输入邮箱" clearable size="large" />
</el-form-item> </el-form-item>
<el-form-item class="phone-item" prop="phone" v-else-if="activeTab === 'phone'" key="form-phone-item"> <el-form-item
<el-select v-model="loginForm.countryCode" :teleported="false" value-key="code" placeholder="国家" size="large"> class="phone-item"
<el-option v-for="item in countryOptions" :key="item.code" :label="item.code" :value="item.code"> prop="phone"
v-else-if="activeTab === 'phone'"
key="form-phone-item"
>
<el-select
v-model="loginForm.countryCode"
:teleported="false"
value-key="code"
placeholder="国家"
size="large"
>
<el-option
v-for="item in countryOptions"
:key="item.code"
:label="item.code"
:value="item.code"
>
<div class="country-option"> <div class="country-option">
<span class="country-name">{{ item.name }}</span> <span class="country-name">{{ item.name }}</span>
<span class="country-code">{{ item.code }}</span> <span class="country-code">{{ item.code }}</span>
@ -23,13 +39,21 @@
</el-form-item> </el-form-item>
<el-form-item v-else-if="activeTab === 'password'" key="form-password-item" prop="password"> <el-form-item v-else-if="activeTab === 'password'" key="form-password-item" prop="password">
<el-input v-model="loginForm.password" placeholder="请输入密码" type="password" show-password clearable <el-input
size="large" /> v-model="loginForm.password"
placeholder="请输入密码"
type="password"
show-password
clearable
size="large"
/>
<router-link to="/user/forgot-password" class="forgot-password">忘记密码?</router-link> <router-link to="/user/forgot-password" class="forgot-password">忘记密码?</router-link>
</el-form-item> </el-form-item>
</Transition> </Transition>
<el-form-item class="form-submit"> <el-form-item class="form-submit">
<el-button type="primary" @click="handleLogin">{{ activeTab === 'password' ? '登录' : '注册/登录' }}</el-button> <el-button type="primary" @click="handleLogin">{{
activeTab === 'password' ? '登录' : '注册/登录'
}}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@ -58,7 +82,6 @@ const userText = ref({
title: '', title: '',
text: '初次登录时,您的电子邮箱或手机号码将被用于注册。', text: '初次登录时,您的电子邮箱或手机号码将被用于注册。',
account: '', account: '',
}) })
//tabs 实例 //tabs 实例
@ -70,7 +93,7 @@ const activeTab = ref<string>('email')
const hideTab = ref<boolean>(false) const hideTab = ref<boolean>(false)
// login form 实例 // login form 实例
const userStore = useUserStore(); const userStore = useUserStore()
const loginFormRef = ref<FormInstance>() const loginFormRef = ref<FormInstance>()
const loginForm = ref({ const loginForm = ref({
email: '', email: '',
@ -94,39 +117,44 @@ const loginRules = ref<FormRules>({
*/ */
function generateTimestampWithRandomNum() { function generateTimestampWithRandomNum() {
// 1. 获取当前时间戳(毫秒级) // 1. 获取当前时间戳(毫秒级)
const timestamp = Date.now(); const timestamp = Date.now()
// 2. 生成6位随机数字(范围:000000 ~ 999999) // 2. 生成6位随机数字(范围:000000 ~ 999999)
// Math.random()生成0~1的随机数,乘以1000000后取整,不足6位补0 // Math.random()生成0~1的随机数,乘以1000000后取整,不足6位补0
const random6Num = Math.floor(Math.random() * 1000000).toString().padStart(6, '0'); const random6Num = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, '0')
// 3. 拼接并返回 // 3. 拼接并返回
return timestamp + random6Num; return timestamp + random6Num
} }
// 登录 // 登录
const handleLogin = async () => { const handleLogin = async () => {
if (!loginFormRef.value) return; if (!loginFormRef.value) return
try { try {
const currentTab = activeTab.value; const currentTab = activeTab.value
//验证邮箱/手机号 → 切换到密码页 //验证邮箱/手机号 → 切换到密码页
if (currentTab === 'email' || currentTab === 'phone') { if (currentTab === 'email' || currentTab === 'phone') {
//校验当前账号字段(email/phone) //校验当前账号字段(email/phone)
await loginFormRef.value.validateField(currentTab); await loginFormRef.value.validateField(currentTab)
//存储账号类型和账号值(方便后续登录取值) //存储账号类型和账号值(方便后续登录取值)
const loginValue = loginForm.value[currentTab]; const loginValue =
setStorage('loginType', currentTab, 'session'); // 存储账号类型(email/phone) currentTab === 'phone'
setStorage('login', loginValue, 'session'); // 存储账号值 ? loginForm.value.countryCode + loginForm.value[currentTab]
: loginForm.value[currentTab]
setStorage('loginType', currentTab, 'session') // 存储账号类型(email/phone)
setStorage('login', loginValue, 'session') // 存储账号值
//切换到密码页,更新提示 //切换到密码页,更新提示
hideTab.value = true; hideTab.value = true
userText.value = { userText.value = {
backText: '返回', backText: '返回',
title: '欢迎回来!', title: '欢迎回来!',
text: '请输入您的登录密码', text: '请输入您的登录密码',
account: loginValue account: loginValue,
}; }
activeTab.value = 'password'; activeTab.value = 'password'
if (currentTab === 'phone' && loginForm.value.email !== '') { if (currentTab === 'phone' && loginForm.value.email !== '') {
loginForm.value.email = '' loginForm.value.email = ''
@ -135,32 +163,33 @@ const handleLogin = async () => {
// 验证密码+验证码 → 执行登录 // 验证密码+验证码 → 执行登录
else if (currentTab === 'password') { else if (currentTab === 'password') {
// 校验密码+验证码 // 校验密码+验证码
await loginFormRef.value.validateField(['password', 'code']); await loginFormRef.value.validateField(['password', 'code'])
// 生成device_no并保存到变量(关键修改1) // 生成device_no并保存到变量(关键修改1)
const deviceNo = generateTimestampWithRandomNum(); const deviceNo = generateTimestampWithRandomNum()
// 登录参数 // 登录参数
const loginParams = { const loginParams = {
email: loginForm.value.email, email: loginForm.value.email,
mobile: loginForm.value.phone, mobile: loginForm.value.countryCode + loginForm.value.phone,
login_type: getStorage('loginType', 'session') === 'email' ? 1 : 2, login_type: getStorage('loginType', 'session') === 'email' ? 1 : 2,
code: loginForm.value.code, code: loginForm.value.code,
password: loginForm.value.password, password: loginForm.value.password,
device_no: deviceNo device_no: deviceNo,
}; }
await userStore.login(loginParams); await userStore.login(loginParams)
setStorage('device_no', deviceNo); setStorage('device_no', deviceNo)
activeTab.value = 'email'; // 重置tab activeTab.value = 'email' // 重置tab
hideTab.value = false; hideTab.value = false
loginForm.value = { // 清空表单 loginForm.value = {
// 清空表单
email: '', email: '',
countryCode: '+86', countryCode: '+86',
phone: '', phone: '',
password: '', password: '',
code: '' code: '',
}; }
} }
} catch (error) { } catch (error) {
console.error('表单校验失败:', error); console.error('表单校验失败:', error)
} }
} }
@ -168,30 +197,30 @@ const handleLogin = async () => {
const handleBack = () => { const handleBack = () => {
if (activeTab.value === 'password') { if (activeTab.value === 'password') {
// 从sessionStorage取账号类型(而非用password作为key) // 从sessionStorage取账号类型(而非用password作为key)
const loginType = getStorage('loginType', 'session') || 'email'; const loginType = getStorage('loginType', 'session') || 'email'
// 清空密码、验证码字段,避免残留 // 清空密码、验证码字段,避免残留
loginForm.value.password = ''; loginForm.value.password = ''
loginForm.value.code = ''; loginForm.value.code = ''
// 清空表单校验提示 // 清空表单校验提示
loginFormRef.value?.clearValidate(['password', 'code']); loginFormRef.value?.clearValidate(['password', 'code'])
// 重置文本和tab状态 // 重置文本和tab状态
hideTab.value = false; hideTab.value = false
userText.value = { userText.value = {
backText: '', backText: '',
title: '', title: '',
text: '初次登录时,您的电子邮箱或手机号码将被用于注册。', text: '初次登录时,您的电子邮箱或手机号码将被用于注册。',
account: '' account: '',
}; }
activeTab.value = loginType as string; activeTab.value = loginType as string
} else { } else {
// 非password页:仅重置为email(可选,也可保留当前tab) // 非password页:仅重置为email(可选,也可保留当前tab)
activeTab.value = 'email'; activeTab.value = 'email'
// 清空当前账号字段和校验提示 // 清空当前账号字段和校验提示
loginForm.value.email = ''; loginForm.value.email = ''
loginForm.value.phone = ''; loginForm.value.phone = ''
loginFormRef.value?.clearValidate(['email', 'phone']); loginFormRef.value?.clearValidate(['email', 'phone'])
}
} }
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -170,7 +170,8 @@ const sendCodeApi = async () => {
// 发送验证码参数 // 发送验证码参数
const sendParams = { const sendParams = {
email: registerForm.value.email, email: registerForm.value.email,
mobile: registerForm.value.phone, mobile: registerForm.value.countryCode + registerForm.value.phone,
mobile_area: registerForm.value.countryCode,
reg_type: activeTab.value === 'email' ? 1 : 2, reg_type: activeTab.value === 'email' ? 1 : 2,
type: 'reg_code', type: 'reg_code',
} }
@ -188,10 +189,13 @@ const handleSendCode = async () => {
// 确定类型、接收方、对应的倒计时状态和发送函数 // 确定类型、接收方、对应的倒计时状态和发送函数
const type: string = activeTab.value === 'phone' ? 'phone' : 'email' const type: string = activeTab.value === 'phone' ? 'phone' : 'email'
const receiver = type === 'phone' ? registerForm.value.phone : registerForm.value.email const receiver =
type === 'phone'
? registerForm.value.countryCode + registerForm.value.phone
: registerForm.value.email
const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState
const sendFunc = type === 'phone' ? 'sendPhoneCaptcha' : 'sendEmailCaptcha' const sendFunc = type === 'phone' ? 'sendPhoneCaptcha' : 'sendEmailCaptcha'
console.log('registerForm.countryCode', registerForm.value.countryCode)
// 调用封装的发送函数(传正确的函数,而非字符串) // 调用封装的发送函数(传正确的函数,而非字符串)
await sendCaptcha( await sendCaptcha(
type, type,
@ -211,7 +215,7 @@ const handleRegister = async () => {
// 注册参数 // 注册参数
const registerParams = { const registerParams = {
email: registerForm.value.email, email: registerForm.value.email,
mobile: registerForm.value.phone, mobile: registerForm.value.countryCode + registerForm.value.phone,
reg_type: activeTab.value === 'email' ? 1 : 2, reg_type: activeTab.value === 'email' ? 1 : 2,
invite_code: registerForm.value.inviteCode, invite_code: registerForm.value.inviteCode,
code: registerForm.value.code, code: registerForm.value.code,