601 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="fullPage">
<!-- 状态栏占位区域仅在APP中显示 -->
<!-- <div v-if="isInAppEnvironment" style="height: 30px"></div> -->
<img :src="images.pageTop" alt="" width="100%" style="display: block" />
<div
style="
background: linear-gradient(
180deg,
#ffcc97 0%,
#ffa95e 14.99%,
#ff8e2e 29.99%,
#fd6500 44.98%,
#d53f00 59.98%
);
min-height: calc(100vh - 93.3vw + 24px);
border-radius: 24px 24px 0 0;
margin-top: -24px;
position: relative;
z-index: 2;
padding: 12px;
"
>
<!-- 头部 -->
<div style="display: flex; gap: 4px; align-items: flex-end; margin: 0 12px 12px">
<div style="font-size: 0.8em; font-weight: 700">Want To Become A Recharge Agency?</div>
<div
style="
font-size: 0.9em;
color: #ff3e13;
font-weight: 700;
border-bottom: 1px solid #ff3e13;
padding: 0 4px;
"
@click="maskLayerShow = true"
>
Contact Us Now!
</div>
</div>
<!-- 代理列表 -->
<div
style="
padding: 12px;
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 8px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.4);
backdrop-filter: blur(32px);
"
v-for="FreightAgency in FreightAgencyList?.records"
>
<!-- 基本信息 -->
<div style="display: flex; gap: 4px">
<!-- 头像 -->
<div
style="
width: 15%;
min-width: 0;
align-self: stretch;
display: flex;
align-items: center;
"
>
<img
:src="FreightAgency.userBaseInfo.userAvatar"
alt=""
style="
display: block;
width: 100%;
border-radius: 50%;
aspect-ratio: 1/1;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
</div>
<!-- 身份信息 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 4px;
"
>
<!-- 基本信息 -->
<div style="display: flex; align-items: center; gap: 4px">
<!-- 国家 -->
<img :src="FreightAgency.ownNationalFlag" alt="" width="8%" style="display: block" />
<!-- 用户名 -->
<div
style="
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
>
{{ FreightAgency.userBaseInfo.userNickname }}
</div>
</div>
<!-- 身份 -->
<div style="display: flex; align-items: center; gap: 4px">
<!-- 账号 -->
<div style="position: relative; width: 30%">
<img :src="images.idBg" alt="" width="100%" style="display: block" />
<div
style="
position: absolute;
inset: 0;
padding: 5% 8% 5% 23%;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<div
style="
font-size: 0.6em;
width: 80%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: flex;
justify-content: center;
align-items: center;
"
>
{{
FreightAgency.userBaseInfo.ownSpecialId?.account ||
FreightAgency.userBaseInfo.account
}}
</div>
<img
:src="images.copyWhite"
alt=""
width="15%"
@click="
copyPhone(
FreightAgency.userBaseInfo.ownSpecialId?.account ||
FreightAgency.userBaseInfo.account
)
"
/>
</div>
</div>
<!-- 身份标识 -->
<img
src="../../assets/icon/identity/coinSeller.png"
alt=""
width="25%"
style="display: block"
/>
</div>
</div>
</div>
<!-- 代理信息 -->
<div>
<!-- 财力展示 -->
<div style="display: flex; gap: 4px; align-items: center">
<div style="font-weight: 700">Current balance:</div>
<div style="width: 5%">
<img
src="/src/assets/icon/coin.png"
alt=""
width="100%"
style="aspect-ratio: 1/1; display: block"
/>
</div>
<div style="color: #f00; font-weight: 590">{{ maskAmount(FreightAgency.balance) }}</div>
</div>
<!-- 老资历 -->
<div style="font-weight: 600; font-size: 0.8em">
Has been a coin agency for {{ FreightAgency.becomeDays }} days
</div>
<!-- 社交的手腕 -->
<div>
<div style="font-weight: 600; font-size: 0.8em; margin-bottom: 4px">
Available countries:
</div>
<div style="display: flex; gap: 4px; align-items: center; flex-wrap: wrap">
<img
v-for="imgUrl in FreightAgency.nationalFlag"
:src="imgUrl"
alt=""
width="8%"
style="display: block"
/>
</div>
</div>
<!-- 实力展示 -->
<div>{{ FreightAgency.transactionCount }} successful transaction</div>
<!-- 食人树 -->
<div
style="display: flex; align-items: center; gap: 4px; font-weight: 600; font-size: 0.9em"
>
<img :src="images.phone" alt="" width="8%" />
{{ FreightAgency.contactInfo || '' }}
</div>
</div>
<!-- 按钮 -->
<div style="display: flex; justify-content: space-between">
<!-- 关注按钮 -->
<div
style="
width: 45%;
padding: 8px 12px;
border-radius: 89px;
background-blend-mode: normal, soft-light, normal;
display: flex;
justify-content: center;
align-items: center;
"
:style="{
background: FreightAgency.isFollow
? `radial-gradient(107.94% 107.94% at 15.9% 13.54%, rgba(255, 255, 255, 0.20) 0%, rgba(255, 255, 255, 0.00) 100%), linear-gradient(180deg, #FFF 0%, #000 100%), #D1D1D1`
: `radial-gradient(
107.94% 107.94% at 15.9% 13.54%,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0) 100%
),
linear-gradient(180deg, #fff 0%, #000 100%), #b464ff`,
}"
@click="followBt(FreightAgency.isFollow, FreightAgency.userBaseInfo.id)"
>
<div style="font-weight: 500">{{ FollowButtonText(FreightAgency.isFollow) }}</div>
</div>
<!-- 私聊按钮 -->
<div
style="
width: 45%;
padding: 8px 12px;
text-align: center;
border-radius: 60px;
background: radial-gradient(
107.94% 107.94% at 15.9% 13.54%,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0) 100%
),
linear-gradient(180deg, #fff 0%, #000 100%), #4ecf5d;
background-blend-mode: normal, soft-light, normal;
display: flex;
justify-content: center;
align-items: center;
"
>
<div style="font-weight: 500" @click="gotoPrivateChat(FreightAgency.userBaseInfo.id)">
Send Message
</div>
</div>
</div>
</div>
</div>
<!-- 弹窗遮罩层 -->
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
<div style="height: 100%; display: flex; justify-content: center; align-items: center">
<div
style="
width: 90%;
padding: 10px;
border-radius: 12px;
background: #fff;
display: flex;
gap: 10px;
flex-direction: column;
"
@click.stop
>
<div style="color: rgba(0, 0, 0, 0.8); font-weight: 800; font-size: 0.9em">
Contact Likei company's Whatsapp account:
</div>
<div style="display: flex; gap: 10px; align-items: center">
<img :src="images.phone" alt="" width="24px" />
<div style="color: #000; font-weight: 600">+86 18709616238</div>
<img :src="images.copy" alt="" width="24px" @click="copyPhone('+86 18709616238')" />
</div>
<div style="display: flex; gap: 10px; align-items: center">
<img :src="images.phone" alt="" width="24px" />
<div style="color: #000; font-weight: 600">+86 18942503069</div>
<img :src="images.copy" alt="" width="24px" @click="copyPhone('+86 18942503069')" />
</div>
</div>
</div>
</maskLayer>
<!-- 提示弹窗 -->
<div
v-show="tipShow"
style="
position: fixed;
top: 50vh;
display: flex;
justify-content: center;
width: 100vw;
z-index: 1000;
"
>
<div
style="padding: 12px; border-radius: 8px; background: rgba(0, 0, 0, 0.4); font-weight: 600"
>
Copied successfully
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useClipboard } from '@vueuse/core'
import { showError, showWarning, showInfo, showSuccess } from '@/utils/toast.js'
import {
connectApplication,
parseAccessOrigin,
parseHeader,
setHttpHeaders,
isInApp,
gotoPrivateChat,
} from '@/utils/appBridge.js'
import {
appConnectionManager,
isAppConnected,
getAppHeaderInfo,
} from '@/utils/appConnectionManager.js'
import { handleAvatarImageError } from '@/utils/imageHandler.js'
import { getFreightAgencys } from '@/api/userInfo.js'
import { follow } from '@/api/recruit.js'
import maskLayer from '@/components/MaskLayer.vue'
//vite动态批量导入图片
const imageModules = import.meta.glob('@/assets/images/RechargeAgency/*.{png,jpg,svg}', {
eager: true,
})
// 生成文件名映射对象(自动移除路径和扩展名)
const images = Object.fromEntries(
Object.entries(imageModules).map(([path, module]) => {
const fileName = path.split('/').pop().split('.')[0]
return [fileName, module.default]
})
)
const { copy, isSupported } = useClipboard()
const appConnected = ref(false)
const headerInfo = ref({})
const isInAppEnvironment = ref(false) // 检测是否在APP环境中
const FreightAgencyList = ref([]) //充值代理列表
const maskLayerShow = ref(false) // 控制遮罩层显示隐藏
const tipShow = ref(false) //提示弹窗
// 获取代理列表
const getAgencyList = async () => {
const resAgencyList = await getFreightAgencys()
if (resAgencyList.status && resAgencyList.body) {
FreightAgencyList.value = resAgencyList.body
}
}
// 格式化金额
const maskAmount = (number) => {
let amount = number.toString()
if (amount.length === 0) return ''
if (amount.length === 1) return amount
let firstChar = amount.charAt(0)
let maskedPart = '*'.repeat(amount.length - 1)
return firstChar + maskedPart
}
// 格式化关注按钮
const FollowButtonText = (isFollow) => {
return isFollow ? 'Following' : 'Follow'
}
// 点击关注按钮
const followBt = async (isFollow, userId) => {
if (isFollow) return
let data = {
userId,
}
try {
const resFollow = await follow(data)
if (resFollow.status) {
getAgencyList() // 获取代理列表
}
} catch (error) {
showError(error.errorMsg)
}
}
// 关闭弹窗
const closedPopup = () => {
maskLayerShow.value = false
}
// 复制到剪贴板(原生方法)
const copyPhone = (text) => {
if (isSupported.value) {
copy(text)
} else {
// 降级方案
const textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.select()
document.execCommand('copy')
document.body.removeChild(textArea)
}
tipShow.value = true
setTimeout(() => {
tipShow.value = false
}, 2000)
}
/**
* 连接APP并获取认证信息优化版 - 仅专注连接)
*/
const connectToApp = async () => {
console.group('🔗 APP Connection Process')
console.debug('🚀 Starting APP connection...')
try {
// 检查是否在APP环境中
if (!isInApp()) {
console.debug('🌐 Browser environment detected')
appConnected.value = true
console.groupEnd()
// 触发连接成功回调
getAgencyList() // 获取代理列表
return { success: true, environment: 'browser' }
}
// 检查是否已经连接且未过期
if (isAppConnected()) {
console.debug('✅ APP already connected and valid')
appConnected.value = true
// 使用缓存的头部信息
const cachedHeaderInfo = getAppHeaderInfo()
if (cachedHeaderInfo) {
headerInfo.value = cachedHeaderInfo
console.debug('🔧 Using cached HTTP headers')
}
console.groupEnd()
// 触发连接成功回调
getAgencyList() // 获取代理列表
return { success: true, environment: 'app', fromCache: true }
}
// 检查是否有正在进行的连接
if (appConnectionManager.isConnecting()) {
console.debug('⏳ Connection already in progress, waiting...')
await appConnectionManager.getConnectionPromise()
appConnected.value = true
console.debug('✅ Connected via existing process')
console.groupEnd()
// 触发连接成功回调
getAgencyList() // 获取代理列表
return { success: true, environment: 'app', fromCache: true }
}
// 建立新的APP连接
console.debug('📱 Establishing new APP connection...')
const connectionResult = await new Promise((resolve, reject) => {
const connectionPromise = new Promise((connectResolve, connectReject) => {
connectApplication(async (access) => {
try {
const result = parseAccessOrigin(access)
if (result.success) {
// 解析头部信息
headerInfo.value = parseHeader(result.data)
// 设置HTTP请求头
console.debug('🔧 Setting HTTP headers...')
await setHttpHeaders(headerInfo.value)
// 标记连接成功
appConnected.value = true
appConnectionManager.setConnected(headerInfo.value)
console.debug('🎉 APP connection established successfully')
connectResolve({ success: true, environment: 'app', fromCache: false })
} else {
console.error('❌ Failed to parse access origin:', result.error)
appConnectionManager.reset()
connectReject(new Error(result.error))
}
} catch (error) {
console.error('❌ Connection process failed:', error)
appConnectionManager.reset()
connectReject(error)
}
})
})
// 设置连接状态
appConnectionManager.setConnecting(connectionPromise)
connectionPromise.then(resolve).catch(reject)
})
console.debug('✅ Connection completed successfully')
console.groupEnd()
// 触发连接成功回调
getAgencyList() // 获取代理列表
return connectionResult
} catch (error) {
console.error('❌ Connection failed:', error)
appConnected.value = false
console.groupEnd()
// 触发连接失败回调
// if (onConnectionFailed) {
// onConnectionFailed(error)
// }
// 连接失败的特殊处理
if (error.message && error.message.includes('parse access')) {
router.push({ path: '/not_app', query: { message: error.message } })
}
return { success: false, error: error.message }
}
}
onMounted(() => {
connectToApp()
isInAppEnvironment.value = isInApp()
})
</script>
<style lang="scss" scoped>
* {
color: #fff;
}
.fullPage {
width: 100vw;
min-height: 100vh;
background-color: #220447;
position: relative;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 12px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
</style>