401 lines
10 KiB
Vue
401 lines
10 KiB
Vue
<script setup>
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import request from '@/utils/request'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
|
||
const router = useRouter()
|
||
|
||
// 账号信息(从 JWT 解析)
|
||
const currentAccount = ref('')
|
||
const currentServerId = ref(1)
|
||
|
||
// 表单
|
||
const serverId = ref('')
|
||
const roleId = ref('')
|
||
const roleName = ref('')
|
||
const payType = ref(0) // 0=支付宝 1=微信
|
||
const payAccount = ref('')
|
||
const amount = ref('')
|
||
|
||
// 区服列表
|
||
const servers = ref([])
|
||
|
||
// 游戏配置
|
||
const gameConfig = ref({})
|
||
|
||
const submitting = ref(false)
|
||
|
||
// 计算兑换金额
|
||
const money = computed(() => {
|
||
const ratio = gameConfig.value?.withdrawRatio || 10000
|
||
const a = parseInt(amount.value)
|
||
if (!a || a < ratio) return 0
|
||
return Math.floor(a / ratio)
|
||
})
|
||
|
||
const currencyName = computed(() => gameConfig.value?.currencyName || '货币')
|
||
|
||
/**
|
||
* 解析 JWT payload
|
||
*/
|
||
function parseJwt(token) {
|
||
try {
|
||
const base64Url = token.split('.')[1]
|
||
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
|
||
return JSON.parse(decodeURIComponent(escape(atob(base64))))
|
||
} catch {
|
||
return null
|
||
}
|
||
}
|
||
|
||
async function loadConfig() {
|
||
try {
|
||
const res = await request.get('/api/config')
|
||
if (res?.data) {
|
||
gameConfig.value = {
|
||
...gameConfig.value,
|
||
gameName: res.data.gameName,
|
||
withdrawRatio: res.data.withdrawRatio || 10000,
|
||
withdrawMinOnce: res.data.withdrawMinOnce || 20,
|
||
currencyName: res.data.currencyName || '货币',
|
||
}
|
||
}
|
||
} catch {}
|
||
}
|
||
|
||
async function loadServers() {
|
||
try {
|
||
const res = await request.get('/api/server/list')
|
||
if (res?.code === 0) {
|
||
servers.value = res.serverlist?.[0]?.serverlist || []
|
||
}
|
||
} catch {}
|
||
}
|
||
|
||
async function handleWithdraw() {
|
||
if (!serverId.value) return ElMessage.error('请选择区服')
|
||
if (!roleId.value) return ElMessage.error('请输入角色 ID')
|
||
if (!roleName.value) return ElMessage.error('请输入角色名')
|
||
if (!payAccount.value) return ElMessage.error('请输入收款账户')
|
||
const a = parseInt(amount.value)
|
||
if (!a || a <= 0) return ElMessage.error('请输入提现数量')
|
||
const ratio = gameConfig.value?.withdrawRatio || 10000
|
||
const minOnce = gameConfig.value?.withdrawMinOnce || 20
|
||
if (a < ratio * minOnce) return ElMessage.error(`单次提现数量不能低于 ${ratio * minOnce}`)
|
||
|
||
const payTypeName = payType.value === 0 ? '支付宝' : '微信'
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
`确认提现 ${a} ${currencyName.value},折合人民币 ${money.value} 元?\n收款方式:${payTypeName}(${payAccount.value})`,
|
||
'确认提现',
|
||
{ confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }
|
||
)
|
||
} catch {
|
||
return
|
||
}
|
||
|
||
submitting.value = true
|
||
try {
|
||
const res = await request.post('/api/game/withdraw', {
|
||
server_id: 's' + serverId.value,
|
||
account: currentAccount.value,
|
||
role_id: roleId.value,
|
||
role_name: roleName.value,
|
||
pay_type: payType.value,
|
||
pay_account: payAccount.value,
|
||
amount: a,
|
||
})
|
||
if (res.code === 0) {
|
||
ElMessageBox.alert(res.message, '提现成功', { type: 'success', confirmButtonText: '知道了' })
|
||
amount.value = ''
|
||
} else {
|
||
ElMessage.error(res.message || '提现失败')
|
||
}
|
||
} catch {} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(async () => {
|
||
const token = sessionStorage.getItem('CQ-TOKEN')
|
||
if (!token) {
|
||
router.replace('/login')
|
||
return
|
||
}
|
||
const payload = parseJwt(token)
|
||
if (payload?.username) {
|
||
currentAccount.value = payload.username
|
||
currentServerId.value = payload.server_id || 1
|
||
serverId.value = currentServerId.value
|
||
}
|
||
|
||
await Promise.all([loadConfig(), loadServers()])
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="withdraw-page pagebg">
|
||
<div class="withdraw-box">
|
||
<div class="header">
|
||
<button class="back-btn" @click="router.back()">← 返回</button>
|
||
<h2>游戏提现</h2>
|
||
</div>
|
||
|
||
<div class="account-info">
|
||
当前账号:<strong>{{ currentAccount }}</strong>
|
||
</div>
|
||
|
||
<div class="form">
|
||
<!-- 区服 -->
|
||
<div class="form-item">
|
||
<label>选择区服</label>
|
||
<select v-model="serverId">
|
||
<option value="">请选择区服</option>
|
||
<option v-for="s in servers" :key="s.srvid" :value="s.srvid">
|
||
{{ s.serverName }}
|
||
</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 角色 ID -->
|
||
<div class="form-item">
|
||
<label>角色 ID</label>
|
||
<input
|
||
v-model="roleId"
|
||
type="number"
|
||
placeholder="请输入游戏内角色 ID"
|
||
min="1"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 角色名 -->
|
||
<div class="form-item">
|
||
<label>角色名</label>
|
||
<input
|
||
v-model="roleName"
|
||
type="text"
|
||
placeholder="请输入游戏内角色名"
|
||
maxlength="24"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 提现数量 -->
|
||
<div class="form-item">
|
||
<label>提现数量({{ currencyName }})</label>
|
||
<input
|
||
v-model="amount"
|
||
type="number"
|
||
:placeholder="`最少 ${(gameConfig.withdrawRatio || 10000) * (gameConfig.withdrawMinOnce || 20)} ${currencyName}`"
|
||
min="0"
|
||
/>
|
||
<div v-if="money > 0" class="hint">≈ 人民币 {{ money }} 元</div>
|
||
</div>
|
||
|
||
<!-- 收款方式 -->
|
||
<div class="form-item">
|
||
<label>收款方式</label>
|
||
<div class="pay-type-row">
|
||
<label class="radio-label" :class="{ active: payType === 0 }" @click="payType = 0">
|
||
支付宝
|
||
</label>
|
||
<label class="radio-label" :class="{ active: payType === 1 }" @click="payType = 1">
|
||
微信
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 收款账户 -->
|
||
<div class="form-item">
|
||
<label>{{ payType === 0 ? '支付宝账号' : '微信号' }}</label>
|
||
<input
|
||
v-model="payAccount"
|
||
type="text"
|
||
:placeholder="payType === 0 ? '请输入支付宝账号(手机号/邮箱)' : '请输入微信号'"
|
||
maxlength="30"
|
||
/>
|
||
</div>
|
||
|
||
<div class="tip">
|
||
提示:提现到账时间为工作日,如有疑问请联系客服。
|
||
</div>
|
||
|
||
<button
|
||
class="submit-btn"
|
||
:disabled="submitting"
|
||
@click="handleWithdraw"
|
||
>
|
||
{{ submitting ? '处理中…' : '申请提现' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.withdraw-page {
|
||
min-height: 100vh;
|
||
background: #0a0a0a;
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
|
||
.withdraw-box {
|
||
width: 100%;
|
||
max-width: 480px;
|
||
color: #ddd;
|
||
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||
|
||
h2 {
|
||
color: #f5bd10;
|
||
font-size: 18px;
|
||
margin: 0;
|
||
}
|
||
|
||
.back-btn {
|
||
background: transparent;
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
color: rgba(255, 255, 255, 0.7);
|
||
padding: 6px 14px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
white-space: nowrap;
|
||
|
||
&:hover {
|
||
border-color: #f5bd10;
|
||
color: #f5bd10;
|
||
}
|
||
}
|
||
}
|
||
|
||
.account-info {
|
||
background: rgba(245, 189, 16, 0.08);
|
||
border: 1px solid rgba(245, 189, 16, 0.2);
|
||
border-radius: 8px;
|
||
padding: 10px 16px;
|
||
font-size: 14px;
|
||
margin-bottom: 20px;
|
||
|
||
strong {
|
||
color: #f5bd10;
|
||
}
|
||
}
|
||
|
||
.form {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
|
||
.form-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
|
||
label {
|
||
font-size: 13px;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
input, select {
|
||
height: 38px;
|
||
padding: 0 12px;
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
border-radius: 6px;
|
||
background: rgba(255, 255, 255, 0.06);
|
||
color: #fff;
|
||
font-size: 14px;
|
||
outline: none;
|
||
box-sizing: border-box;
|
||
|
||
&::placeholder {
|
||
color: rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
&:focus {
|
||
border-color: #f5bd10;
|
||
}
|
||
|
||
option {
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.hint {
|
||
font-size: 12px;
|
||
color: #f5bd10;
|
||
}
|
||
|
||
.pay-type-row {
|
||
display: flex;
|
||
gap: 12px;
|
||
|
||
.radio-label {
|
||
flex: 1;
|
||
height: 38px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
transition: all 0.2s;
|
||
|
||
&.active {
|
||
border-color: #f5bd10;
|
||
color: #f5bd10;
|
||
background: rgba(245, 189, 16, 0.08);
|
||
}
|
||
|
||
&:hover:not(.active) {
|
||
border-color: rgba(255, 255, 255, 0.3);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.tip {
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.35);
|
||
padding: 8px 12px;
|
||
background: rgba(255, 255, 255, 0.03);
|
||
border-radius: 6px;
|
||
border-left: 3px solid rgba(245, 189, 16, 0.4);
|
||
}
|
||
|
||
.submit-btn {
|
||
height: 44px;
|
||
border-radius: 8px;
|
||
border: none;
|
||
background: #f5bd10;
|
||
color: #000;
|
||
font-weight: bold;
|
||
font-size: 16px;
|
||
letter-spacing: 4px;
|
||
cursor: pointer;
|
||
transition: opacity 0.2s;
|
||
|
||
&:hover:not(:disabled) {
|
||
opacity: 0.85;
|
||
}
|
||
|
||
&:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|