入金货币,二维码生成
This commit is contained in:
parent
47c2ae7d42
commit
0e195a5434
|
|
@ -28,6 +28,7 @@
|
|||
"element-plus": "^2.13.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"pinia": "^3.0.4",
|
||||
"qrcode": "^1.5.4",
|
||||
"vue": "^3.5.29",
|
||||
"vue-router": "^5.0.3",
|
||||
"xlsx": "^0.18.5"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
import { request } from '@/api'
|
||||
|
||||
// 获取入金货币列表
|
||||
export const getCurrencyList = () => {
|
||||
return request.get('/currency/getCurrencyList')
|
||||
}
|
||||
// 创建入金货币
|
||||
export const createCurrency = (params: any) => {
|
||||
return request.post('/currency/createCurrency', { ...params })
|
||||
}
|
||||
// 编辑入金货币
|
||||
export const editCurrency = (params: any) => {
|
||||
return request.post('/currency/editCurrency', { ...params })
|
||||
}
|
||||
// 删除入金货币
|
||||
export const delCurrency = (params: any) => {
|
||||
return request.post('/currency/delCurrency', { ...params })
|
||||
}
|
||||
|
|
@ -1,14 +1,20 @@
|
|||
<!-- src/components/CommonTableHeader.vue -->
|
||||
<template>
|
||||
<div class="header">
|
||||
<h4>{{ title }}</h4>
|
||||
<div class="header-actions">
|
||||
<el-button v-for="(btn, index) in buttons" :key="btn.key || index" :type="btn.type || 'default'"
|
||||
:icon="btn.icon" @click="handleButtonClick(btn)" :disabled="btn.disabled">
|
||||
{{ btn.label }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="header">
|
||||
<h4>{{ title }}</h4>
|
||||
<div class="header-actions">
|
||||
<el-button
|
||||
v-for="(btn, index) in buttons"
|
||||
:key="btn.key || index"
|
||||
:type="btn.type || 'default'"
|
||||
:icon="btn.icon"
|
||||
@click="handleButtonClick(btn)"
|
||||
:disabled="btn.disabled"
|
||||
>
|
||||
{{ btn.label }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
@ -17,29 +23,47 @@ import type { Component } from 'vue'
|
|||
|
||||
// 定义按钮接口
|
||||
interface HeaderButton {
|
||||
label: string
|
||||
type?: ButtonProps['type']
|
||||
icon?: Component
|
||||
disabled?: boolean
|
||||
key?: string | number // 可选的唯一标识
|
||||
label: string
|
||||
type?: ButtonProps['type']
|
||||
icon?: Component
|
||||
disabled?: boolean
|
||||
key?: string | number // 可选的唯一标识
|
||||
}
|
||||
|
||||
// 定义组件属性接口
|
||||
interface Props {
|
||||
title: string
|
||||
buttons: HeaderButton[]
|
||||
title: string
|
||||
buttons: HeaderButton[]
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
// 定义自定义事件,向外暴露按钮点击事件
|
||||
const emit = defineEmits<{
|
||||
// 事件名:button-click,参数为按钮对象
|
||||
'button-click': [button: HeaderButton]
|
||||
// 事件名:button-click,参数为按钮对象
|
||||
'button-click': [button: HeaderButton]
|
||||
}>()
|
||||
|
||||
// 统一处理按钮点击
|
||||
const handleButtonClick = (button: HeaderButton) => {
|
||||
// 触发自定义事件,把点击的按钮对象传出去
|
||||
emit('button-click', button)
|
||||
// 触发自定义事件,把点击的按钮对象传出去
|
||||
emit('button-click', button)
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
h4 {
|
||||
margin: 0 40px 0 0;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,162 @@
|
|||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<h1>入金货币</h1>
|
||||
<!-- 搜索 -->
|
||||
<FundDetailHeader title="入金货币" :buttons="buttonsOptions" @button-click="handleButtonClick">
|
||||
</FundDetailHeader>
|
||||
<!-- 表格 -->
|
||||
<FundDetailTable
|
||||
:table-data="tableData"
|
||||
:columns="tableColumnsOptions"
|
||||
@row-click="handleRowClick"
|
||||
row-key="id"
|
||||
/>
|
||||
<FundDetailDialog
|
||||
:visible="visible"
|
||||
:title="dialogTitle"
|
||||
@close="visible = false"
|
||||
@cancel="visible = false"
|
||||
@confirm="handleConfirm"
|
||||
>
|
||||
<FundDetailForm
|
||||
:formData="formData"
|
||||
:formItems="formItems"
|
||||
:formRules="formRules"
|
||||
:optionsMap="optionsMap"
|
||||
></FundDetailForm>
|
||||
</FundDetailDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FundDetailHeader from '@/components/common/header.vue'
|
||||
import FundDetailTable from '@/components/common/Table.vue'
|
||||
import FundDetailDialog from '@/components/common/Dialog.vue'
|
||||
import FundDetailForm from '@/components/common/Form.vue'
|
||||
import type { ButtonProps } from 'element-plus'
|
||||
import type { Component } from 'vue'
|
||||
import { buttons, tableColumns, form, rules } from './options'
|
||||
import {
|
||||
getCurrencyList,
|
||||
createCurrency,
|
||||
delCurrency,
|
||||
editCurrency,
|
||||
} from '@/api/modules/capital/currency.ts'
|
||||
|
||||
// 定义按钮接口
|
||||
interface HeaderButton {
|
||||
label: string
|
||||
type?: ButtonProps['type']
|
||||
icon?: Component
|
||||
disabled?: boolean
|
||||
key?: string | number // 可选的唯一标识
|
||||
}
|
||||
|
||||
const buttonsOptions = ref(buttons)
|
||||
const formItems = ref(form)
|
||||
const tableData = ref([])
|
||||
const tableColumnsOptions = ref(tableColumns)
|
||||
const dialogTitle = ref('')
|
||||
const formRules = ref(rules)
|
||||
const formData = ref({
|
||||
name: '',
|
||||
symbol: '',
|
||||
rate: null,
|
||||
type: 1,
|
||||
status: 1,
|
||||
})
|
||||
const optionsMap = {
|
||||
type: [
|
||||
{ label: '法币', value: 1 },
|
||||
{ label: '数字货币', value: 2 },
|
||||
],
|
||||
status: [
|
||||
{ label: '正常', value: 1 },
|
||||
{ label: '禁用', value: 2 },
|
||||
],
|
||||
}
|
||||
|
||||
let rowData: any = {}
|
||||
|
||||
const visible = ref<boolean>(false)
|
||||
|
||||
const getTableList = async () => {
|
||||
const data = await getCurrencyList()
|
||||
tableData.value = (data || []).map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
typeStr: ['', '法币', '数字货币'][item.type],
|
||||
statusStr: ['', '正常', '禁用'][item.status],
|
||||
}
|
||||
})
|
||||
console.log('tableData.value', tableData.value)
|
||||
}
|
||||
|
||||
const handleRowClick = (row: any) => {
|
||||
console.log(row)
|
||||
rowData = row
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
name: '',
|
||||
symbol: '',
|
||||
rate: null,
|
||||
type: 1,
|
||||
status: 1,
|
||||
}
|
||||
}
|
||||
|
||||
const handleButtonClick = (button: HeaderButton) => {
|
||||
console.log(button)
|
||||
switch (button.key) {
|
||||
case 'add':
|
||||
handleAdd()
|
||||
break
|
||||
|
||||
case 'edit':
|
||||
if (!rowData.id) return ElMessage.warning('请选择数据')
|
||||
handleEdit()
|
||||
break
|
||||
|
||||
case 'delete':
|
||||
ElMessageBox.confirm('你确定要删除此数据').then(async () => {
|
||||
await delCurrency({ id: rowData.id })
|
||||
ElMessage.success('删除成功')
|
||||
await getTableList()
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const handleAdd = () => {
|
||||
dialogTitle.value = '添加'
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = () => {
|
||||
dialogTitle.value = '编辑'
|
||||
visible.value = true
|
||||
formData.value = {
|
||||
name: rowData.name,
|
||||
symbol: rowData.symbol,
|
||||
rate: rowData.rate,
|
||||
type: rowData.type,
|
||||
status: rowData.status,
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (dialogTitle.value === '添加') {
|
||||
await createCurrency(formData.value)
|
||||
} else {
|
||||
await editCurrency({ ...formData.value, id: rowData.id })
|
||||
}
|
||||
visible.value = false
|
||||
resetForm()
|
||||
await getTableList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @description: 搜索配置
|
||||
*/
|
||||
export const buttons = [
|
||||
{ label: '添加', type: 'primary', key: 'add' },
|
||||
{ label: '编辑', type: 'primary', key: 'edit' },
|
||||
{ label: '删除', type: 'danger', key: 'delete' },
|
||||
]
|
||||
export const tableColumns = [
|
||||
{ prop: 'name', label: '币名' },
|
||||
{ prop: 'symbol', label: '货币符号' },
|
||||
{ prop: 'rate', label: '汇率' },
|
||||
{ prop: 'typeStr', label: '货币类型' },
|
||||
{ prop: 'statusStr', label: '状态' },
|
||||
{ prop: 'created_at', label: '创建时间' },
|
||||
]
|
||||
|
||||
export const form = [
|
||||
{ prop: 'name', label: '币名', type: 'input', placeholder: '请输入币名' },
|
||||
{ prop: 'symbol', label: '货币符号', type: 'input', placeholder: '请输入货币符号' },
|
||||
{ prop: 'rate', label: '汇率', type: 'number', placeholder: '请输入汇率' },
|
||||
{ prop: 'type', label: '货币类型', type: 'radio', placeholder: '请输入货币类型' },
|
||||
{ prop: 'status', label: '状态', type: 'radio', placeholder: '请输入状态' },
|
||||
]
|
||||
|
||||
export const rules = {
|
||||
name: [{ required: true, message: '请输入币名', trigger: ['change'] }],
|
||||
symbol: [{ required: true, message: '请输入货币符号', trigger: ['change'] }],
|
||||
rate: [{ required: true, message: '请输入汇率', trigger: ['change'] }],
|
||||
type: [{ required: true, message: '请输入货币类型', trigger: ['change'] }],
|
||||
status: [{ required: true, message: '请输入状态', trigger: ['change'] }],
|
||||
}
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="qr-code">
|
||||
<el-card class="qr-code" v-if="userInfo.type * 1 === 2 && userInfo.role_id * 1 === 5">
|
||||
<h3>
|
||||
推广
|
||||
<span>统计起始时间:2026-3-20</span>
|
||||
|
|
@ -172,10 +172,7 @@
|
|||
<div class="item">
|
||||
<span>推广二维码</span>
|
||||
<div class="text">
|
||||
<img :src="qrCode" alt="推广二维码" />
|
||||
<el-icon @click="handleCopyQrCode">
|
||||
<DocumentCopy />
|
||||
</el-icon>
|
||||
<canvas ref="qrCanvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -215,8 +212,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { CircleCheckFilled, EditPen, Setting, DocumentCopy } from '@element-plus/icons-vue'
|
||||
import qrCodeImg from '@/assets/images/qrCode.png'
|
||||
|
||||
//组件
|
||||
import AgentDialog from '@/components/common/Dialog.vue'
|
||||
import AgentForm from '@/components/common/Form.vue'
|
||||
|
|
@ -241,6 +236,8 @@ import { useUserStore } from '@/stores/modules/user.ts'
|
|||
|
||||
const userStore = useUserStore()
|
||||
|
||||
import QRCode from 'qrcode'
|
||||
|
||||
/**
|
||||
* @description: dialog数据
|
||||
*/
|
||||
|
|
@ -303,8 +300,8 @@ const inputRef = ref<HTMLInputElement>()
|
|||
/*
|
||||
* @description: 复制推广二维码和推广链接数据
|
||||
*/
|
||||
const copyText = ref('3123123213123123123')
|
||||
const qrCode = ref(qrCodeImg)
|
||||
const copyText = ref(userInfo.invite_url)
|
||||
const qrCanvas = ref()
|
||||
|
||||
/**
|
||||
* @description: 定义公共请求数据方法
|
||||
|
|
@ -322,6 +319,16 @@ const getLeverOptions = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const generateBasic = async () => {
|
||||
const userInfo = getStorage('userInfo')
|
||||
if (!userInfo?.invite_url || !qrCanvas.value) return
|
||||
|
||||
await QRCode.toCanvas(qrCanvas.value, userInfo.invite_url, {
|
||||
width: 200,
|
||||
margin: 2,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 页面加载完成需要请求的数据
|
||||
*/
|
||||
|
|
@ -329,6 +336,7 @@ onMounted(() => {
|
|||
getLeverOptions()
|
||||
getAccoundList()
|
||||
getWalletCapitaInfo()
|
||||
generateBasic()
|
||||
})
|
||||
|
||||
/**
|
||||
|
|
@ -424,36 +432,52 @@ const handleSetting = (id: string) => {
|
|||
* @description: 复制推广二维码和推广链接方法
|
||||
*/
|
||||
const handleCopyUrl = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(copyText.value)
|
||||
ElMessage.success('复制成功!')
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err)
|
||||
ElMessage.error('复制失败,请重试')
|
||||
const success = await copyToClipboard(copyText.value)
|
||||
if (success) {
|
||||
alert('复制成功')
|
||||
} else {
|
||||
alert('复制失败,请手动复制')
|
||||
}
|
||||
}
|
||||
const handleCopyQrCode = async () => {
|
||||
try {
|
||||
// 1. 加载图片并转为Blob
|
||||
const response = await fetch(qrCode.value)
|
||||
const blob = await response.blob()
|
||||
|
||||
// 2. 写入剪贴板(仅支持png/jpeg等常见格式)
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob,
|
||||
}),
|
||||
])
|
||||
ElMessage.success('图片复制成功!')
|
||||
async function copyToClipboard(text: string) {
|
||||
// 方案1: 优先使用现代 Clipboard API
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
return true
|
||||
} catch (err) {
|
||||
console.warn('Clipboard API 失败,尝试降级方案', err)
|
||||
}
|
||||
}
|
||||
|
||||
// 方案2: 降级到 document.execCommand(兼容 HTTP 环境)
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.value = text
|
||||
textarea.style.position = 'fixed'
|
||||
textarea.style.left = '-9999px'
|
||||
textarea.style.top = '0'
|
||||
document.body.appendChild(textarea)
|
||||
textarea.focus()
|
||||
textarea.select()
|
||||
|
||||
try {
|
||||
const success = document.execCommand('copy')
|
||||
document.body.removeChild(textarea)
|
||||
return success
|
||||
} catch (err) {
|
||||
console.error('复制图片失败:', err)
|
||||
ElMessage.error('图片复制失败,请检查图片格式或权限')
|
||||
document.body.removeChild(textarea)
|
||||
console.error('复制失败:', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.agent-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
.el-card {
|
||||
margin-bottom: 20px;
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@
|
|||
import type { FormInstance } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
// 组件配置
|
||||
import MenuHeader from '@/components/common/Header.vue'
|
||||
import MenuTable from '@/components/common/Table.vue'
|
||||
import MenuDialog from '@/components/common/Dialog.vue'
|
||||
import MenuForm from '@/components/common/Form.vue'
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ import type { FormInstance } from 'element-plus'
|
|||
import { Plus } from '@element-plus/icons-vue'
|
||||
|
||||
// 组件配置
|
||||
import RoleHeader from '@/components/common/Header.vue'
|
||||
import RoleTable from '@/components/common/Table.vue'
|
||||
import RoleDialog from '@/components/common/Dialog.vue'
|
||||
import RoleForm from '@/components/common/Form.vue'
|
||||
|
|
|
|||
|
|
@ -5,41 +5,93 @@
|
|||
<UserTabs v-model="activeTab" :tab-list="tabList" />
|
||||
|
||||
<el-form class="user-form" ref="registerFormRef" :model="registerForm" :rules="registerRules">
|
||||
|
||||
<Transition name="form-switch" mode="out-in">
|
||||
<el-form-item class="captcha-item" v-if="activeTab === 'email'" key="form-email-item" prop="email">
|
||||
<el-form-item
|
||||
class="captcha-item"
|
||||
v-if="activeTab === 'email'"
|
||||
key="form-email-item"
|
||||
prop="email"
|
||||
>
|
||||
<el-input v-model="registerForm.email" placeholder="请输入邮箱" clearable size="large" />
|
||||
<el-button type="primary" class="captcha-btn" @click="handleSendCode" :disabled="emailIsCounting">{{
|
||||
emailIsCounting ? `${emailCountdown}s后重新获取` : '发送验证码' }}</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="captcha-btn"
|
||||
@click="handleSendCode"
|
||||
:disabled="emailIsCounting"
|
||||
>{{ emailIsCounting ? `${emailCountdown}s后重新获取` : '发送验证码' }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item class="phone-item captcha-item" prop="phone" v-else-if="activeTab === 'phone'"
|
||||
key="form-phone-item">
|
||||
<el-select v-model="registerForm.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">
|
||||
<el-form-item
|
||||
class="phone-item captcha-item"
|
||||
prop="phone"
|
||||
v-else-if="activeTab === 'phone'"
|
||||
key="form-phone-item"
|
||||
>
|
||||
<el-select
|
||||
v-model="registerForm.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">
|
||||
<span class="country-name">{{ item.name }}</span>
|
||||
<span class="country-code">{{ item.code }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-input v-model="registerForm.phone" placeholder="请输入手机号" clearable size="large" />
|
||||
<el-button type="primary" class="captcha-btn" @click="handleSendCode" :disabled="phoneIsCounting">{{
|
||||
phoneIsCounting ? `${phoneCountdown}s后重新获取` : '发送验证码' }}</el-button>
|
||||
<el-input
|
||||
v-model="registerForm.phone"
|
||||
placeholder="请输入手机号"
|
||||
clearable
|
||||
size="large"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="captcha-btn"
|
||||
@click="handleSendCode"
|
||||
:disabled="phoneIsCounting"
|
||||
>{{ phoneIsCounting ? `${phoneCountdown}s后重新获取` : '发送验证码' }}</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</Transition>
|
||||
|
||||
<UserCaptcha v-model="registerForm.code" />
|
||||
<el-form-item label="设置密码(6-12位密码,包含特殊符号以及大小写)" label-position="top" prop="password">
|
||||
<el-input placeholder="请输入密码" clearable show-password size="large" type="password"
|
||||
v-model="registerForm.password" />
|
||||
<el-form-item
|
||||
label="设置密码(6-12位密码,包含特殊符号以及大小写)"
|
||||
label-position="top"
|
||||
prop="password"
|
||||
>
|
||||
<el-input
|
||||
placeholder="请输入密码"
|
||||
clearable
|
||||
show-password
|
||||
size="large"
|
||||
type="password"
|
||||
v-model="registerForm.password"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="邀请链接/邀请码(选填)" label-position="top" prop="inviteCode">
|
||||
<el-input placeholder="请输入邀请链接/邀请码" clearable size="large" type="text" v-model="registerForm.inviteCode" />
|
||||
<el-input
|
||||
placeholder="请输入邀请链接/邀请码"
|
||||
clearable
|
||||
size="large"
|
||||
type="text"
|
||||
v-model="registerForm.inviteCode"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox label="同意注册协议">我已阅读并同意 隐私条例、 退款政策、 AML&CTF政策、<br> 执行政策 和 网站条款和条件</el-checkbox>
|
||||
<el-checkbox label="同意注册协议"
|
||||
>我已阅读并同意 隐私条例、 退款政策、 AML&CTF政策、<br />
|
||||
执行政策 和 网站条款和条件</el-checkbox
|
||||
>
|
||||
</el-form-item>
|
||||
<el-form-item class="form-submit">
|
||||
<el-button type="primary" class="form-btn" @click="handleRegister">注册</el-button>
|
||||
|
|
@ -66,16 +118,18 @@ import { sendCaptcha, initCaptchaState, clearCaptchaTimer } from '@/utils/sendCa
|
|||
//用户校验规则
|
||||
import { countryOptions } from '@/views/user/config/mock'
|
||||
import { passwordRules, emailRules, phoneRules, codeRules } from './config/rules'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
//store实例
|
||||
const userStore = useUserStore()
|
||||
const route = useRoute()
|
||||
|
||||
// UserText 实例
|
||||
const userText = ref({
|
||||
backText: '',
|
||||
title: '注册',
|
||||
text: '',
|
||||
});
|
||||
})
|
||||
|
||||
//tabs 实例
|
||||
const tabList = ref([
|
||||
|
|
@ -92,7 +146,7 @@ const registerForm = ref({
|
|||
phone: '',
|
||||
password: '',
|
||||
code: '',
|
||||
inviteCode: ''
|
||||
inviteCode: '',
|
||||
})
|
||||
const registerRules = ref<FormRules>({
|
||||
email: emailRules().email,
|
||||
|
|
@ -102,14 +156,13 @@ const registerRules = ref<FormRules>({
|
|||
})
|
||||
|
||||
//初始化验证码倒计时状态
|
||||
const emailCaptchaState = initCaptchaState('email'); // 邮箱专属
|
||||
const phoneCaptchaState = initCaptchaState('phone'); // 手机专属
|
||||
const emailCaptchaState = initCaptchaState('email') // 邮箱专属
|
||||
const phoneCaptchaState = initCaptchaState('phone') // 手机专属
|
||||
// 邮箱验证码状态
|
||||
const { isCounting: emailIsCounting, countdown: emailCountdown } = emailCaptchaState
|
||||
// 手机验证码状态
|
||||
const { isCounting: phoneIsCounting, countdown: phoneCountdown } = phoneCaptchaState
|
||||
|
||||
|
||||
//方法
|
||||
|
||||
// 定义发送验证码接口
|
||||
|
|
@ -119,7 +172,7 @@ const sendCodeApi = async () => {
|
|||
email: registerForm.value.email,
|
||||
mobile: registerForm.value.phone,
|
||||
reg_type: activeTab.value === 'email' ? 1 : 2,
|
||||
type:'resetPass_code'
|
||||
type: 'reg_code',
|
||||
}
|
||||
return userStore.sendCode(sendParams)
|
||||
}
|
||||
|
|
@ -134,26 +187,26 @@ const handleSendCode = async () => {
|
|||
}
|
||||
|
||||
// 确定类型、接收方、对应的倒计时状态和发送函数
|
||||
const type: string = activeTab.value === 'phone' ? 'phone' : 'email';
|
||||
const receiver = type === 'phone' ? registerForm.value.phone : registerForm.value.email;
|
||||
const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState;
|
||||
const sendFunc = type === 'phone' ? "sendPhoneCaptcha" : "sendEmailCaptcha";
|
||||
const type: string = activeTab.value === 'phone' ? 'phone' : 'email'
|
||||
const receiver = type === 'phone' ? registerForm.value.phone : registerForm.value.email
|
||||
const currentCaptchaState = type === 'phone' ? phoneCaptchaState : emailCaptchaState
|
||||
const sendFunc = type === 'phone' ? 'sendPhoneCaptcha' : 'sendEmailCaptcha'
|
||||
|
||||
// 调用封装的发送函数(传正确的函数,而非字符串)
|
||||
await sendCaptcha(
|
||||
type,
|
||||
receiver,
|
||||
currentCaptchaState, // 只更新当前类型的倒计时状态
|
||||
() => sendCodeApi() // 传入返回Promise的函数
|
||||
);
|
||||
() => sendCodeApi(), // 传入返回Promise的函数
|
||||
)
|
||||
}
|
||||
|
||||
//注册
|
||||
const handleRegister = async () => {
|
||||
if (!registerFormRef.value) return;
|
||||
if (!registerFormRef.value) return
|
||||
try {
|
||||
// 提交时再次验证(显示错误提示)
|
||||
await registerFormRef.value.validate();
|
||||
await registerFormRef.value.validate()
|
||||
|
||||
// 注册参数
|
||||
const registerParams = {
|
||||
|
|
@ -162,25 +215,28 @@ const handleRegister = async () => {
|
|||
reg_type: activeTab.value === 'email' ? 1 : 2,
|
||||
invite_code: registerForm.value.inviteCode,
|
||||
code: registerForm.value.code,
|
||||
password: registerForm.value.password
|
||||
};
|
||||
password: registerForm.value.password,
|
||||
}
|
||||
|
||||
// 注册逻辑
|
||||
await userStore.register(registerParams);
|
||||
await userStore.register(registerParams)
|
||||
} catch (error) {
|
||||
console.error('表单校验失败:', error);
|
||||
console.error('表单校验失败:', error)
|
||||
}
|
||||
}
|
||||
// 组件卸载时清除定时器(关键:防止内存泄漏)
|
||||
onUnmounted(() => {
|
||||
clearCaptchaTimer(emailCaptchaState);
|
||||
clearCaptchaTimer(phoneCaptchaState);
|
||||
});
|
||||
clearCaptchaTimer(emailCaptchaState)
|
||||
clearCaptchaTimer(phoneCaptchaState)
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
registerForm.value.inviteCode = route.query.invite_code
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.user-form {
|
||||
|
||||
.phone-item,
|
||||
.captcha-item {
|
||||
:deep(.el-form-item__content) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue