feat(提现申请功能): 对接接口完成
This commit is contained in:
parent
bb04c3c6db
commit
0aeea9dd20
14
src/api/upload.js
Normal file
14
src/api/upload.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { get, post } from '../utils/http.js'
|
||||
|
||||
// 上传图片
|
||||
export const uploadImg = async (file) => {
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file) // 这里的'file'需要与后端接口期望的参数名一致
|
||||
const response = await post('/external/oss/upload', formData)
|
||||
return response
|
||||
} catch (error) {
|
||||
console.error('Failed to upload img:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
39
src/utils/applyStore.js
Normal file
39
src/utils/applyStore.js
Normal file
@ -0,0 +1,39 @@
|
||||
let applyInfo = null
|
||||
|
||||
/**
|
||||
* 设置申请资料缓存
|
||||
* @param {Object} applyInfo - 申请资料
|
||||
*/
|
||||
export function setApplyInfo(data) {
|
||||
applyInfo = data
|
||||
|
||||
// 可选:存储到localStorage以便持久化
|
||||
if (data) {
|
||||
localStorage.setItem('applyInfo', JSON.stringify(data))
|
||||
} else {
|
||||
localStorage.removeItem('applyInfo')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取申请资料
|
||||
* @returns {Object|null}
|
||||
*/
|
||||
export function getApplyInfo() {
|
||||
if (applyInfo) {
|
||||
return applyInfo
|
||||
}
|
||||
|
||||
// 从localStorage恢复
|
||||
const stored = localStorage.getItem('applyInfo')
|
||||
if (stored) {
|
||||
try {
|
||||
applyInfo = JSON.parse(stored)
|
||||
return applyInfo
|
||||
} catch (error) {
|
||||
console.error('解析申请资料缓存失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
// HTTP请求配置
|
||||
import {getApiBaseUrl, getCurrentEnv, getUserAuth, isDebugMode} from './env.js'
|
||||
import { getApiBaseUrl, getCurrentEnv, getUserAuth, isDebugMode } from './env.js'
|
||||
|
||||
/**
|
||||
* 自定义API错误类
|
||||
@ -8,14 +8,14 @@ class ApiError extends Error {
|
||||
constructor(message, options = {}) {
|
||||
super(message)
|
||||
this.name = 'ApiError'
|
||||
this.status = options.status || null // HTTP状态码
|
||||
this.errorCode = options.errorCode || null // 业务错误码 (如: 6010)
|
||||
this.status = options.status || null // HTTP状态码
|
||||
this.errorCode = options.errorCode || null // 业务错误码 (如: 6010)
|
||||
this.errorCodeName = options.errorCodeName || null // 错误码名称 (如: "TEAM_NOT_FOUND")
|
||||
this.errorMsg = options.errorMsg || null // 业务错误消息
|
||||
this.time = options.time || null // API响应时间戳
|
||||
this.extValues = options.extValues || null // 扩展值
|
||||
this.response = options.response || null // 原始响应数据
|
||||
this.type = options.type || 'unknown' // 错误类型: 'http' | 'business' | 'network'
|
||||
this.errorMsg = options.errorMsg || null // 业务错误消息
|
||||
this.time = options.time || null // API响应时间戳
|
||||
this.extValues = options.extValues || null // 扩展值
|
||||
this.response = options.response || null // 原始响应数据
|
||||
this.type = options.type || 'unknown' // 错误类型: 'http' | 'business' | 'network'
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ if (DEBUG_MODE) {
|
||||
const token = getUserAuth()
|
||||
// 默认公共请求头配置
|
||||
let COMMON_HEADERS = {
|
||||
'authorization': `Bearer ${token}`,
|
||||
authorization: `Bearer ${token}`,
|
||||
'req-app-intel': 'version=1.0.0;build=1;model=SM-G9550;sysVersion=9;channel=Google',
|
||||
'req-client': 'Android',
|
||||
'req-imei': '8fa54d728ab449e04f9292329ed44bda',
|
||||
@ -49,7 +49,7 @@ let COMMON_HEADERS = {
|
||||
export function updateCommonHeaders(headers) {
|
||||
COMMON_HEADERS = {
|
||||
...COMMON_HEADERS,
|
||||
...headers
|
||||
...headers,
|
||||
}
|
||||
console.debug('🔄 Updated common headers:', COMMON_HEADERS)
|
||||
}
|
||||
@ -110,37 +110,40 @@ export { COMMON_HEADERS, ApiError }
|
||||
* @returns {Promise} - 返回Promise对象
|
||||
*/
|
||||
export async function request(url, options = {}) {
|
||||
const {
|
||||
method = 'GET',
|
||||
data = null,
|
||||
headers = {},
|
||||
...otherOptions
|
||||
} = options
|
||||
|
||||
const { method = 'GET', data = null, headers = {}, ...otherOptions } = options
|
||||
const fullUrl = url.startsWith('http') ? url : `${API_BASE_URL}${url}`
|
||||
|
||||
const config = {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...COMMON_HEADERS,
|
||||
...headers
|
||||
...headers,
|
||||
},
|
||||
...otherOptions
|
||||
...otherOptions,
|
||||
}
|
||||
|
||||
// 如果是POST、PUT等方法且有数据,添加body
|
||||
if (data && ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {
|
||||
config.body = JSON.stringify(data)
|
||||
if (data instanceof FormData) {
|
||||
// FormData类型:由浏览器自动设置Content-Type
|
||||
config.body = data
|
||||
// 删除手动设置的Content-Type(重要!)
|
||||
if (config.headers['Content-Type']) {
|
||||
delete config.headers['Content-Type']
|
||||
}
|
||||
} else {
|
||||
// 非FormData:按JSON处理
|
||||
config.body = JSON.stringify(data)
|
||||
config.headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const response = await fetch(fullUrl, config)
|
||||
|
||||
if (!response.ok) {
|
||||
// 尝试解析响应体获取更多错误信息
|
||||
let errorData = await response.json()
|
||||
let errorData = await response.json()
|
||||
|
||||
throw new ApiError(`HTTP Error: ${response.status} ${response.statusText}`, {
|
||||
type: 'http',
|
||||
@ -149,7 +152,7 @@ export async function request(url, options = {}) {
|
||||
errorCode: errorData.errorCode,
|
||||
errorCodeName: errorData.errorCodeName,
|
||||
errorMsg: errorData.errorMsg,
|
||||
time: errorData.time
|
||||
time: errorData.time,
|
||||
})
|
||||
}
|
||||
|
||||
@ -180,7 +183,7 @@ export async function request(url, options = {}) {
|
||||
throw new ApiError(error.message || 'Network or unknown error', {
|
||||
type: 'network',
|
||||
originalError: error,
|
||||
code: 'NETWORK_ERROR'
|
||||
code: 'NETWORK_ERROR',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1399,15 +1399,19 @@ const getWinners = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
//获取中奖记录
|
||||
//获取领取任务奖励
|
||||
const receiveTaskReward = async (code) => {
|
||||
let data = {
|
||||
taskCode: code,
|
||||
}
|
||||
const resReceive = await receiveTickets(data)
|
||||
if (resReceive.status && resReceive.body) {
|
||||
getTaskList() //获取任务列表
|
||||
getTickets() //获取拥有的抽奖券
|
||||
try {
|
||||
const resReceive = await receiveTickets(data)
|
||||
if (resReceive.status && resReceive.body) {
|
||||
getTaskList() //获取任务列表
|
||||
getTickets() //获取拥有的抽奖券
|
||||
}
|
||||
} catch (error) {
|
||||
showError(error.errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +70,9 @@
|
||||
<div v-else style="width: 100%; display: flex; flex-direction: column; gap: 8px">
|
||||
<!-- 图片 -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center">
|
||||
<span style="font-weight: 510; font-size: 1em">Passport/ID card:(3/3)</span>
|
||||
<span style="font-weight: 510; font-size: 1em"
|
||||
>Passport/ID card:({{ bankCardInfo.cardImages.length }}/3)</span
|
||||
>
|
||||
<img
|
||||
src="../../../assets/icon/arrow.png"
|
||||
alt=""
|
||||
@ -79,28 +81,24 @@
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 4px">
|
||||
<div
|
||||
style="width: 10vw; aspect-ratio: 1/1; border-radius: 4px; background: #ededed"
|
||||
></div>
|
||||
<div
|
||||
style="width: 10vw; aspect-ratio: 1/1; border-radius: 4px; background: #ededed"
|
||||
></div>
|
||||
<div
|
||||
style="width: 10vw; aspect-ratio: 1/1; border-radius: 4px; background: #ededed"
|
||||
></div>
|
||||
<img
|
||||
:src="imgUrl"
|
||||
alt=""
|
||||
v-for="imgUrl in bankCardInfo.cardImages"
|
||||
style="width: 10vw; aspect-ratio: 1/1; border-radius: 4px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 卡号 -->
|
||||
<div style="font-weight: 500">Contact number:</div>
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-weight: 600; font-size: 0.8em">
|
||||
123-456-789-1234
|
||||
{{ bankCardInfo.contactNumber }}
|
||||
</div>
|
||||
|
||||
<!-- 备注信息 -->
|
||||
<div style="font-weight: 500">Other description:</div>
|
||||
<div style="color: rgba(0, 0, 0, 0.4); font-weight: 600; font-size: 0.8em">
|
||||
asdfasdfasdfasdf asfasfsaasdasdfasdfasdfa sfasfasfasfasfsdasdfasdfasf asdfafdsa asdf
|
||||
asdasddsasdsadads
|
||||
{{ bankCardInfo.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -192,7 +190,9 @@ import GeneralHeader from '@/components/GeneralHeader.vue'
|
||||
import maskLayer from '@/components/MaskLayer.vue'
|
||||
import {
|
||||
withdrawableAmount, //获取可提现金额
|
||||
withdrawApply, //提交提现申请
|
||||
} from '@/api/lottery'
|
||||
import { setApplyInfo, getApplyInfo } from '@/utils/applyStore.js'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
@ -200,9 +200,7 @@ const router = useRouter()
|
||||
const maskLayerShow = ref(false)
|
||||
const currentAmount = ref(0) //可提现金额
|
||||
|
||||
const bankCardInfo = ref({
|
||||
// id: '',
|
||||
})
|
||||
const bankCardInfo = ref({})
|
||||
const hasBankCard = computed(() => {
|
||||
return Object.keys(bankCardInfo.value).length > 0
|
||||
})
|
||||
@ -220,6 +218,15 @@ const gotoKYC = () => {
|
||||
// 提现按钮
|
||||
const CashOut = () => {
|
||||
if (hasBankCard.value) {
|
||||
try {
|
||||
const resApply = withdrawApply(getApplyInfo())
|
||||
if (resApply.status) {
|
||||
setApplyInfo()
|
||||
router.go(-1)
|
||||
}
|
||||
} catch (error) {
|
||||
showError(error.errorMsg)
|
||||
}
|
||||
} else {
|
||||
maskLayerShow.value = true
|
||||
}
|
||||
@ -242,6 +249,8 @@ const getDrawableAmount = async () => {
|
||||
|
||||
onMounted(() => {
|
||||
getDrawableAmount() //获取可提现金额
|
||||
bankCardInfo.value =
|
||||
{ ...getApplyInfo(), cardImages: JSON.parse(getApplyInfo().cardImages) } || {}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -61,19 +61,27 @@
|
||||
<!-- 上传图片 -->
|
||||
<div style="font-weight: 600">Passport/lD card:</div>
|
||||
<div style="display: flex; align-items: center">
|
||||
<img
|
||||
src="/src/assets/icon/addImg.png"
|
||||
alt=""
|
||||
style="display: block; width: 15vw; aspect-ratio: 1/1; object-fit: cover"
|
||||
/>
|
||||
<div style="position: relative">
|
||||
<img
|
||||
src="/src/assets/icon/addImg.png"
|
||||
alt=""
|
||||
style="display: block; width: 15vw; aspect-ratio: 1/1; object-fit: cover"
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
@change="handleFileChange"
|
||||
style="position: absolute; inset: 0; opacity: 0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<img
|
||||
src="/src/assets/icon/direction.png"
|
||||
alt=""
|
||||
style="display: block; width: 5vw; aspect-ratio: 1/1; object-fit: cover"
|
||||
/>
|
||||
<div style="position: relative">
|
||||
<div v-for="(imgUrl, index) in previewUrls" style="position: relative">
|
||||
<img
|
||||
src="/src/assets/icon/addImg.png"
|
||||
:src="imgUrl"
|
||||
alt=""
|
||||
style="display: block; width: 15vw; aspect-ratio: 1/1; object-fit: cover"
|
||||
/>
|
||||
@ -89,6 +97,7 @@
|
||||
aspect-ratio: 1/1;
|
||||
object-fit: cover;
|
||||
"
|
||||
@click="handleCancel(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -119,11 +128,10 @@
|
||||
<script setup>
|
||||
import GeneralHeader from '@/components/GeneralHeader.vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import {
|
||||
withdrawApply, //获取可提现金额
|
||||
} from '@/api/lottery'
|
||||
import { showError, showWarning, showInfo, showSuccess } from '@/utils/toast.js'
|
||||
import { setApplyInfo, getApplyInfo } from '@/utils/applyStore.js'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { uploadImg } from '@/api/upload.js'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -131,22 +139,77 @@ const cardNum = ref('')
|
||||
const description = ref('')
|
||||
const count = computed(() => description.value.length)
|
||||
|
||||
const previewUrls = ref([]) //预览图
|
||||
const tempFiles = ref([]) //用于提交图片的临时文件列表
|
||||
const resImgUrls = ref([]) //用于提交申请的文件数组
|
||||
|
||||
// 点击上传图片
|
||||
// 处理文件选择
|
||||
const handleFileChange = (e) => {
|
||||
//最多3张图片
|
||||
if (previewUrls.value.length < 3) {
|
||||
const fileList = e.target.files // 获取第一个文件对象
|
||||
|
||||
// 1. 检查是否有文件
|
||||
if (!fileList || fileList.length === 0) {
|
||||
console.error('未选择文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 提取第一个文件并验证
|
||||
const file = fileList[0]
|
||||
if (!file.type || !file.type.startsWith('image/')) {
|
||||
console.error(`不支持的文件类型: ${file.type || '未知'}`)
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 通过验证后的操作(示例)
|
||||
console.log('文件名:', file.name) // defaultAvatar.png
|
||||
console.log('文件类型:', file.type) // image/png
|
||||
console.log('文件大小:', (file.size / 1024).toFixed(2), 'KB')
|
||||
|
||||
// 生成预览图
|
||||
previewUrls.value.push(URL.createObjectURL(file))
|
||||
//临时文件列表,用于上传
|
||||
tempFiles.value.push(file)
|
||||
// uploadImgUrl()
|
||||
} else {
|
||||
showWarning('you can upload up to 3 pictures at most')
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = (index) => {
|
||||
previewUrls.value.splice(index, 1)
|
||||
tempFiles.value.splice(index, 1)
|
||||
resImgUrls.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 上传图片
|
||||
const uploadImgUrl = async () => {
|
||||
for (let index = 0; index < tempFiles.value.length; index++) {
|
||||
const file = tempFiles.value[index]
|
||||
try {
|
||||
const res = await uploadImg(file)
|
||||
if (res.status) {
|
||||
resImgUrls.value.push(res.body)
|
||||
}
|
||||
} catch (error) {
|
||||
showError(error.errorMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交申请
|
||||
const Submit = () => {
|
||||
const Submit = async () => {
|
||||
await uploadImgUrl()
|
||||
let data = {
|
||||
amount: 10,
|
||||
contactNumber: cardNum.value,
|
||||
description: description.value,
|
||||
cardImages: '',
|
||||
}
|
||||
try {
|
||||
const resApply = withdrawApply(data)
|
||||
if (resApply.status) {
|
||||
router.go(-1)
|
||||
}
|
||||
} catch (error) {
|
||||
showError(error.errorMsg)
|
||||
cardImages: JSON.stringify(resImgUrls.value),
|
||||
}
|
||||
setApplyInfo(data)
|
||||
router.go(-1)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user