| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
- <title>BEX会员等级</title>
- <link rel="stylesheet" href="/css/vip.css">
- </head>
- <body>
- <div class="container">
- <div class="header">
- <svg id="backButton" width="16" height="16" viewBox="0 0 16 16" fill="none"
- xmlns="http://www.w3.org/2000/svg">
- <path d="M10.4 13.6L4.8 8L10.4 2.4" stroke="white" stroke-width="1.6" stroke-linecap="round"
- stroke-linejoin="round" />
- </svg>
- <div class="header-title" data-i18n="headerTitle">BEX会员等级</div>
- <div></div>
- </div>
- <div class="vip-card">
- <i class="vip-bg"></i>
- <div class="vip-box">
- <div class="vip-card-header">
- <div class="vip-level-badge">
- <div class="vip-level-title" data-i18n="vipLevelTitle">会员等级</div>
- <div class="vip-level-text" data-field="vipLevel"></div>
- </div>
- </div>
- <div class="progress-items">
- <div class="progress-item">
- <div class="progress-label" data-i18n="progressContribution"> <span
- data-field="contribution"></span> </div>
- <div class="progress-bar">
- <div class="progress-bar-fill" data-field="contributionProgress"></div>
- </div>
- </div>
- <div class="progress-item">
- <div class="progress-label" data-i18n="progressStaking"><span data-field="staking"></span>
- </div>
- <div class="progress-bar">
- <div class="progress-bar-fill" data-field="stakingProgress"></div>
- </div>
- </div>
- </div>
- <div class="vip-card-tip" data-i18n="vipTip">*满足以上条件即可升级至 VIP3</div>
- </div>
- </div>
- <!-- <div class="section-title" data-i18n="benefitsTitle">等级权益</div> -->
- <div class="benefits-tabs">
- <div class="benefits-tab active" data-tab="requirements" data-i18n="benefitsTabRequirements">等级权益</div>
- <div class="benefits-tab" data-tab="benefits" data-i18n="benefitsTabBenefits">升级规则</div>
- </div>
- <div class="benefits-table">
- <div>
- <div class="benefits-header requirements-header" style="display: none;">
- <div data-i18n="benefitsVipLevel" class="td">会员等级</div>
- <div data-i18n="benefitsContribution" class="td">贡献值</div>
- <div data-i18n="benefitsStaking" class="td">个人质押要求</div>
- </div>
- <div class="benefits-header benefits-ben-header">
- <div data-i18n="benefitsVipLevel" class="td">会员等级</div>
- <div data-i18n="benefitsTeamReward" class="td">团队质押动态奖励</div>
- <div data-i18n="benefitsTradeReturn" class="td">交易返佣比例</div>
- </div>
- <div id="benefitsBody" style="display: none;"></div>
- <div id="benefitsBodyAlt"></div>
- </div>
- </div>
- <div class="contribution-section">
- <div class="contribution-title" data-i18n="contributionTitle">贡献值获取方式</div>
- <div class="contribution-card">
- <div class="contribution-item" data-i18n="contributionPersonal">个人质押:质押 <span class="highlight"
- data-field="rules.personalStaking">100枚</span> = <span class="highlight"
- data-i18n="contributionUnit">1个</span>贡献值</div>
- <div class="contribution-item" data-i18n="contributionTeam">团队质押:质押 <span class="highlight"
- data-field="rules.teamStaking">1000枚</span> = <span class="highlight"
- data-i18n="contributionUnit">1个</span>贡献值</div>
- <div class="contribution-item" data-i18n="contributionSpot">现货交易:团队(含个人)交易 <span class="highlight"
- data-field="rules.spotTrading">5000$</span> = <span class="highlight"
- data-i18n="contributionUnit">1个</span>贡献值</div>
- <div class="contribution-item" data-i18n="contributionFutures">合约交易:团队(含个人)交易 <span class="highlight"
- data-field="rules.futuresTrading">50,0000$</span> = <span class="highlight"
- data-i18n="contributionUnit">1个</span>贡献值</div>
- </div>
- <div class="contribution-tip" data-i18n="contributionTip">*降级规则: 所有级别如贡献值下降,原级别保留 1 个月;下个月如继续未达标,则降级至对应实际级别
- </div>
- </div>
- <div class="loading-overlay" id="loadingOverlay" style="display: none;">
- <div class="loading-content">
- <div class="loading-spinner"></div>
- <span class="loading-text" data-i18n="loadingText">加载中...</span>
- </div>
- </div>
- <div class="error-overlay" id="errorOverlay" style="display: none;">
- <div class="error-content">
- <div class="error-icon">!</div>
- <span class="error-text" data-i18n="errorText">加载失败,请稍后重试</span>
- <button class="error-retry" id="retryBtn" data-i18n="retryBtn">重试</button>
- </div>
- </div>
- </div>
- <script>
- (function () {
- const translations = {
- 'zh': {
- headerTitle: 'BEX会员等级',
- vipLevelTitle: '会员等级',
- progressContribution: '距离升级至 {nextVipLevel} 还需要 <span data-field="contribution"></span> (当前贡献值: <span data-field="personalValue"></span>)',
- progressStaking: '距离升级至 {nextVipLevel} 还需要个人质押 <span data-field="staking">799枚</span> (当前质押: <span data-field="personalStakeValue"></span>)',
- vipTip: '*满足以上条件即可升级至 {nextVipLevel}',
- benefitsTitle: '等级权益',
- benefitsTabRequirements: '等级权益',
- benefitsTabBenefits: '升级规则',
- benefitsVipLevel: '会员等级',
- benefitsContribution: '贡献值',
- benefitsStaking: '个人质押要求',
- benefitsTeamReward: '团队质押动态奖励',
- benefitsTradeReturn: '交易返佣比例',
- benefitsDailyLimit: '提币额度',
- benefitsFeeDiscount: '手续费折扣',
- benefitsCustomerService: '专属客服',
- benefitsAirDrop: '空投权益',
- contributionTitle: '贡献值获取方式',
- contributionPersonal: '个人质押:质押 <span class="highlight" data-field="rules.personalStaking">100枚</span> = <span class="highlight">1个</span>贡献值',
- contributionTeam: '团队质押:质押 <span class="highlight" data-field="rules.teamStaking">1000枚</span> = <span class="highlight">1个</span>贡献值',
- contributionSpot: '现货交易:团队(含个人)交易 <span class="highlight" data-field="rules.spotTrading">5000$</span> = <span class="highlight">1个</span>贡献值',
- contributionFutures: '合约交易:团队(含个人)交易 <span class="highlight" data-field="rules.futuresTrading">50,0000$</span> = <span class="highlight">1个</span>贡献值',
- contributionTip: '*降级规则: 所有级别如贡献值下降,原级别保留 1 个月;下个月如继续未达标,则降级至对应实际级别',
- loadingText: '加载中...',
- errorText: '加载失败,请稍后重试',
- retryBtn: '重试',
- contributionUnit: '1个',
- stakingUnit: '枚',
- teamRewardUnit: '%',
- tradeReturnUnit: '%'
- },
- 'en': {
- headerTitle: 'BEX VIP Level',
- vipLevelTitle: 'VIP Level',
- progressContribution: 'Need <span data-field="contribution"></span> more contribution points to upgrade to {nextVipLevel} (Current: <span data-field="personalValue"></span>)',
- progressStaking: 'Need <span data-field="staking">799</span> more staking coins to upgrade to {nextVipLevel} (Current: <span data-field="personalStakeValue"></span>)',
- vipTip: '*Meet the above conditions to upgrade to {nextVipLevel}',
- benefitsTitle: 'Level Benefits',
- benefitsTabRequirements: 'Upgrade Requirements',
- benefitsTabBenefits: 'Benefits',
- benefitsVipLevel: 'VIP Level',
- benefitsContribution: 'Contribution',
- benefitsStaking: 'Personal Staking',
- benefitsTeamReward: 'Team Reward',
- benefitsTradeReturn: 'Team commission ratio',
- benefitsDailyLimit: 'Withdrawal Limit',
- benefitsFeeDiscount: 'Fee Discount',
- benefitsCustomerService: 'VIP Support',
- benefitsAirDrop: 'Airdrop',
- contributionTitle: 'How to Earn Contribution',
- contributionPersonal: 'Personal Staking: Stake <span class="highlight" data-field="rules.personalStaking">100 coins</span> = <span class="highlight">1</span> contribution',
- contributionTeam: 'Team Staking: Stake <span class="highlight" data-field="rules.teamStaking">1000 coins</span> = <span class="highlight">1</span> contribution',
- contributionSpot: 'Spot Trading: Team(including personal) trade <span class="highlight" data-field="rules.spotTrading">5000$</span> = <span class="highlight">1</span> contribution',
- contributionFutures: 'Futures Trading: Team(including personal) trade <span class="highlight" data-field="rules.futuresTrading">50,0000$</span> = <span class="highlight">1</span> contribution',
- contributionTip: '*Downgrade Rule: If contribution drops, current level is retained for 1 month; if still not met next month, downgrade to actual level',
- loadingText: 'Loading...',
- errorText: 'Failed to load, please try again later',
- retryBtn: 'Retry',
- contributionUnit: '1',
- stakingUnit: 'coins',
- teamRewardUnit: '%',
- tradeReturnUnit: '%'
- }
- };
- function getUrlParam(name) {
- const urlParams = new URLSearchParams(window.location.search);
- return urlParams.get(name);
- }
- function getCurrentLang() {
- const lang = getUrlParam('lang');
- let l = '';
- if (lang === 'zh' || lang === 'zh-CN' || lang === 'zh-TW' || lang === 'zh-HK') {
- l = 'zh';
- } else {
- l = 'en';
- }
- return l;
- }
- function applyTranslations(lang, nextVipLevel) {
- const translation = translations[lang] || translations['en'];
- const elements = document.querySelectorAll('[data-i18n]');
- elements.forEach(element => {
- const key = element.dataset.i18n;
- if (translation[key]) {
- let text = translation[key];
- if (nextVipLevel) {
- text = text.replace(/{nextVipLevel}/g, 'VIP' + nextVipLevel);
- }
- element.innerHTML = text;
- }
- });
- document.documentElement.lang = lang === 'zh' ? 'zh-CN' : 'en';
- }
- const API_URL = '/api/v1';
- const loadingOverlay = document.getElementById('loadingOverlay');
- const errorOverlay = document.getElementById('errorOverlay');
- const retryBtn = document.getElementById('retryBtn');
- const benefitsBody = document.getElementById('benefitsBody');
- const benefitsBodyAlt = document.getElementById('benefitsBodyAlt');
- const backButton = document.getElementById('backButton');
- const benefitsTabs = document.querySelectorAll('.benefits-tab');
- benefitsTabs.forEach(tab => {
- tab.addEventListener('click', function () {
- const tabName = this.dataset.tab;
- benefitsTabs.forEach(t => t.classList.remove('active'));
- this.classList.add('active');
- if (tabName === 'requirements') {
- document.querySelector('.requirements-header.requirements-header').style.display = 'none';
- document.querySelector('.benefits-header.benefits-ben-header').style.display = 'grid';
- benefitsBody.style.display = 'none';
- benefitsBodyAlt.style.display = 'block';
- } else {
- document.querySelector('.requirements-header.requirements-header').style.display = 'grid';
- document.querySelector('.benefits-header.benefits-ben-header').style.display = 'none';
- benefitsBody.style.display = 'block';
- benefitsBodyAlt.style.display = 'none';
- }
- });
- });
- backButton.addEventListener('click', function () {
- if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
- window.flutter_inappwebview.callHandler('closeWebview');
- }
- });
- function showLoading() {
- loadingOverlay.style.display = 'flex';
- errorOverlay.style.display = 'none';
- }
- function hideLoading() {
- loadingOverlay.style.display = 'none';
- }
- function showError() {
- loadingOverlay.style.display = 'none';
- errorOverlay.style.display = 'flex';
- }
- function renderBenefitsRows(vipList) {
- if (!Array.isArray(vipList) || vipList.length === 0) {
- console.warn('VIP列表数据为空或格式错误');
- return;
- }
- benefitsBody.innerHTML = '';
- const lang = getCurrentLang();
- const translation = translations[lang] || translations['en'];
- const stakingUnit = lang === 'zh' ? '枚' : 'coins';
- vipList.forEach(item => {
- const row = document.createElement('div');
- row.className = 'benefits-row';
- row.dataset.vip = item.vipLevel;
- row.innerHTML = `
- <div class="vip-name td">
- <img src="/images/vip${item.vipLevel}.png" alt="${item.vipName}">
- <span>${item.vipName}</span>
- </div>
- <div class="td">${item.contributionValue}</div>
- <div class="td">${item.personalPledgeRequirements}${stakingUnit}</div>
- `;
- benefitsBody.appendChild(row);
- });
- }
- function renderBenefitsAltRows(vipList) {
- if (!Array.isArray(vipList) || vipList.length === 0) {
- console.warn('VIP列表数据为空或格式错误');
- return;
- }
- benefitsBodyAlt.innerHTML = '';
- const lang = getCurrentLang();
- const translation = translations[lang] || translations['en'];
- const stakingUnit = lang === 'zh' ? '枚' : 'coins';
- vipList.forEach(item => {
- const row = document.createElement('div');
- row.className = 'benefits-row';
- row.dataset.vip = item.vipLevel;
- row.innerHTML = `
- <div class="vip-name td">
- <img src="/images/vip${item.vipLevel}.png" alt="${item.vipName}">
- <span>${item.vipName}</span>
- </div>
- <div class="td">${item.teamStakingDynamicRewards}${translation.teamRewardUnit}</div>
- <div class="td">${item.transactionRefundRatio}${translation.tradeReturnUnit}</div>
- `;
- benefitsBodyAlt.appendChild(row);
- });
- }
- function insertData(data) {
- const vipBenefitManagements = data.vipBenefitManagements || [];
- const contributionRuleConfigs = data.contributionRuleConfigs || [];
- if (vipBenefitManagements.length > 0) {
- renderBenefitsRows(vipBenefitManagements);
- renderBenefitsAltRows(vipBenefitManagements);
- }
- const rules = {};
- contributionRuleConfigs.forEach(item => {
- if (item.ruleCode === 'PERSONAL_STAKE') {
- rules.personalStaking = item.threshold + (getCurrentLang() === 'zh' ? '枚' : ' coins');
- } else if (item.ruleCode === 'TEAM_STAKE') {
- rules.teamStaking = item.threshold + (getCurrentLang() === 'zh' ? '枚' : ' coins');
- } else if (item.ruleCode === 'SPOT_TRADE') {
- rules.spotTrading = item.threshold + '$';
- } else if (item.ruleCode === 'FUTURES_TRADE') {
- rules.futuresTrading = item.threshold + '$';
- }
- });
- const currentVipLevel = data.vipLevel || 0;
- const personalValue = data.personalValue || 0;
- const personalStakeValue = data.personalStakeValue || 0;
- const currentLevelInfo = vipBenefitManagements.find(item => item.vipLevel === currentVipLevel);
- const nextLevelInfo = vipBenefitManagements.find(item => item.vipLevel === currentVipLevel + 1);
- let contributionProgress = 0;
- let stakingProgress = 0;
- let contributionNeeded = 0;
- let stakingNeeded = 0;
- if (nextLevelInfo) {
- const currentContribution = currentLevelInfo ? currentLevelInfo.contributionValue || 0 : 0;
- const currentStaking = currentLevelInfo ? currentLevelInfo.personalPledgeRequirements || 0 : 0;
- const nextContribution = nextLevelInfo.contributionValue || 0;
- const nextStaking = nextLevelInfo.personalPledgeRequirements || 0;
- const contributionDelta = nextContribution - currentContribution;
- const stakingDelta = nextStaking - currentStaking;
- const currentContributionIncrement = personalValue - currentContribution;
- const currentStakingIncrement = personalStakeValue - currentStaking;
- contributionNeeded = Math.max(0, contributionDelta - currentContributionIncrement);
- stakingNeeded = Math.max(0, stakingDelta - currentStakingIncrement);
- contributionProgress = contributionDelta > 0 ? Math.min(100, Math.max(0, (currentContributionIncrement / contributionDelta) * 100)) : 0;
- stakingProgress = stakingDelta > 0 ? Math.min(100, Math.max(0, (currentStakingIncrement / stakingDelta) * 100)) : 0;
- } else {
- contributionProgress = 100;
- stakingProgress = 100;
- }
- const lang = getCurrentLang();
- const stakingUnit = lang === 'zh' ? '枚' : ' coins';
- const elements = document.querySelectorAll('[data-field]');
- elements.forEach(element => {
- const field = element.dataset.field;
- let value = getNestedValue(data, field);
- if (field === 'vipLevel' && data.vipName) {
- value = data.vipName;
- }
- if (field.startsWith('rules.')) {
- const ruleKey = field.replace('rules.', '');
- value = rules[ruleKey];
- }
- if (field === 'contribution') {
- value = contributionNeeded + (lang === 'zh' ? '贡献值' : ' contribution');
- }
- if (field === 'staking') {
- value = stakingNeeded + stakingUnit;
- }
- if (field === 'personalValue') {
- value = value + (lang === 'zh' ? '贡献值' : ' contribution');
- }
- if (field === 'personalStakeValue') {
- value = value + stakingUnit;
- }
- if (field === 'contributionProgress') {
- value = contributionProgress.toFixed(0);
- }
- if (field === 'stakingProgress') {
- value = stakingProgress.toFixed(0);
- }
- if (value !== undefined && value !== null) {
- if (field.includes('Progress')) {
- element.style.width = value + '%';
- } else {
- element.textContent = value;
- }
- }
- });
- }
- function getNestedValue(obj, path) {
- return path.split('.').reduce((current, key) => {
- return current && current[key] !== undefined ? current[key] : undefined;
- }, obj);
- }
- let token = '';
- async function getFilterFromNative() {
- try {
- const filter = await window.flutter_inappwebview.callHandler('getToken');
- token = filter;
- return filter;
- } catch (e) {
- console.error('JSBridge 调用失败', e);
- return null;
- }
- }
- async function fetchData() {
- showLoading();
- try {
- const headers = {
- 'Content-Type': 'application/json',
- 'Accept': 'application/json',
- 'X-Requested-With': 'XMLHttpRequest',
- };
- if (token) {
- headers['Authorization'] = token;
- }
- const response = await fetch(API_URL + '/userVipInfo/info', {
- method: 'GET',
- headers: headers,
- credentials: 'include',
- timeout: 10000
- });
- if (!response.ok) {
- throw new Error(`请求失败! status: ${response.status}`);
- }
- const result = await response.json();
- const data = result.data || {};
- const currentVipLevel = data.vipLevel || 0;
- const nextVipLevel = currentVipLevel + 1;
- applyTranslations(getCurrentLang(), nextVipLevel);
- insertData(data);
- hideLoading();
- } catch (error) {
- console.error('Data fetch error:', error);
- hideLoading();
- }
- }
- retryBtn.addEventListener('click', fetchData);
- document.addEventListener('DOMContentLoaded', async function () {
- const lang = getCurrentLang();
- applyTranslations(lang);
- await getFilterFromNative();
- fetchData();
- });
- })();
- </script>
- </body>
- </html>
|