入金货币,二维码生成
This commit is contained in:
parent
47c2ae7d42
commit
0e195a5434
|
|
@ -28,6 +28,7 @@
|
||||||
"element-plus": "^2.13.2",
|
"element-plus": "^2.13.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"vue": "^3.5.29",
|
"vue": "^3.5.29",
|
||||||
"vue-router": "^5.0.3",
|
"vue-router": "^5.0.3",
|
||||||
"xlsx": "^0.18.5"
|
"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 -->
|
<!-- src/components/CommonTableHeader.vue -->
|
||||||
<template>
|
<template>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h4>{{ title }}</h4>
|
<h4>{{ title }}</h4>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<el-button v-for="(btn, index) in buttons" :key="btn.key || index" :type="btn.type || 'default'"
|
<el-button
|
||||||
:icon="btn.icon" @click="handleButtonClick(btn)" :disabled="btn.disabled">
|
v-for="(btn, index) in buttons"
|
||||||
{{ btn.label }}
|
:key="btn.key || index"
|
||||||
</el-button>
|
:type="btn.type || 'default'"
|
||||||
</div>
|
:icon="btn.icon"
|
||||||
|
@click="handleButtonClick(btn)"
|
||||||
|
:disabled="btn.disabled"
|
||||||
|
>
|
||||||
|
{{ btn.label }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
@ -17,29 +23,47 @@ import type { Component } from 'vue'
|
||||||
|
|
||||||
// 定义按钮接口
|
// 定义按钮接口
|
||||||
interface HeaderButton {
|
interface HeaderButton {
|
||||||
label: string
|
label: string
|
||||||
type?: ButtonProps['type']
|
type?: ButtonProps['type']
|
||||||
icon?: Component
|
icon?: Component
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
key?: string | number // 可选的唯一标识
|
key?: string | number // 可选的唯一标识
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义组件属性接口
|
// 定义组件属性接口
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
buttons: HeaderButton[]
|
buttons: HeaderButton[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
// 定义自定义事件,向外暴露按钮点击事件
|
// 定义自定义事件,向外暴露按钮点击事件
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
// 事件名:button-click,参数为按钮对象
|
// 事件名:button-click,参数为按钮对象
|
||||||
'button-click': [button: HeaderButton]
|
'button-click': [button: HeaderButton]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// 统一处理按钮点击
|
// 统一处理按钮点击
|
||||||
const handleButtonClick = (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>
|
<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>
|
</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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card class="qr-code">
|
<el-card class="qr-code" v-if="userInfo.type * 1 === 2 && userInfo.role_id * 1 === 5">
|
||||||
<h3>
|
<h3>
|
||||||
推广
|
推广
|
||||||
<span>统计起始时间:2026-3-20</span>
|
<span>统计起始时间:2026-3-20</span>
|
||||||
|
|
@ -172,10 +172,7 @@
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<span>推广二维码</span>
|
<span>推广二维码</span>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<img :src="qrCode" alt="推广二维码" />
|
<canvas ref="qrCanvas"></canvas>
|
||||||
<el-icon @click="handleCopyQrCode">
|
|
||||||
<DocumentCopy />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -215,8 +212,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CircleCheckFilled, EditPen, Setting, DocumentCopy } from '@element-plus/icons-vue'
|
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 AgentDialog from '@/components/common/Dialog.vue'
|
||||||
import AgentForm from '@/components/common/Form.vue'
|
import AgentForm from '@/components/common/Form.vue'
|
||||||
|
|
@ -241,6 +236,8 @@ import { useUserStore } from '@/stores/modules/user.ts'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: dialog数据
|
* @description: dialog数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -303,8 +300,8 @@ const inputRef = ref<HTMLInputElement>()
|
||||||
/*
|
/*
|
||||||
* @description: 复制推广二维码和推广链接数据
|
* @description: 复制推广二维码和推广链接数据
|
||||||
*/
|
*/
|
||||||
const copyText = ref('3123123213123123123')
|
const copyText = ref(userInfo.invite_url)
|
||||||
const qrCode = ref(qrCodeImg)
|
const qrCanvas = ref()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 定义公共请求数据方法
|
* @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: 页面加载完成需要请求的数据
|
* @description: 页面加载完成需要请求的数据
|
||||||
*/
|
*/
|
||||||
|
|
@ -329,6 +336,7 @@ onMounted(() => {
|
||||||
getLeverOptions()
|
getLeverOptions()
|
||||||
getAccoundList()
|
getAccoundList()
|
||||||
getWalletCapitaInfo()
|
getWalletCapitaInfo()
|
||||||
|
generateBasic()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -424,36 +432,52 @@ const handleSetting = (id: string) => {
|
||||||
* @description: 复制推广二维码和推广链接方法
|
* @description: 复制推广二维码和推广链接方法
|
||||||
*/
|
*/
|
||||||
const handleCopyUrl = async () => {
|
const handleCopyUrl = async () => {
|
||||||
try {
|
const success = await copyToClipboard(copyText.value)
|
||||||
await navigator.clipboard.writeText(copyText.value)
|
if (success) {
|
||||||
ElMessage.success('复制成功!')
|
alert('复制成功')
|
||||||
} catch (err) {
|
} else {
|
||||||
console.error('复制失败:', err)
|
alert('复制失败,请手动复制')
|
||||||
ElMessage.error('复制失败,请重试')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleCopyQrCode = async () => {
|
|
||||||
try {
|
|
||||||
// 1. 加载图片并转为Blob
|
|
||||||
const response = await fetch(qrCode.value)
|
|
||||||
const blob = await response.blob()
|
|
||||||
|
|
||||||
// 2. 写入剪贴板(仅支持png/jpeg等常见格式)
|
async function copyToClipboard(text: string) {
|
||||||
await navigator.clipboard.write([
|
// 方案1: 优先使用现代 Clipboard API
|
||||||
new ClipboardItem({
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
[blob.type]: blob,
|
try {
|
||||||
}),
|
await navigator.clipboard.writeText(text)
|
||||||
])
|
return true
|
||||||
ElMessage.success('图片复制成功!')
|
} 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) {
|
} catch (err) {
|
||||||
console.error('复制图片失败:', err)
|
document.body.removeChild(textarea)
|
||||||
ElMessage.error('图片复制失败,请检查图片格式或权限')
|
console.error('复制失败:', err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.agent-container {
|
.agent-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
.el-card {
|
.el-card {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
// 组件配置
|
// 组件配置
|
||||||
import MenuHeader from '@/components/common/Header.vue'
|
|
||||||
import MenuTable from '@/components/common/Table.vue'
|
import MenuTable from '@/components/common/Table.vue'
|
||||||
import MenuDialog from '@/components/common/Dialog.vue'
|
import MenuDialog from '@/components/common/Dialog.vue'
|
||||||
import MenuForm from '@/components/common/Form.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 { Plus } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
// 组件配置
|
// 组件配置
|
||||||
import RoleHeader from '@/components/common/Header.vue'
|
|
||||||
import RoleTable from '@/components/common/Table.vue'
|
import RoleTable from '@/components/common/Table.vue'
|
||||||
import RoleDialog from '@/components/common/Dialog.vue'
|
import RoleDialog from '@/components/common/Dialog.vue'
|
||||||
import RoleForm from '@/components/common/Form.vue'
|
import RoleForm from '@/components/common/Form.vue'
|
||||||
|
|
|
||||||
|
|
@ -5,41 +5,93 @@
|
||||||
<UserTabs v-model="activeTab" :tab-list="tabList" />
|
<UserTabs v-model="activeTab" :tab-list="tabList" />
|
||||||
|
|
||||||
<el-form class="user-form" ref="registerFormRef" :model="registerForm" :rules="registerRules">
|
<el-form class="user-form" ref="registerFormRef" :model="registerForm" :rules="registerRules">
|
||||||
|
|
||||||
<Transition name="form-switch" mode="out-in">
|
<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-input v-model="registerForm.email" placeholder="请输入邮箱" clearable size="large" />
|
||||||
<el-button type="primary" class="captcha-btn" @click="handleSendCode" :disabled="emailIsCounting">{{
|
<el-button
|
||||||
emailIsCounting ? `${emailCountdown}s后重新获取` : '发送验证码' }}</el-button>
|
type="primary"
|
||||||
|
class="captcha-btn"
|
||||||
|
@click="handleSendCode"
|
||||||
|
:disabled="emailIsCounting"
|
||||||
|
>{{ emailIsCounting ? `${emailCountdown}s后重新获取` : '发送验证码' }}</el-button
|
||||||
|
>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item class="phone-item captcha-item" prop="phone" v-else-if="activeTab === 'phone'"
|
<el-form-item
|
||||||
key="form-phone-item">
|
class="phone-item captcha-item"
|
||||||
<el-select v-model="registerForm.countryCode" :teleported="false" value-key="code" placeholder="国家"
|
prop="phone"
|
||||||
size="large">
|
v-else-if="activeTab === 'phone'"
|
||||||
<el-option v-for="item in countryOptions" :key="item.code" :label="item.code" :value="item.code">
|
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">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input v-model="registerForm.phone" placeholder="请输入手机号" clearable size="large" />
|
<el-input
|
||||||
<el-button type="primary" class="captcha-btn" @click="handleSendCode" :disabled="phoneIsCounting">{{
|
v-model="registerForm.phone"
|
||||||
phoneIsCounting ? `${phoneCountdown}s后重新获取` : '发送验证码' }}</el-button>
|
placeholder="请输入手机号"
|
||||||
|
clearable
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
class="captcha-btn"
|
||||||
|
@click="handleSendCode"
|
||||||
|
:disabled="phoneIsCounting"
|
||||||
|
>{{ phoneIsCounting ? `${phoneCountdown}s后重新获取` : '发送验证码' }}</el-button
|
||||||
|
>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<UserCaptcha v-model="registerForm.code" />
|
<UserCaptcha v-model="registerForm.code" />
|
||||||
<el-form-item label="设置密码(6-12位密码,包含特殊符号以及大小写)" label-position="top" prop="password">
|
<el-form-item
|
||||||
<el-input placeholder="请输入密码" clearable show-password size="large" type="password"
|
label="设置密码(6-12位密码,包含特殊符号以及大小写)"
|
||||||
v-model="registerForm.password" />
|
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>
|
||||||
<el-form-item label="邀请链接/邀请码(选填)" label-position="top" prop="inviteCode">
|
<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-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>
|
||||||
<el-form-item class="form-submit">
|
<el-form-item class="form-submit">
|
||||||
<el-button type="primary" class="form-btn" @click="handleRegister">注册</el-button>
|
<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 { countryOptions } from '@/views/user/config/mock'
|
||||||
import { passwordRules, emailRules, phoneRules, codeRules } from './config/rules'
|
import { passwordRules, emailRules, phoneRules, codeRules } from './config/rules'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
//store实例
|
//store实例
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
// UserText 实例
|
// UserText 实例
|
||||||
const userText = ref({
|
const userText = ref({
|
||||||
backText: '',
|
backText: '',
|
||||||
title: '注册',
|
title: '注册',
|
||||||
text: '',
|
text: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
//tabs 实例
|
//tabs 实例
|
||||||
const tabList = ref([
|
const tabList = ref([
|
||||||
|
|
@ -92,7 +146,7 @@ const registerForm = ref({
|
||||||
phone: '',
|
phone: '',
|
||||||
password: '',
|
password: '',
|
||||||
code: '',
|
code: '',
|
||||||
inviteCode: ''
|
inviteCode: '',
|
||||||
})
|
})
|
||||||
const registerRules = ref<FormRules>({
|
const registerRules = ref<FormRules>({
|
||||||
email: emailRules().email,
|
email: emailRules().email,
|
||||||
|
|
@ -102,14 +156,13 @@ const registerRules = ref<FormRules>({
|
||||||
})
|
})
|
||||||
|
|
||||||
//初始化验证码倒计时状态
|
//初始化验证码倒计时状态
|
||||||
const emailCaptchaState = initCaptchaState('email'); // 邮箱专属
|
const emailCaptchaState = initCaptchaState('email') // 邮箱专属
|
||||||
const phoneCaptchaState = initCaptchaState('phone'); // 手机专属
|
const phoneCaptchaState = initCaptchaState('phone') // 手机专属
|
||||||
// 邮箱验证码状态
|
// 邮箱验证码状态
|
||||||
const { isCounting: emailIsCounting, countdown: emailCountdown } = emailCaptchaState
|
const { isCounting: emailIsCounting, countdown: emailCountdown } = emailCaptchaState
|
||||||
// 手机验证码状态
|
// 手机验证码状态
|
||||||
const { isCounting: phoneIsCounting, countdown: phoneCountdown } = phoneCaptchaState
|
const { isCounting: phoneIsCounting, countdown: phoneCountdown } = phoneCaptchaState
|
||||||
|
|
||||||
|
|
||||||
//方法
|
//方法
|
||||||
|
|
||||||
// 定义发送验证码接口
|
// 定义发送验证码接口
|
||||||
|
|
@ -119,7 +172,7 @@ const sendCodeApi = async () => {
|
||||||
email: registerForm.value.email,
|
email: registerForm.value.email,
|
||||||
mobile: registerForm.value.phone,
|
mobile: registerForm.value.phone,
|
||||||
reg_type: activeTab.value === 'email' ? 1 : 2,
|
reg_type: activeTab.value === 'email' ? 1 : 2,
|
||||||
type:'resetPass_code'
|
type: 'reg_code',
|
||||||
}
|
}
|
||||||
return userStore.sendCode(sendParams)
|
return userStore.sendCode(sendParams)
|
||||||
}
|
}
|
||||||
|
|
@ -134,26 +187,26 @@ 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.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'
|
||||||
|
|
||||||
// 调用封装的发送函数(传正确的函数,而非字符串)
|
// 调用封装的发送函数(传正确的函数,而非字符串)
|
||||||
await sendCaptcha(
|
await sendCaptcha(
|
||||||
type,
|
type,
|
||||||
receiver,
|
receiver,
|
||||||
currentCaptchaState, // 只更新当前类型的倒计时状态
|
currentCaptchaState, // 只更新当前类型的倒计时状态
|
||||||
() => sendCodeApi() // 传入返回Promise的函数
|
() => sendCodeApi(), // 传入返回Promise的函数
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//注册
|
//注册
|
||||||
const handleRegister = async () => {
|
const handleRegister = async () => {
|
||||||
if (!registerFormRef.value) return;
|
if (!registerFormRef.value) return
|
||||||
try {
|
try {
|
||||||
// 提交时再次验证(显示错误提示)
|
// 提交时再次验证(显示错误提示)
|
||||||
await registerFormRef.value.validate();
|
await registerFormRef.value.validate()
|
||||||
|
|
||||||
// 注册参数
|
// 注册参数
|
||||||
const registerParams = {
|
const registerParams = {
|
||||||
|
|
@ -162,25 +215,28 @@ const handleRegister = async () => {
|
||||||
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,
|
||||||
password: registerForm.value.password
|
password: registerForm.value.password,
|
||||||
};
|
}
|
||||||
|
|
||||||
// 注册逻辑
|
// 注册逻辑
|
||||||
await userStore.register(registerParams);
|
await userStore.register(registerParams)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('表单校验失败:', error);
|
console.error('表单校验失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 组件卸载时清除定时器(关键:防止内存泄漏)
|
// 组件卸载时清除定时器(关键:防止内存泄漏)
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearCaptchaTimer(emailCaptchaState);
|
clearCaptchaTimer(emailCaptchaState)
|
||||||
clearCaptchaTimer(phoneCaptchaState);
|
clearCaptchaTimer(phoneCaptchaState)
|
||||||
});
|
})
|
||||||
|
|
||||||
|
onMounted(()=>{
|
||||||
|
registerForm.value.inviteCode = route.query.invite_code
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.user-form {
|
.user-form {
|
||||||
|
|
||||||
.phone-item,
|
.phone-item,
|
||||||
.captcha-item {
|
.captcha-item {
|
||||||
:deep(.el-form-item__content) {
|
:deep(.el-form-item__content) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue