f
This commit is contained in:
3
.env
3
.env
@@ -33,6 +33,9 @@ VITE_SITE_ORIGIN=https://chimei.ronsafe.cn
|
||||
# 报告 H5 根地址(可选,优先与 VITE_SITE_ORIGIN 配合使用)
|
||||
VITE_REPORT_BASE_URL=https://chimei.ronsafe.cn
|
||||
|
||||
# 与 bdrp-webview `.env` 中 VITE_TOKEN_VERSION 保持一致(外部浏览器带参登录)
|
||||
VITE_TOKEN_VERSION=1.0
|
||||
|
||||
# `src/pages/not-found.vue` 必需
|
||||
VITE_SEO_SITE_NAME=赤眉
|
||||
|
||||
|
||||
32
_backup/pages/agent-vip-apply.vue
Normal file
32
_backup/pages/agent-vip-apply.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { buildAgentVipApplyH5Url } from '@/composables/useAgentVipH5'
|
||||
import { ensureCurrentPageAccess } from '@/composables/useNavigationAuthGuard'
|
||||
|
||||
definePage({ layout: false, auth: true })
|
||||
|
||||
const src = ref('')
|
||||
|
||||
onLoad((query) => {
|
||||
const params: Record<string, string> = {}
|
||||
const type = query?.type
|
||||
if (type) {
|
||||
const normalized = String(type).toLowerCase()
|
||||
if (normalized === 'vip' || normalized === 'svip')
|
||||
params.type = normalized
|
||||
}
|
||||
src.value = buildAgentVipApplyH5Url(params)
|
||||
if (!src.value)
|
||||
uni.showToast({ title: '未配置 H5 站点地址', icon: 'none' })
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
ensureCurrentPageAccess('redirect')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<web-view v-if="src" :src="src" />
|
||||
<view v-else class="flex min-h-screen items-center justify-center bg-gray-50 px-6 text-center text-sm text-gray-500">
|
||||
无法打开会员升级页面,请检查 VITE_SITE_ORIGIN 配置
|
||||
</view>
|
||||
</template>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import InquireForm from '@/components/InquireForm.vue'
|
||||
import InquireForm from './components/InquireForm.vue'
|
||||
|
||||
definePage({ layout: 'default' })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
||||
|
||||
export default defineManifestConfig({
|
||||
'name': '赤眉',
|
||||
'name': '戎行赤眉',
|
||||
'appid': 'H5A4E639E',
|
||||
'description': '',
|
||||
'versionName': '1.0.0',
|
||||
@@ -36,19 +36,40 @@ export default defineManifestConfig({
|
||||
},
|
||||
"Payment": {},
|
||||
"UIWebview": {},
|
||||
"Camera": {}
|
||||
},
|
||||
/* 应用发布信息 */
|
||||
distribute: {
|
||||
/* android打包配置 */
|
||||
android: {
|
||||
permissions: [
|
||||
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
|
||||
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
|
||||
permissionExternalStorage: {
|
||||
request: 'none',
|
||||
},
|
||||
permissionPhoneState: {
|
||||
request: 'none',
|
||||
},
|
||||
// 云打包默认已含 INTERNET / 网络状态 / 读写存储等,此处只配置「强制移除」与启动申请策略,避免 permissions 重复合并失败
|
||||
excludePermissions: [
|
||||
'<uses-permission android:name="android.permission.CAMERA"/>',
|
||||
'<uses-feature android:name="android.hardware.camera"/>',
|
||||
'<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>',
|
||||
'<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>',
|
||||
'<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>',
|
||||
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>',
|
||||
'<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE"/>',
|
||||
'<uses-permission android:name="android.permission.CALL_PHONE"/>',
|
||||
'<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/>',
|
||||
'<uses-permission android:name="android.permission.READ_CALL_LOG"/>',
|
||||
'<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>',
|
||||
'<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>',
|
||||
'<uses-permission android:name="android.permission.CALL_PRIVILEGED"/>',
|
||||
'<uses-permission android:name="android.permission.ADD_VOICEMAIL"/>',
|
||||
'<uses-permission android:name="android.permission.READ_VOICEMAIL"/>',
|
||||
'<uses-permission android:name="android.permission.WRITE_VOICEMAIL"/>',
|
||||
'<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>',
|
||||
'<uses-permission android:name="android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER"/>',
|
||||
],
|
||||
"targetSdkVersion": 30
|
||||
},
|
||||
/* ios打包配置 */
|
||||
"ios": {
|
||||
|
||||
@@ -12,14 +12,11 @@ export default defineUniPages({
|
||||
{ path: 'pages/agent-rewards-details', auth: true, style: { navigationBarTitleText: '奖励明细' } },
|
||||
{ path: 'pages/agent-service-agreement', style: { navigationBarTitleText: '信息技术服务合同', navigationStyle: 'default' } },
|
||||
{ path: 'pages/agent-vip', auth: true, style: { navigationBarTitleText: '代理会员' } },
|
||||
{ path: 'pages/agent-vip-apply', auth: true, style: { navigationBarTitleText: 'VIP代理申请' } },
|
||||
{ path: 'pages/agent-vip-config', auth: true, style: { navigationBarTitleText: '代理报告配置' } },
|
||||
{ path: 'pages/authorization', style: { navigationBarTitleText: '授权书', navigationStyle: 'default' } },
|
||||
{ path: 'pages/help', style: { navigationBarTitleText: '帮助中心' } },
|
||||
{ path: 'pages/help-detail', style: { navigationBarTitleText: '帮助详情' } },
|
||||
{ path: 'pages/help-guide', style: { navigationBarTitleText: '引导指南' } },
|
||||
{ path: 'pages/history-query', auth: true, style: { navigationBarTitleText: '历史报告' } },
|
||||
{ path: 'pages/inquire', style: { navigationBarTitleText: '查询报告' } },
|
||||
{ path: 'pages/invitation', auth: true, style: { navigationBarTitleText: '邀请下级' } },
|
||||
{ path: 'pages/invitation-agent-apply', auth: true, style: { navigationBarTitleText: '代理申请' } },
|
||||
{ path: 'pages/login', style: { navigationBarTitleText: '登录' } },
|
||||
|
||||
12
src/auto-imports.d.ts
vendored
12
src/auto-imports.d.ts
vendored
@@ -10,8 +10,10 @@ declare global {
|
||||
const ZoomAdapter: typeof import('./utils/zoomAdapter.js')['ZoomAdapter']
|
||||
const aesDecrypt: typeof import('./utils/crypto.js')['aesDecrypt']
|
||||
const aesEncrypt: typeof import('./utils/crypto.js')['aesEncrypt']
|
||||
const appendAppAuthQueryParams: typeof import('./composables/useReportWebview')['appendAppAuthQueryParams']
|
||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||
const buildAgentVipApplyH5Url: typeof import('./composables/useAgentVipH5')['buildAgentVipApplyH5Url']
|
||||
const calculatePromotionPricing: typeof import('./utils/promotionPricing.js')['calculatePromotionPricing']
|
||||
const chatCrypto: typeof import('./utils/chatCrypto.js')['default']
|
||||
const chatEncrypt: typeof import('./utils/chatEncrypt.js')['default']
|
||||
@@ -78,6 +80,7 @@ declare global {
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const navigateLogin: typeof import('./utils/navigate')['navigateLogin']
|
||||
const navigateTo: typeof import('./utils/navigate')['navigateTo']
|
||||
const navigateToAgentVipApplyH5: typeof import('./composables/useAgentVipH5')['navigateToAgentVipApplyH5']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
|
||||
@@ -122,7 +125,9 @@ declare global {
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||
const openAgentVipApplyInExternalBrowser: typeof import('./composables/useAgentVipH5')['openAgentVipApplyInExternalBrowser']
|
||||
const openCustomerService: typeof import('./composables/useCustomerService')['openCustomerService']
|
||||
const openExternalUrl: typeof import('./composables/openExternalUrl')['openExternalUrl']
|
||||
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const provideLocal: typeof import('@vueuse/core')['provideLocal']
|
||||
@@ -156,6 +161,7 @@ declare global {
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const shouldSilencePrivacyBlockedRequest: typeof import('./composables/usePrivacyConsent')['shouldSilencePrivacyBlockedRequest']
|
||||
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||
const syncRefs: typeof import('@vueuse/core')['syncRefs']
|
||||
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||
@@ -408,8 +414,10 @@ declare module 'vue' {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly aesDecrypt: UnwrapRef<typeof import('./utils/crypto.js')['aesDecrypt']>
|
||||
readonly aesEncrypt: UnwrapRef<typeof import('./utils/crypto.js')['aesEncrypt']>
|
||||
readonly appendAppAuthQueryParams: UnwrapRef<typeof import('./composables/useReportWebview')['appendAppAuthQueryParams']>
|
||||
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
||||
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
||||
readonly buildAgentVipApplyH5Url: UnwrapRef<typeof import('./composables/useAgentVipH5')['buildAgentVipApplyH5Url']>
|
||||
readonly calculatePromotionPricing: UnwrapRef<typeof import('./utils/promotionPricing.js')['calculatePromotionPricing']>
|
||||
readonly clearAuthStorage: UnwrapRef<typeof import('./utils/storage')['clearAuthStorage']>
|
||||
readonly clearToken: UnwrapRef<typeof import('./utils/storage')['clearToken']>
|
||||
@@ -474,6 +482,7 @@ declare module 'vue' {
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly navigateLogin: UnwrapRef<typeof import('./utils/navigate')['navigateLogin']>
|
||||
readonly navigateTo: UnwrapRef<typeof import('./utils/navigate')['navigateTo']>
|
||||
readonly navigateToAgentVipApplyH5: UnwrapRef<typeof import('./composables/useAgentVipH5')['navigateToAgentVipApplyH5']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
|
||||
@@ -518,7 +527,9 @@ declare module 'vue' {
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
|
||||
readonly openAgentVipApplyInExternalBrowser: UnwrapRef<typeof import('./composables/useAgentVipH5')['openAgentVipApplyInExternalBrowser']>
|
||||
readonly openCustomerService: UnwrapRef<typeof import('./composables/useCustomerService')['openCustomerService']>
|
||||
readonly openExternalUrl: UnwrapRef<typeof import('./composables/openExternalUrl')['openExternalUrl']>
|
||||
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
|
||||
@@ -552,6 +563,7 @@ declare module 'vue' {
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly shouldSilencePrivacyBlockedRequest: UnwrapRef<typeof import('./composables/usePrivacyConsent')['shouldSilencePrivacyBlockedRequest']>
|
||||
readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']>
|
||||
readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']>
|
||||
readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']>
|
||||
|
||||
1
src/components.d.ts
vendored
1
src/components.d.ts
vendored
@@ -12,7 +12,6 @@ declare module 'vue' {
|
||||
BindPhoneDialog: typeof import('./components/BindPhoneDialog.vue')['default']
|
||||
EmptyState: typeof import('./components/EmptyState.vue')['default']
|
||||
ImageSaveGuide: typeof import('./components/ImageSaveGuide.vue')['default']
|
||||
InquireForm: typeof import('./components/InquireForm.vue')['default']
|
||||
LoginDialog: typeof import('./components/LoginDialog.vue')['default']
|
||||
Payment: typeof import('./components/Payment.vue')['default']
|
||||
PriceInputPopup: typeof import('./components/PriceInputPopup.vue')['default']
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup>
|
||||
import { openAgentVipApplyInExternalBrowser } from '@/composables/useAgentVipH5'
|
||||
|
||||
function toAgentVip() {
|
||||
uni.navigateTo({ url: '/pages/agent-vip-apply' })
|
||||
openAgentVipApplyInExternalBrowser()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
46
src/composables/openExternalUrl.ts
Normal file
46
src/composables/openExternalUrl.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/** 在系统默认浏览器中打开链接(App 用 plus,H5 开发调试用 window.open) */
|
||||
function redactUrlForLog(url: string): string {
|
||||
try {
|
||||
const u = new URL(url)
|
||||
for (const key of ['token', 'refreshAfter', 'accessExpire']) {
|
||||
if (u.searchParams.has(key))
|
||||
u.searchParams.set(key, '***')
|
||||
}
|
||||
return u.toString()
|
||||
}
|
||||
catch {
|
||||
return '[invalid-url]'
|
||||
}
|
||||
}
|
||||
|
||||
export function openExternalUrl(url: string): boolean {
|
||||
if (!url)
|
||||
return false
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
try {
|
||||
if (typeof plus !== 'undefined' && plus.runtime?.openURL) {
|
||||
plus.runtime.openURL(url, (err: unknown) => {
|
||||
console.error('[openExternalUrl] 打开失败', err, redactUrlForLog(url))
|
||||
uni.showToast({ title: '无法打开浏览器', icon: 'none' })
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('[openExternalUrl] 异常', e)
|
||||
}
|
||||
uni.showToast({ title: '当前环境无法打开外部浏览器', icon: 'none' })
|
||||
return false
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
window.open(url, '_blank')
|
||||
return true
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS || H5
|
||||
uni.showToast({ title: '请使用 App 打开', icon: 'none' })
|
||||
return false
|
||||
// #endif
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { pages } from 'virtual:uni-pages'
|
||||
const pathToPage: Record<string, string> = {
|
||||
'/': '/pages/index',
|
||||
'/login': '/pages/login',
|
||||
'/historyQuery': '/pages/history-query',
|
||||
'/historyQuery': '/pages/index',
|
||||
'/help': '/pages/help',
|
||||
'/help/detail': '/pages/help-detail',
|
||||
'/help/guide': '/pages/help-guide',
|
||||
@@ -35,7 +35,7 @@ const nameToPage: Record<string, string> = {
|
||||
invitation: '/pages/invitation',
|
||||
promote: '/pages/promote',
|
||||
agent: '/pages/agent',
|
||||
history: '/pages/history-query',
|
||||
history: '/pages/index',
|
||||
help: '/pages/help',
|
||||
helpDetail: '/pages/help-detail',
|
||||
helpGuide: '/pages/help-guide',
|
||||
@@ -70,7 +70,7 @@ export function resolveWebToUni(to: string | { name?: string, path?: string, que
|
||||
|
||||
if (pathOnly.startsWith('/inquire/')) {
|
||||
const feature = pathOnly.replace(/^\/inquire\//, '')
|
||||
const base = `/pages/inquire?feature=${encodeURIComponent(feature)}`
|
||||
const base = `/pages/promote?feature=${encodeURIComponent(feature)}`
|
||||
return queryPart ? `${base}&${queryPart}` : base
|
||||
}
|
||||
if (pathOnly.startsWith('/agent/invitationAgentApply/')) {
|
||||
@@ -142,12 +142,31 @@ const pageMetaByRoute = new Map(
|
||||
(pages as UniPageMeta[]).map(page => [normalizeUniRoute(page.path), page]),
|
||||
)
|
||||
|
||||
function resolvePageMetaByRoute(route: string): UniPageMeta | undefined {
|
||||
const normalized = normalizeUniRoute(route)
|
||||
const candidates = [
|
||||
normalized,
|
||||
normalized.startsWith('pages/') ? normalized.slice('pages/'.length) : `pages/${normalized}`,
|
||||
]
|
||||
for (const key of candidates) {
|
||||
const meta = pageMetaByRoute.get(key)
|
||||
if (meta)
|
||||
return meta
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function getCurrentPageMeta(): UniPageMeta | undefined {
|
||||
return pageMetaByRoute.get(getCurrentUniRoute())
|
||||
return resolvePageMetaByRoute(getCurrentUniRoute())
|
||||
}
|
||||
|
||||
export function getPageTitleByRoute(route = getCurrentUniRoute()): string {
|
||||
return pageMetaByRoute.get(normalizeUniRoute(route))?.style?.navigationBarTitleText || 'BDRP'
|
||||
const meta = resolvePageMetaByRoute(route)
|
||||
const raw = meta?.style?.navigationBarTitleText
|
||||
if (typeof raw === 'string' && raw.trim())
|
||||
return raw.trim()
|
||||
const appName = import.meta.env.VITE_APP_NAME
|
||||
return (typeof appName === 'string' && appName.trim()) ? appName.trim() : 'BDRP'
|
||||
}
|
||||
|
||||
export function getLayoutPageTitle(): string {
|
||||
@@ -163,7 +182,6 @@ const UNI_TO_WEB_NOTIFY_PATHS: Record<string, string[]> = {
|
||||
'pages/agent': ['/agent'],
|
||||
'pages/me': ['/me'],
|
||||
'pages/promote': ['/agent/promote'],
|
||||
'pages/history-query': ['/historyQuery'],
|
||||
'pages/help': ['/help'],
|
||||
'pages/help-detail': ['/help/detail'],
|
||||
'pages/help-guide': ['/help/guide'],
|
||||
@@ -176,7 +194,6 @@ const UNI_TO_WEB_NOTIFY_PATHS: Record<string, string[]> = {
|
||||
'pages/agent-service-agreement': ['/agentSerivceAgreement'],
|
||||
'pages/authorization': ['/authorization'],
|
||||
'pages/payment-result': ['/payment/result'],
|
||||
'pages/inquire': ['/inquire'],
|
||||
'pages/login': ['/login'],
|
||||
'pages/invitation': ['/agent/invitation'],
|
||||
'pages/agent-promote-details': ['/agent/promoteDetails'],
|
||||
@@ -202,8 +219,8 @@ export function getWebPathsForNotification(): string[] {
|
||||
const q: Record<string, string> = { ...(page?.options || {}) }
|
||||
|
||||
// 动态路由:带参数的路径
|
||||
if (r === 'pages/inquire' && q.feature)
|
||||
return [`/inquire/${q.feature}`]
|
||||
if (r === 'pages/promote' && q.feature)
|
||||
return [`/inquire/${q.feature}`, '/agent/promote']
|
||||
if (r === 'pages/subordinate-detail' && q.id)
|
||||
return [`/agent/subordinateDetail/${q.id}`]
|
||||
if (r === 'pages/invitation-agent-apply' && q.linkIdentifier)
|
||||
|
||||
58
src/composables/useAgentVipH5.ts
Normal file
58
src/composables/useAgentVipH5.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { openExternalUrl } from '@/composables/openExternalUrl'
|
||||
import { useReportWebview } from '@/composables/useReportWebview'
|
||||
import { getToken } from '@/utils/storage'
|
||||
|
||||
const VIP_APPLY_H5_PATH = '/agent/vipApply'
|
||||
|
||||
const CONFIRM_TITLE = '前往浏览器完成升级'
|
||||
const CONFIRM_CONTENT = '代理会员开通/续费将在系统浏览器中完成,与 App 支付完全分离。是否继续打开?'
|
||||
|
||||
export function buildAgentVipApplyH5Url(params: Record<string, string> = {}) {
|
||||
const { buildSitePathUrl } = useReportWebview()
|
||||
return buildSitePathUrl(VIP_APPLY_H5_PATH, params)
|
||||
}
|
||||
|
||||
function buildTypeQuery(options?: { type?: 'vip' | 'svip' | 'VIP' | 'SVIP' }) {
|
||||
const query: Record<string, string> = {}
|
||||
const rawType = options?.type
|
||||
if (rawType) {
|
||||
const normalized = String(rawType).toUpperCase()
|
||||
if (normalized === 'VIP' || normalized === 'SVIP')
|
||||
query.type = normalized.toLowerCase()
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
/**
|
||||
* 在系统浏览器打开 H5 会员页(带 App 登录态),打开前需用户确认。
|
||||
*/
|
||||
export function openAgentVipApplyInExternalBrowser(options?: { type?: 'vip' | 'svip' | 'VIP' | 'SVIP' }) {
|
||||
if (!getToken()) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
uni.navigateTo({ url: '/pages/login' })
|
||||
return false
|
||||
}
|
||||
|
||||
const h5Url = buildAgentVipApplyH5Url(buildTypeQuery(options))
|
||||
if (!h5Url) {
|
||||
uni.showToast({ title: '未配置 H5 站点地址', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: CONFIRM_TITLE,
|
||||
content: CONFIRM_CONTENT,
|
||||
confirmText: '继续',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm)
|
||||
openExternalUrl(h5Url)
|
||||
},
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
/** @deprecated 请使用 openAgentVipApplyInExternalBrowser */
|
||||
export function navigateToAgentVipApplyH5(options?: { type?: 'vip' | 'svip' | 'VIP' | 'SVIP' }) {
|
||||
return openAgentVipApplyInExternalBrowser(options)
|
||||
}
|
||||
@@ -18,7 +18,6 @@ const HOME_URL = '/pages/index'
|
||||
*/
|
||||
const ROUTE_PERMISSION_MAP: Record<string, PagePermission> = {
|
||||
'pages/promote': { requiresAuth: true, requiresAgent: true },
|
||||
'pages/history-query': { requiresAuth: true },
|
||||
'pages/withdraw': { requiresAuth: true, requiresAgent: true },
|
||||
'pages/payment-result': { requiresAuth: true },
|
||||
'pages/report-result-webview': { requiresAuth: true },
|
||||
|
||||
@@ -3,6 +3,27 @@ import { getToken } from '@/utils/storage'
|
||||
|
||||
declare function getCurrentPages(): any[]
|
||||
|
||||
/** 与 bdrp-webview `VITE_TOKEN_VERSION` 保持一致,供外部浏览器 / WebView 带参登录 */
|
||||
function getAppTokenVersionForH5(): string {
|
||||
return String(import.meta.env.VITE_TOKEN_VERSION || '1.0')
|
||||
}
|
||||
|
||||
/** 外部浏览器打开 H5 时携带 App 登录态(须与 webview `applyAppWebViewTokenFromRoute` 对齐) */
|
||||
export function appendAppAuthQueryParams(params: Record<string, string> = {}): Record<string, string> {
|
||||
const merged: Record<string, string> = { ...params, source: 'app' }
|
||||
const token = getToken()
|
||||
if (token)
|
||||
merged.token = token
|
||||
const refreshAfter = uni.getStorageSync('refreshAfter')
|
||||
const accessExpire = uni.getStorageSync('accessExpire')
|
||||
if (refreshAfter != null && refreshAfter !== '')
|
||||
merged.refreshAfter = String(refreshAfter)
|
||||
if (accessExpire != null && accessExpire !== '')
|
||||
merged.accessExpire = String(accessExpire)
|
||||
merged.tokenVersion = getAppTokenVersionForH5()
|
||||
return merged
|
||||
}
|
||||
|
||||
/** App 端部分 WebView 无 URLSearchParams,用手动编码保证兼容 */
|
||||
function buildQueryString(params: Record<string, string>): string {
|
||||
const parts: string[] = []
|
||||
@@ -20,46 +41,37 @@ export function useReportWebview() {
|
||||
}
|
||||
|
||||
const buildReportUrl = (page: 'example' | 'report', params: Record<string, string> = {}) => {
|
||||
const token = getToken() || ''
|
||||
const base = resolveBase()
|
||||
if (!base) {
|
||||
console.warn('[report webview] 请配置 VITE_SITE_ORIGIN 或 VITE_REPORT_BASE_URL')
|
||||
return ''
|
||||
}
|
||||
const merged: Record<string, string> = { source: 'app', ...params }
|
||||
if (token)
|
||||
merged.token = token
|
||||
const merged = appendAppAuthQueryParams(params)
|
||||
const search = buildQueryString(merged)
|
||||
/** 使用 H5 无顶栏的 App 专用路由,避免 PageLayout 与 App 原生导航栏重复 */
|
||||
return `${base}/app/${page === 'example' ? 'example' : 'report'}?${search}`
|
||||
}
|
||||
|
||||
const buildReportShareUrl = (linkIdentifier: string, params: Record<string, string> = {}) => {
|
||||
const token = getToken() || ''
|
||||
const base = resolveBase()
|
||||
if (!base) {
|
||||
console.warn('[report webview] 请配置 VITE_SITE_ORIGIN 或 VITE_REPORT_BASE_URL')
|
||||
return ''
|
||||
}
|
||||
const merged: Record<string, string> = { source: 'app', ...params }
|
||||
if (token)
|
||||
merged.token = token
|
||||
const merged = appendAppAuthQueryParams(params)
|
||||
const search = buildQueryString(merged)
|
||||
const encodedLink = encodeURIComponent(linkIdentifier)
|
||||
return `${base}/report/share/${encodedLink}${search ? `?${search}` : ''}`
|
||||
}
|
||||
|
||||
const buildSitePathUrl = (path: string, params: Record<string, string> = {}) => {
|
||||
const token = getToken() || ''
|
||||
const base = resolveBase()
|
||||
if (!base) {
|
||||
console.warn('[site webview] 请配置 VITE_SITE_ORIGIN 或 VITE_REPORT_BASE_URL')
|
||||
return ''
|
||||
}
|
||||
const normalizedPath = path.startsWith('/') ? path : `/${path}`
|
||||
const merged: Record<string, string> = { source: 'app', ...params }
|
||||
if (token)
|
||||
merged.token = token
|
||||
const merged = appendAppAuthQueryParams(params)
|
||||
const search = buildQueryString(merged)
|
||||
return `${base}${normalizedPath}${search ? `?${search}` : ''}`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import {
|
||||
getCurrentUniRoute,
|
||||
getLayoutPageTitle,
|
||||
@@ -11,10 +11,13 @@ import { ensurePrivacyConsentIfNeeded } from '@/composables/usePrivacyConsent'
|
||||
import { useGlobalNotification } from '@/composables/useGlobalNotification'
|
||||
|
||||
const router = useRouter()
|
||||
const pageTitle = ref('赤眉')
|
||||
const currentRoute = ref('')
|
||||
const safeAreaTop = ref(0)
|
||||
const immersiveNavbarSolid = ref(false)
|
||||
/** 用于在 onShow 时触发标题 computed 重新读取页面栈 */
|
||||
const routeRevision = ref(0)
|
||||
const currentRoute = computed(() => {
|
||||
routeRevision.value
|
||||
return getCurrentUniRoute()
|
||||
})
|
||||
const pageTitle = computed(() => getLayoutPageTitle())
|
||||
|
||||
const {
|
||||
showPopup,
|
||||
@@ -25,24 +28,10 @@ const {
|
||||
onClosePopup,
|
||||
} = useGlobalNotification()
|
||||
|
||||
const immersiveRoutes = new Set([
|
||||
'pages/inquire',
|
||||
])
|
||||
|
||||
const isImmersivePage = computed(() => immersiveRoutes.has(currentRoute.value))
|
||||
const immersiveNavbarClass = computed(() => (
|
||||
immersiveNavbarSolid.value ? 'immersive-wd-navbar is-solid' : 'immersive-wd-navbar'
|
||||
))
|
||||
const immersiveNavbarStyle = computed(() => (
|
||||
immersiveNavbarSolid.value
|
||||
? 'background: rgba(255,255,255,0.96); box-shadow: 0 2px 12px rgba(15, 23, 42, 0.08);'
|
||||
: 'background: transparent; box-shadow: none;'
|
||||
))
|
||||
|
||||
function syncTitle() {
|
||||
currentRoute.value = getCurrentUniRoute()
|
||||
pageTitle.value = getLayoutPageTitle()
|
||||
immersiveNavbarSolid.value = false
|
||||
nextTick(() => {
|
||||
routeRevision.value += 1
|
||||
})
|
||||
}
|
||||
|
||||
function buildCurrentPageUrl() {
|
||||
@@ -60,15 +49,6 @@ function ensureAuthIfNeeded() {
|
||||
ensurePageAccessByUrl(buildCurrentPageUrl(), 'redirect')
|
||||
}
|
||||
|
||||
function getSafeAreaTop() {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
if (res.safeArea)
|
||||
safeAreaTop.value = res.safeArea.top
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function onClickOverlay() {
|
||||
onClosePopup()
|
||||
}
|
||||
@@ -77,17 +57,9 @@ function onClickLeft() {
|
||||
router.back()
|
||||
}
|
||||
|
||||
function onImmersiveNavbarChange(payload) {
|
||||
if (!payload || payload.route !== currentRoute.value || !immersiveRoutes.has(payload.route))
|
||||
return
|
||||
immersiveNavbarSolid.value = Boolean(payload.solid)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSafeAreaTop()
|
||||
syncTitle()
|
||||
loadShownNotificationKeys()
|
||||
console.log('[通知DEBUG] default layout onMounted, 即将请求通知')
|
||||
getGlobalNotify()
|
||||
})
|
||||
|
||||
@@ -95,35 +67,29 @@ onShow(() => {
|
||||
ensurePrivacyConsentIfNeeded()
|
||||
syncTitle()
|
||||
ensureAuthIfNeeded()
|
||||
console.log('[通知DEBUG] default layout onShow')
|
||||
checkNotification()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on('immersive-navbar-change', onImmersiveNavbarChange)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off('immersive-navbar-change', onImmersiveNavbarChange)
|
||||
onReady(() => {
|
||||
syncTitle()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
class="page-layout min-h-screen from-sky-100/20 to-white bg-gradient-to-b"
|
||||
:class="{ 'transparent-layout': isImmersivePage }"
|
||||
>
|
||||
<view class="page-layout min-h-screen from-sky-100/20 to-white bg-gradient-to-b">
|
||||
<wd-navbar
|
||||
v-if="isImmersivePage"
|
||||
:class="immersiveNavbarClass"
|
||||
:title="pageTitle"
|
||||
:custom-style="immersiveNavbarStyle"
|
||||
:key="currentRoute"
|
||||
safe-area-inset-top
|
||||
left-arrow
|
||||
placeholder
|
||||
fixed
|
||||
bordered
|
||||
@click-left="onClickLeft"
|
||||
/>
|
||||
<wd-navbar v-else :title="pageTitle" safe-area-inset-top left-arrow placeholder fixed @click-left="onClickLeft" />
|
||||
>
|
||||
<template #title>
|
||||
<text class="layout-navbar-title">{{ pageTitle }}</text>
|
||||
</template>
|
||||
</wd-navbar>
|
||||
<slot />
|
||||
<wd-popup v-model="showPopup" round :close-on-click-modal="true" @close="onClickOverlay">
|
||||
<view class="popup-content p-8 text-center" style="width: 85vw;">
|
||||
@@ -142,28 +108,16 @@ onUnmounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.transparent-layout {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.immersive-wd-navbar) {
|
||||
z-index: 1000;
|
||||
transition: background-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.immersive-wd-navbar .wd-navbar__title),
|
||||
:deep(.immersive-wd-navbar .wd-navbar__left),
|
||||
:deep(.immersive-wd-navbar .wd-navbar__left .wd-icon) {
|
||||
color: #fff !important;
|
||||
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.22);
|
||||
transition: color 0.2s ease, text-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.immersive-wd-navbar.is-solid .wd-navbar__title),
|
||||
:deep(.immersive-wd-navbar.is-solid .wd-navbar__left),
|
||||
:deep(.immersive-wd-navbar.is-solid .wd-navbar__left .wd-icon) {
|
||||
color: #111827 !important;
|
||||
text-shadow: none;
|
||||
:deep(.layout-navbar-title) {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #111827;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
|
||||
@@ -1,986 +0,0 @@
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { openCustomerService } from '@/composables/useCustomerService'
|
||||
import { useAgentStore } from '@/stores/agentStore'
|
||||
|
||||
definePage({ layout: 'default', auth: true })
|
||||
|
||||
/** API 与初始状态可能为 undefined/字符串,避免参与运算出现 NaN */
|
||||
function toFiniteNumber(v, fallback = 0) {
|
||||
if (v === null || v === undefined || v === '')
|
||||
return fallback
|
||||
const n = typeof v === 'number' ? v : Number(String(v).replace(/,/g, ''))
|
||||
return Number.isFinite(n) ? n : fallback
|
||||
}
|
||||
|
||||
// 获取代理状态
|
||||
const agentStore = useAgentStore()
|
||||
const { level, ExpiryTime } = storeToRefs(agentStore)
|
||||
|
||||
const selectedType = ref('vip') // 默认选择 VIP(供下方 computed 使用)
|
||||
|
||||
// 计算是否已经是VIP或SVIP
|
||||
const isVipOrSvip = computed(() => ['VIP', 'SVIP'].includes(level.value))
|
||||
const isVip = computed(() => level.value === 'VIP')
|
||||
const isSvip = computed(() => level.value === 'SVIP')
|
||||
|
||||
// 计算续费后的到期时间
|
||||
const renewalExpiryTime = computed(() => {
|
||||
if (!ExpiryTime.value)
|
||||
return '未知'
|
||||
|
||||
// 从格式化字符串中提取日期部分
|
||||
const dateStr = ExpiryTime.value.split(' ')[0] // 假设格式是 "YYYY-MM-DD HH:MM:SS"
|
||||
const parts = dateStr.split('-').map(p => Number.parseInt(p, 10))
|
||||
const [year, month, day] = parts
|
||||
if (parts.length < 3 || [year, month, day].some(n => !Number.isFinite(n)))
|
||||
return '未知'
|
||||
|
||||
// 创建日期对象并加一年
|
||||
const expiryDate = new Date(year, month - 1, day) // 月份从0开始,所以要-1
|
||||
expiryDate.setFullYear(expiryDate.getFullYear() + 1)
|
||||
|
||||
// 返回格式化的日期字符串
|
||||
return `${expiryDate.getFullYear()}-${String(expiryDate.getMonth() + 1).padStart(2, '0')}-${String(expiryDate.getDate()).padStart(2, '0')}`
|
||||
})
|
||||
|
||||
// 按钮文字 - 根据当前状态显示不同文案
|
||||
const buttonText = computed(() => {
|
||||
if (!isVipOrSvip.value)
|
||||
return '立即开通' // 非会员状态
|
||||
|
||||
if (selectedType.value === 'vip') {
|
||||
if (isVip.value)
|
||||
return '续费VIP代理' // VIP续费VIP
|
||||
return '降级不可用' // SVIP不能降级到VIP
|
||||
}
|
||||
else {
|
||||
if (isSvip.value)
|
||||
return '续费SVIP代理' // SVIP续费SVIP
|
||||
return '升级SVIP代理' // VIP升级SVIP
|
||||
}
|
||||
})
|
||||
|
||||
// 是否可以操作按钮
|
||||
const canPerformAction = computed(() => {
|
||||
// 非会员可以开通任何会员
|
||||
if (!isVipOrSvip.value)
|
||||
return true
|
||||
|
||||
// VIP不能降级到普通会员
|
||||
if (isVip.value && selectedType.value === '')
|
||||
return false
|
||||
|
||||
// SVIP不能降级到VIP
|
||||
if (isSvip.value && selectedType.value === 'vip')
|
||||
return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
// 计算按钮类名
|
||||
const buttonClass = computed(() => {
|
||||
const baseClass
|
||||
= 'w-full py-4 rounded-lg font-bold text-lg shadow-lg transform transition-transform active:scale-98 relative overflow-hidden'
|
||||
|
||||
if (!canPerformAction.value) {
|
||||
return `${baseClass} bg-gray-400 text-white cursor-not-allowed`
|
||||
}
|
||||
|
||||
if (isVip.value && selectedType.value === 'svip') {
|
||||
return `${baseClass} bg-gradient-to-r from-purple-500 to-indigo-600 text-white`
|
||||
}
|
||||
|
||||
return `${baseClass} bg-gradient-to-r from-amber-500 to-amber-600 active:from-amber-600 active:to-amber-700 text-white`
|
||||
})
|
||||
|
||||
// VIP价格配置(展示用单位;数值在拉取 API 后写入)
|
||||
const vipConfig = reactive({ priceUnit: '元' })
|
||||
|
||||
// 计算得出的收益数据
|
||||
const revenueData = computed(() => {
|
||||
const price = toFiniteNumber(vipConfig.price)
|
||||
const svipPrice = toFiniteNumber(vipConfig.svipPrice)
|
||||
const vipCommission = toFiniteNumber(vipConfig.vipCommission)
|
||||
const svipCommission = toFiniteNumber(vipConfig.svipCommission)
|
||||
const vipFloatingRate = toFiniteNumber(vipConfig.vipFloatingRate)
|
||||
const svipFloatingRate = toFiniteNumber(vipConfig.svipFloatingRate)
|
||||
const vipConversionBonus = toFiniteNumber(vipConfig.vipConversionBonus)
|
||||
const svipConversionBonus = toFiniteNumber(vipConfig.svipConversionBonus)
|
||||
const withdrawRatio = toFiniteNumber(vipConfig.withdrawRatio)
|
||||
|
||||
const baseOrders = 300 // 基础订单数
|
||||
const pricePerOrder = 50 // 每单价格
|
||||
const baseRevenue = baseOrders * pricePerOrder // 基础推广收益
|
||||
|
||||
const vipCommissionRevenue = baseOrders * vipCommission // VIP下级贡献收益
|
||||
const svipCommissionRevenue = baseOrders * svipCommission // SVIP下级贡献收益
|
||||
|
||||
const vipFloatingRevenue = 100 * 100 * (vipFloatingRate / 100) // VIP浮动收益
|
||||
const svipFloatingRevenue = 200 * 100 * (svipFloatingRate / 100) // SVIP浮动收益
|
||||
|
||||
const vipConversionRevenue = vipConversionBonus * 2 // VIP转化奖励
|
||||
const svipConversionRevenue = svipConversionBonus * 2 // SVIP转化奖励
|
||||
|
||||
const vipExtraRevenue = 3000 // VIP额外收益估计
|
||||
const svipExtraRevenue = 6000 // SVIP额外收益估计
|
||||
|
||||
// 平级提现奖励(只有SVIP才有)
|
||||
const withdrawReward = 20000 * (withdrawRatio / 100)
|
||||
|
||||
// 计算月总收益
|
||||
const vipMonthlyTotal
|
||||
= baseRevenue
|
||||
+ vipCommissionRevenue
|
||||
+ vipFloatingRevenue
|
||||
+ vipConversionRevenue
|
||||
+ vipExtraRevenue
|
||||
|
||||
const svipMonthlyTotal
|
||||
= baseRevenue
|
||||
+ svipCommissionRevenue
|
||||
+ svipFloatingRevenue
|
||||
+ withdrawReward
|
||||
+ svipConversionRevenue
|
||||
+ svipExtraRevenue
|
||||
|
||||
// 计算VIP和SVIP之间的差额
|
||||
const monthlyDifference = svipMonthlyTotal - vipMonthlyTotal
|
||||
const priceDifference = svipPrice - price
|
||||
|
||||
const vipRate = price > 0 ? Math.round(vipMonthlyTotal / price) : 0
|
||||
const svipRate = svipPrice > 0 ? Math.round(svipMonthlyTotal / svipPrice) : 0
|
||||
|
||||
// 月增收对应到「多少天回本」:避免除 0 或月差为 0 时出现 Infinity / NaN
|
||||
let recoverDays = 0
|
||||
if (priceDifference > 0 && monthlyDifference > 0) {
|
||||
const perDay = monthlyDifference / 30
|
||||
if (perDay > 0)
|
||||
recoverDays = Math.ceil(priceDifference / perDay)
|
||||
}
|
||||
|
||||
return {
|
||||
vipMonthly: Math.round(toFiniteNumber(vipMonthlyTotal)),
|
||||
svipMonthly: Math.round(toFiniteNumber(svipMonthlyTotal)),
|
||||
vipYearly: Math.round(toFiniteNumber(vipMonthlyTotal) * 12),
|
||||
svipYearly: Math.round(toFiniteNumber(svipMonthlyTotal) * 12),
|
||||
monthlyDifference: Math.round(toFiniteNumber(monthlyDifference)),
|
||||
yearlyDifference: Math.round(toFiniteNumber(monthlyDifference) * 12),
|
||||
vipRate,
|
||||
svipRate,
|
||||
priceDifference: Math.round(toFiniteNumber(priceDifference)),
|
||||
recoverDays,
|
||||
withdrawReward: Math.round(toFiniteNumber(withdrawReward)),
|
||||
}
|
||||
})
|
||||
|
||||
// 加载价格配置
|
||||
onMounted(async () => {
|
||||
// 从API获取会员配置信息
|
||||
try {
|
||||
const { data, error } = await useApiFetch('/agent/membership/info', { silent: true }).get().json()
|
||||
|
||||
if (data.value && !error.value && data.value.code === 200) {
|
||||
const configData = data.value.data
|
||||
|
||||
// 更新VIP配置
|
||||
if (configData.vip_config) {
|
||||
const vipData = configData.vip_config
|
||||
vipConfig.price = toFiniteNumber(vipData.price)
|
||||
vipConfig.vipCommission = toFiniteNumber(vipData.report_commission) // 直接显示,就是几元/单
|
||||
vipConfig.vipFloatingRate = toFiniteNumber(vipData.price_ratio) * 100 // 转换为百分比
|
||||
vipConfig.vipConversionBonus = toFiniteNumber(vipData.lower_convert_vip_reward)
|
||||
vipConfig.vipWithdrawalLimit = toFiniteNumber(vipData.exemption_amount)
|
||||
}
|
||||
|
||||
// 更新SVIP配置
|
||||
if (configData.svip_config) {
|
||||
const svipData = configData.svip_config
|
||||
vipConfig.svipPrice = toFiniteNumber(svipData.price)
|
||||
vipConfig.svipCommission = toFiniteNumber(svipData.report_commission) // 直接显示,就是几元/单
|
||||
vipConfig.svipFloatingRate = toFiniteNumber(svipData.price_ratio) * 100 // 转换为百分比
|
||||
vipConfig.withdrawRatio = toFiniteNumber(svipData.lower_withdraw_reward_ratio) * 100 // 转换为百分比
|
||||
vipConfig.svipConversionBonus = toFiniteNumber(svipData.lower_convert_svip_reward)
|
||||
vipConfig.svipWithdrawalLimit = toFiniteNumber(svipData.exemption_amount)
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error('获取会员配置信息失败', data.value?.msg || '未知错误')
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('加载会员配置信息失败', error)
|
||||
}
|
||||
})
|
||||
|
||||
const showPayment = ref(false)
|
||||
const payID = ref('')
|
||||
const payData = computed(() => ({
|
||||
product_name: `${selectedType.value === 'vip' ? 'VIP' : 'SVIP'}代理`,
|
||||
sell_price: selectedType.value === 'vip'
|
||||
? toFiniteNumber(vipConfig.price)
|
||||
: toFiniteNumber(vipConfig.svipPrice),
|
||||
}))
|
||||
|
||||
function showToast(message) {
|
||||
if (!message)
|
||||
return
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
|
||||
// 选择代理类型
|
||||
function selectType(type) {
|
||||
selectedType.value = type
|
||||
}
|
||||
|
||||
// 申请VIP或SVIP
|
||||
async function applyVip() {
|
||||
// 如果是SVIP要降级到VIP,提示不能降级
|
||||
if (isSvip.value && selectedType.value === 'vip') {
|
||||
showToast('SVIP会员不能降级到VIP会员')
|
||||
return
|
||||
}
|
||||
|
||||
if (toFiniteNumber(payData.value.sell_price) <= 0) {
|
||||
showToast('会员价格加载中,请稍后重试')
|
||||
return
|
||||
}
|
||||
|
||||
const { data, error } = await useApiFetch('/agent/membership/activate')
|
||||
.post({
|
||||
type: selectedType.value.toUpperCase(),
|
||||
})
|
||||
.json()
|
||||
|
||||
if (data.value && !error.value) {
|
||||
if (data.value.code === 200) {
|
||||
if (data.value.data.id) {
|
||||
payID.value = data.value.data.id
|
||||
showPayment.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 联系客服
|
||||
function contactService() {
|
||||
openCustomerService()
|
||||
}
|
||||
|
||||
function formatExpiryTime(expiryTimeStr) {
|
||||
if (!expiryTimeStr)
|
||||
return '未知'
|
||||
// 从格式化字符串中提取日期部分
|
||||
return expiryTimeStr.split(' ')[0] // 假设格式是 "YYYY-MM-DD HH:MM:SS"
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="agent-VIP-apply min-h-screen w-full from-amber-50 via-amber-100 to-amber-50 bg-gradient-to-b pb-24">
|
||||
<!-- 装饰元素 -->
|
||||
<view
|
||||
class="absolute right-0 top-0 h-32 w-32 rounded-bl-full from-amber-300 to-amber-500 bg-gradient-to-br opacity-20" />
|
||||
<view
|
||||
class="absolute left-0 top-40 h-16 w-16 rounded-tr-full from-amber-400 to-amber-600 bg-gradient-to-tr opacity-20" />
|
||||
<view
|
||||
class="absolute bottom-60 right-0 h-24 w-24 rounded-tl-full from-amber-300 to-amber-500 bg-gradient-to-bl opacity-20" />
|
||||
|
||||
<!-- 顶部标题区域 -->
|
||||
<view class="header relative px-4 pb-6 pt-8 text-center">
|
||||
<view
|
||||
class="absolute left-1/2 h-1 w-24 animate-pulse rounded-full from-amber-300 via-amber-500 to-amber-300 bg-gradient-to-r -top-2 -translate-x-1/2" />
|
||||
<view class="mb-1 text-3xl text-amber-800 font-bold">
|
||||
{{ isVipOrSvip ? '代理会员续费' : 'VIP代理申请' }}
|
||||
</view>
|
||||
<view class="mx-auto mt-2 max-w-xs text-sm text-amber-700">
|
||||
<template v-if="isVipOrSvip">
|
||||
您的会员有效期至 {{ formatExpiryTime(ExpiryTime) }},续费后有效期至
|
||||
{{ renewalExpiryTime }}
|
||||
</template>
|
||||
<template v-else>
|
||||
平台为疯狂推广者定制的赚买计划,助您收益<text class="text-red-500 font-bold">翻倍增升</text>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 装饰性金币图标 -->
|
||||
<view class="absolute left-4 top-6 transform -rotate-12">
|
||||
<view
|
||||
class="h-8 w-8 flex items-center justify-center rounded-full from-yellow-300 to-yellow-500 bg-gradient-to-br shadow-lg">
|
||||
<text class="text-xs text-white font-bold">¥</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="absolute right-6 top-10 rotate-12 transform">
|
||||
<view
|
||||
class="h-6 w-6 flex items-center justify-center rounded-full from-yellow-400 to-yellow-600 bg-gradient-to-br shadow-lg">
|
||||
<text class="text-xs text-white font-bold">¥</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 选择代理类型 -->
|
||||
<view class="card-container mb-8 px-4">
|
||||
<view class="transform overflow-hidden border border-amber-100 rounded-xl bg-white shadow-lg transition-all">
|
||||
<view
|
||||
class="relative overflow-hidden from-amber-500 to-amber-600 bg-gradient-to-r px-4 py-3 text-center text-white font-bold">
|
||||
<view class="relative z-10">选择代理类型</view>
|
||||
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||
<view
|
||||
class="animate-shimmer absolute left-0 top-0 h-full w-full translate-x-full transform from-transparent via-white to-transparent bg-gradient-to-r opacity-20 -skew-x-30" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex gap-4 p-6">
|
||||
<view
|
||||
class="relative flex-1 transform cursor-pointer border-2 rounded-lg p-4 text-center transition-all duration-300 hover:-translate-y-1"
|
||||
:class="[
|
||||
selectedType === 'vip'
|
||||
? 'border-amber-500 bg-amber-50 shadow-md'
|
||||
: 'border-gray-200 hover:border-amber-300',
|
||||
]" @click="selectType('vip')">
|
||||
<view class="text-xl text-amber-700 font-bold">
|
||||
VIP代理
|
||||
</view>
|
||||
<view class="mt-1 text-lg text-amber-600 font-bold">
|
||||
{{ vipConfig.price }}{{ vipConfig.priceUnit }}
|
||||
</view>
|
||||
<view class="mt-2 text-sm text-gray-600">
|
||||
标准VIP权益
|
||||
</view>
|
||||
<view v-if="selectedType === 'vip'"
|
||||
class="absolute h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br shadow-md -right-2 -top-2">
|
||||
<wd-icon name="check" custom-style="color:#fff;font-size:14px;" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="relative flex-1 transform cursor-pointer border-2 rounded-lg p-4 text-center transition-all duration-300 hover:-translate-y-1"
|
||||
:class="[
|
||||
selectedType === 'svip'
|
||||
? 'border-amber-500 bg-amber-50 shadow-md'
|
||||
: 'border-gray-200 hover:border-amber-300',
|
||||
]" @click="selectType('svip')">
|
||||
<view class="text-xl text-amber-700 font-bold">
|
||||
SVIP代理
|
||||
</view>
|
||||
<view class="mt-1 text-lg text-amber-600 font-bold">
|
||||
{{ vipConfig.svipPrice }}{{ vipConfig.priceUnit }}
|
||||
</view>
|
||||
<view class="mt-2 text-sm text-gray-600">
|
||||
超级VIP权益
|
||||
</view>
|
||||
<view v-if="selectedType === 'svip'"
|
||||
class="absolute h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br shadow-md -right-2 -top-2">
|
||||
<wd-icon name="check" custom-style="color:#fff;font-size:14px;" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 六大超值权益 -->
|
||||
<view class="card-container mb-8 px-4">
|
||||
<view class="overflow-hidden border border-amber-100 rounded-xl bg-white shadow-lg">
|
||||
<view
|
||||
class="relative overflow-hidden from-amber-500 to-amber-600 bg-gradient-to-r px-4 py-3 text-center text-white font-bold">
|
||||
<view class="relative z-10">六大超值权益</view>
|
||||
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||
<view
|
||||
class="animate-shimmer absolute left-0 top-0 h-full w-full translate-x-full transform from-transparent via-white to-transparent bg-gradient-to-r opacity-20 -skew-x-30" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="grid grid-cols-2 gap-4 p-4">
|
||||
<!-- 权益1 -->
|
||||
<view
|
||||
class="border border-amber-200 rounded-lg from-amber-50 to-amber-100 bg-gradient-to-br p-3 transition-all duration-300 hover:border-amber-300 hover:shadow-md">
|
||||
<view class="mb-2 flex items-center text-amber-800 font-bold">
|
||||
<text
|
||||
class="mr-2 h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br text-xs text-white">1</text>
|
||||
下级贡献收益
|
||||
</view>
|
||||
<text class="text-sm text-gray-600">
|
||||
下级完全收益您来定,涨多少赚多少,一单最高收益<text class="text-red-500 font-bold">10元</text>
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 权益2 -->
|
||||
<view
|
||||
class="border border-amber-200 rounded-lg from-amber-50 to-amber-100 bg-gradient-to-br p-3 transition-all duration-300 hover:border-amber-300 hover:shadow-md">
|
||||
<view class="mb-2 flex items-center text-amber-800 font-bold">
|
||||
<text
|
||||
class="mr-2 h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br text-xs text-white">2</text>
|
||||
下级提现收益
|
||||
</view>
|
||||
<text class="text-sm text-gray-600">
|
||||
下级定价标准由您定,超过标准部分收益更丰厚,一单最高多赚<text class="text-red-500 font-bold">10元</text>
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 权益3 -->
|
||||
<view
|
||||
class="border border-amber-200 rounded-lg from-amber-50 to-amber-100 bg-gradient-to-br p-3 transition-all duration-300 hover:border-amber-300 hover:shadow-md">
|
||||
<view class="mb-2 flex items-center text-amber-800 font-bold">
|
||||
<text
|
||||
class="mr-2 h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br text-xs text-white">3</text>
|
||||
转换高额奖励
|
||||
</view>
|
||||
<text class="text-sm text-gray-600">
|
||||
下级成为VIP、SVIP,高额奖励立马发放,<text class="text-red-500 font-bold">399元</text>
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 权益4 -->
|
||||
<view
|
||||
class="border border-amber-200 rounded-lg from-amber-50 to-amber-100 bg-gradient-to-br p-3 transition-all duration-300 hover:border-amber-300 hover:shadow-md">
|
||||
<view class="mb-2 flex items-center text-amber-800 font-bold">
|
||||
<text
|
||||
class="mr-2 h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br text-xs text-white">4</text>
|
||||
下级提现奖励
|
||||
</view>
|
||||
<text class="text-sm text-gray-600">
|
||||
下级成为SVIP,每次提现都奖励1%,坐享被动收入
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 权益6 -->
|
||||
<view
|
||||
class="border border-amber-200 rounded-lg from-amber-50 to-amber-100 bg-gradient-to-br p-3 transition-all duration-300 hover:border-amber-300 hover:shadow-md">
|
||||
<view class="mb-2 flex items-center text-amber-800 font-bold">
|
||||
<text
|
||||
class="mr-2 h-6 w-6 flex items-center justify-center rounded-full from-amber-500 to-amber-600 bg-gradient-to-br text-xs text-white">6</text>
|
||||
平台专项扶持
|
||||
</view>
|
||||
<text class="text-sm text-gray-600">
|
||||
一对一专属客服服务,为合作伙伴提供全方位成长赋能
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 权益对比表 -->
|
||||
<view v-if="selectedType" class="card-container mb-8 px-4">
|
||||
<view class="overflow-hidden border border-amber-100 rounded-xl bg-white shadow-lg">
|
||||
<view
|
||||
class="relative overflow-hidden from-amber-500 to-amber-600 bg-gradient-to-r px-4 py-3 text-center text-white font-bold">
|
||||
<view class="relative z-10">{{ selectedType === 'vip' ? 'VIP' : 'SVIP' }}代理权益对比</view>
|
||||
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||
<view
|
||||
class="animate-shimmer absolute left-0 top-0 h-full w-full translate-x-full transform from-transparent via-white to-transparent bg-gradient-to-r opacity-20 -skew-x-30" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="overflow-x-auto p-4">
|
||||
<table class="w-full border-collapse">
|
||||
<thead>
|
||||
<tr class="from-amber-100 to-amber-200 bg-gradient-to-r">
|
||||
<th class="border border-amber-200 p-2 text-left text-amber-800">
|
||||
权益项目
|
||||
</th>
|
||||
<th class="border border-amber-200 p-2 text-center text-amber-800">
|
||||
普通代理
|
||||
</th>
|
||||
<th class="border border-amber-200 p-2 text-center text-amber-800"
|
||||
:class="{ 'bg-amber-200': selectedType === 'vip' }">
|
||||
VIP代理
|
||||
</th>
|
||||
<th class="border border-amber-200 p-2 text-center text-amber-800"
|
||||
:class="{ 'bg-amber-200': selectedType === 'svip' }">
|
||||
SVIP代理
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
会员权益
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
普通代理<br>免费
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center font-bold" :class="{
|
||||
'text-amber-700 bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
{{ vipConfig.price }}{{ vipConfig.priceUnit }}
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center font-bold" :class="{
|
||||
'text-amber-700 bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ vipConfig.svipPrice }}{{ vipConfig.priceUnit }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级贡献收益
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
1元/单
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
{{ vipConfig.vipCommission }}元/单
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ vipConfig.svipCommission }}元/单
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
自定义设置下级成本
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
❌
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
✓
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
✓
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级价格浮动收益
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
❌
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
最高{{ vipConfig.vipFloatingRate }}%
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
最高{{ vipConfig.svipFloatingRate }}%
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级提现奖励
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
❌
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
❌
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ vipConfig.withdrawRatio }}%
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级转化奖励
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
❌
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
{{ vipConfig.vipConversionBonus }}元*10个
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ vipConfig.svipConversionBonus }}元*10个
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
提现次数额度
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
800元/次
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
{{ vipConfig.vipWithdrawalLimit }}元/次
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ vipConfig.svipWithdrawalLimit }}元/次
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
提现次数
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center">
|
||||
1次/日
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
1次/日
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
2次/日
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收益预估 -->
|
||||
<view v-if="selectedType" class="card-container mb-8 px-4">
|
||||
<view class="overflow-hidden border border-amber-100 rounded-xl bg-white shadow-lg">
|
||||
<view
|
||||
class="relative overflow-hidden from-amber-500 to-amber-600 bg-gradient-to-r px-4 py-3 text-center text-white font-bold">
|
||||
<view class="relative z-10">收益预估对比</view>
|
||||
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||
<view
|
||||
class="animate-shimmer absolute left-0 top-0 h-full w-full translate-x-full transform from-transparent via-white to-transparent bg-gradient-to-r opacity-20 -skew-x-30" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="p-4">
|
||||
<!-- 顶部收益概览 -->
|
||||
<view class="mb-6 overflow-hidden border border-amber-200 rounded-lg">
|
||||
<view class="from-amber-100 to-amber-200 bg-gradient-to-r px-4 py-2 text-center text-amber-800 font-bold">
|
||||
VIP与SVIP代理收益对比
|
||||
</view>
|
||||
<view class="grid grid-cols-2 divide-x divide-amber-200">
|
||||
<view class="p-4 text-center" :class="{ 'bg-amber-50': selectedType === 'vip' }">
|
||||
<view class="mb-1 text-sm text-gray-600">
|
||||
VIP月预计收益
|
||||
</view>
|
||||
<view class="text-xl text-amber-600 font-bold">
|
||||
{{ revenueData.vipMonthly }}元
|
||||
</view>
|
||||
<view class="mt-1 text-xs text-gray-500">
|
||||
年收益:{{ revenueData.vipYearly }}元
|
||||
</view>
|
||||
</view>
|
||||
<view class="p-4 text-center" :class="{ 'bg-amber-50': selectedType === 'svip' }">
|
||||
<view class="mb-1 text-sm text-gray-600">
|
||||
SVIP月预计收益
|
||||
</view>
|
||||
<view class="text-xl text-red-500 font-bold">
|
||||
{{ revenueData.svipMonthly }}元
|
||||
</view>
|
||||
<view class="mt-1 text-xs text-gray-500">
|
||||
年收益:{{ revenueData.svipYearly }}元
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="from-red-50 to-red-100 bg-gradient-to-r px-4 py-2 text-center text-red-600 font-medium">
|
||||
选择SVIP,相比VIP月增收益:<text class="font-bold">{{ revenueData.monthlyDifference }}元</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 详细收益表格 -->
|
||||
<view class="overflow-x-auto">
|
||||
<table class="w-full border-collapse">
|
||||
<thead>
|
||||
<tr class="from-amber-100 to-amber-200 bg-gradient-to-r">
|
||||
<th class="border border-amber-200 p-2 text-left text-amber-800">
|
||||
收益来源
|
||||
</th>
|
||||
<th class="w-1/3 border border-amber-200 p-2 text-center text-amber-800"
|
||||
:class="{ 'bg-amber-200': selectedType === 'vip' }">
|
||||
VIP代理
|
||||
</th>
|
||||
<th class="w-1/3 border border-amber-200 p-2 text-center text-amber-800"
|
||||
:class="{ 'bg-amber-200': selectedType === 'svip' }">
|
||||
SVIP代理
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
推广收益(月)
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
300单×50元=15,000元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
300单×50元=15,000元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级贡献收益(月)
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
300单×{{ vipConfig.vipCommission }}元=360元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
300单×{{ vipConfig.svipCommission }}元=450元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级价格浮动收益(月)
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
100单×100元×{{ vipConfig.vipFloatingRate }}%=500元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
200单×100元×{{ vipConfig.svipFloatingRate }}%=2,000元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级提现奖励(月)
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
-
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ revenueData.withdrawReward }}元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
下级转化奖励(月)
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
{{ vipConfig.vipConversionBonus }}元×2个=598元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
{{ vipConfig.svipConversionBonus }}元×2个=798元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="transition-colors hover:bg-amber-50">
|
||||
<td class="border border-amber-200 p-2 font-medium">
|
||||
额外业务收益(月)
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||
}">
|
||||
约3,000元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-2 text-center" :class="{
|
||||
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||
}">
|
||||
约6,000元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="from-amber-50 to-amber-100 bg-gradient-to-r font-bold">
|
||||
<td class="border border-amber-200 p-3">
|
||||
月计收益
|
||||
</td>
|
||||
<td class="border border-amber-200 p-3 text-center text-amber-700" :class="{
|
||||
'bg-amber-50 border-amber-300': selectedType === 'vip',
|
||||
}">
|
||||
{{ revenueData.vipMonthly }}元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-3 text-center text-red-500" :class="{
|
||||
'bg-amber-50 border-amber-300': selectedType === 'svip',
|
||||
}">
|
||||
{{ revenueData.svipMonthly }}元
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="from-amber-50 to-amber-100 bg-gradient-to-r font-bold">
|
||||
<td class="border border-amber-200 p-3">
|
||||
年计收益
|
||||
</td>
|
||||
<td class="border border-amber-200 p-3 text-center text-amber-700" :class="{
|
||||
'bg-amber-50 border-amber-300': selectedType === 'vip',
|
||||
}">
|
||||
{{ revenueData.vipYearly }}元
|
||||
</td>
|
||||
<td class="border border-amber-200 p-3 text-center text-red-500" :class="{
|
||||
'bg-amber-50 border-amber-300': selectedType === 'svip',
|
||||
}">
|
||||
{{ revenueData.svipYearly }}元
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</view>
|
||||
|
||||
<!-- 投资回报率 -->
|
||||
<view class="mt-6 border border-amber-200 rounded-lg from-amber-50 to-amber-100 bg-gradient-to-r p-4">
|
||||
<view class="mb-3 text-center text-amber-800 font-bold">
|
||||
投资收益分析
|
||||
</view>
|
||||
<view class="grid grid-cols-1 gap-4">
|
||||
<view class="rounded-lg bg-white p-3 shadow-sm">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex-1 border-r border-amber-100 pr-3">
|
||||
<view class="mb-1 text-center text-amber-700 font-medium">
|
||||
VIP方案
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<view class="text-sm text-amber-600">
|
||||
投资{{ vipConfig.price }}元
|
||||
</view>
|
||||
<view class="text-sm text-gray-600">
|
||||
月收益{{ revenueData.vipMonthly }}元
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-1 pl-3">
|
||||
<view class="mb-1 text-center text-red-500 font-medium">
|
||||
SVIP方案
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<view class="text-sm text-red-500">
|
||||
投资{{ vipConfig.svipPrice }}元
|
||||
</view>
|
||||
<view class="text-sm text-gray-600">
|
||||
月收益{{ revenueData.svipMonthly }}元
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 升级收益对比 -->
|
||||
<view class="rounded-lg from-red-50 to-amber-50 bg-gradient-to-r p-3 shadow-sm">
|
||||
<view class="mb-2 text-center text-red-700 font-medium">
|
||||
SVIP升级优势分析
|
||||
</view>
|
||||
<view class="flex items-center justify-center gap-3">
|
||||
<view class="text-center">
|
||||
<view class="text-sm text-gray-600">
|
||||
额外投资
|
||||
</view>
|
||||
<view class="text-red-600 font-bold">
|
||||
{{ revenueData.priceDifference }}元
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="h-6 w-6 flex flex-shrink-0 items-center justify-center rounded-full bg-red-500 text-white">
|
||||
<view class="transform -translate-y-px">
|
||||
→
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<view class="text-sm text-gray-600">
|
||||
每月额外收益
|
||||
</view>
|
||||
<view class="text-red-600 font-bold">
|
||||
{{ revenueData.monthlyDifference }}元
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="h-6 w-6 flex flex-shrink-0 items-center justify-center rounded-full bg-red-500 text-white">
|
||||
<text class="transform -translate-y-px">→</text>
|
||||
</view>
|
||||
<view class="text-center">
|
||||
<view class="text-sm text-gray-600">
|
||||
投资回收时间
|
||||
</view>
|
||||
<view class="text-red-600 font-bold">
|
||||
{{ revenueData.recoverDays }}天
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-3 text-center text-red-500 font-medium">
|
||||
额外投资{{ revenueData.priceDifference }}元,<text class="text-red-600 font-bold">年多赚{{
|
||||
revenueData.yearlyDifference }}元</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申请按钮(固定在底部) -->
|
||||
<view
|
||||
class="fixed bottom-0 left-0 right-0 z-30 from-amber-100 to-transparent bg-gradient-to-t px-4 py-3 backdrop-blur-sm">
|
||||
<view class="flex flex-col gap-2">
|
||||
<wd-button :class="buttonClass" block :disabled="!canPerformAction" @click="applyVip">
|
||||
<text class="relative z-10">{{ buttonText }}</text>
|
||||
<view
|
||||
class="animate-shimmer absolute left-0 top-0 h-full w-full translate-x-full transform from-transparent via-white to-transparent bg-gradient-to-r opacity-20 -skew-x-30" />
|
||||
</wd-button>
|
||||
<wd-button custom-class="contact-wd-btn" block plain @click="contactService">
|
||||
<view class="flex items-center justify-center text-amber-700 font-medium">
|
||||
<wd-icon name="service" custom-class="mr-1" />
|
||||
<text>联系客服咨询</text>
|
||||
</view>
|
||||
</wd-button>
|
||||
<!-- 最终解释权声明 -->
|
||||
<view class="py-1 text-center text-xs text-gray-400">
|
||||
最终解释权归戎行技术有限公司所有
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<Payment :id="payID" v-model="showPayment" :data="payData" type="agent_vip" @close="showPayment = false" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.agent-VIP-apply {
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%) skewX(-30deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(200%) skewX(-30deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-shimmer {
|
||||
animation: shimmer 3s infinite;
|
||||
}
|
||||
|
||||
.active\:scale-98:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
:deep(.contact-wd-btn.wd-button) {
|
||||
border-color: #fbbf24;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4px 10px rgba(120, 53, 15, 0.08);
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup>
|
||||
import { openCustomerService } from '@/composables/useCustomerService'
|
||||
import { openAgentVipApplyInExternalBrowser } from '@/composables/useAgentVipH5'
|
||||
|
||||
definePage({ layout: 'default', auth: true })
|
||||
function toVipApply() {
|
||||
uni.navigateTo({ url: '/pages/agent-vip-apply' })
|
||||
openAgentVipApplyInExternalBrowser()
|
||||
}
|
||||
|
||||
function toService() {
|
||||
|
||||
@@ -1,22 +1,10 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import bgIcon from '/static/images/bg_icon.png'
|
||||
import bannerImg from '/static/images/index/banner_1.png'
|
||||
import bannerImg2 from '/static/images/index/banner_2.png'
|
||||
import bannerImg3 from '/static/images/index/banner_3.png'
|
||||
import honestyBanner from '/static/images/index/banner_B.png'
|
||||
import companyIcon from '/static/images/index/company_bg.png'
|
||||
import housekeepingRiskIcon from '/static/images/index/housekeeping_risk_bg.png'
|
||||
import loanCheckIcon from '/static/images/index/loan_check_bg.png'
|
||||
import marriageRiskIcon from '/static/images/index/marriage_risk_bg.png'
|
||||
import personalDataIcon from '/static/images/index/personal_data_bg.png'
|
||||
import preLoanRiskIcon from '/static/images/index/preloan_risk_bg.png'
|
||||
import rentalInfoBg from '/static/images/index/rentalinfo_bg.png'
|
||||
import rightIcon from '/static/images/index/right.png'
|
||||
import indexPromoteIcon from '/static/images/index/tgbg.png'
|
||||
import indexMyReportIcon from '/static/images/index/wdbg.png'
|
||||
import indexInvitationIcon from '/static/images/index/yqhy.png'
|
||||
import { useAppConfig } from '@/composables/useAppConfig'
|
||||
import { ensurePrivacyConsentIfNeeded } from '@/composables/usePrivacyConsent'
|
||||
|
||||
definePage({ layout: 'home' })
|
||||
@@ -27,28 +15,6 @@ onShow(() => {
|
||||
|
||||
const banners = [bannerImg, bannerImg2, bannerImg3]
|
||||
|
||||
const services = [
|
||||
{ title: '个人大数据', name: 'personalData', bg: personalDataIcon },
|
||||
{ title: '婚恋风险', name: 'marriage', bg: marriageRiskIcon },
|
||||
{ title: '入职背调', name: 'backgroundcheck', bg: preLoanRiskIcon },
|
||||
]
|
||||
|
||||
const riskServices = [
|
||||
{ title: '小微', name: 'companyinfo', bg: companyIcon },
|
||||
{ title: '家政', name: 'homeservice', bg: housekeepingRiskIcon },
|
||||
{ title: '贷前', name: 'preloanbackgroundcheck', bg: loanCheckIcon },
|
||||
{ title: '诚信租赁', name: 'rentalinfo', bg: rentalInfoBg },
|
||||
]
|
||||
const { appConfig, loadAppConfig } = useAppConfig()
|
||||
const queryRetentionDaysText = computed(() => `${appConfig.value.query.retention_days}天`)
|
||||
void loadAppConfig()
|
||||
|
||||
function toInquire(name) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/inquire?feature=${encodeURIComponent(name)}`,
|
||||
})
|
||||
}
|
||||
|
||||
function toInvitation() {
|
||||
uni.navigateTo({ url: '/pages/invitation' })
|
||||
}
|
||||
@@ -56,112 +22,70 @@ function toInvitation() {
|
||||
function toPromote() {
|
||||
uni.navigateTo({ url: '/pages/promote' })
|
||||
}
|
||||
|
||||
function toHistory() {
|
||||
uni.navigateTo({ url: '/pages/history-query' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="box-border min-h-screen">
|
||||
<view class="relative p-4">
|
||||
<swiper class="banner-swiper overflow-hidden rounded-xl" circular autoplay :interval="3000" indicator-dots
|
||||
indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#ffffff">
|
||||
<swiper-item v-for="(item, index) in banners" :key="index">
|
||||
<view class="box-border min-h-screen bg-[#f5faff] pb-4">
|
||||
<view class="m-4 overflow-hidden rounded-2xl">
|
||||
<swiper
|
||||
class="banner-swiper w-full"
|
||||
circular
|
||||
autoplay
|
||||
:interval="3000"
|
||||
indicator-dots
|
||||
indicator-color="rgba(255,255,255,0.45)"
|
||||
indicator-active-color="#ffffff"
|
||||
>
|
||||
<swiper-item v-for="(item, index) in banners" :key="index" class="h-full w-full">
|
||||
<image :src="item" class="h-full w-full" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<view class="px-6">
|
||||
<view class="grid grid-cols-3 gap-3">
|
||||
<view class="flex flex-col items-center justify-center text-center" @click="toPromote">
|
||||
<view class="mt-2 px-4">
|
||||
<view class="mb-2 text-base text-gray-800 font-bold">
|
||||
即刻赚佣金
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="box-content h-16 w-16 flex items-center justify-center rounded-full from-white to-blue-100/10 bg-gradient-to-b p-1 shadow-lg">
|
||||
<image :src="indexPromoteIcon" class="h-12 w-12" mode="aspectFit" />
|
||||
class="card-gradient-1 mb-4 flex items-center rounded-2xl p-0 shadow-xl"
|
||||
@click="toPromote"
|
||||
>
|
||||
<view class="flex flex-1 flex-col items-start justify-center py-4 pl-4">
|
||||
<view class="mb-1 text-[20px] text-blue-700 font-bold">
|
||||
直推报告
|
||||
</view>
|
||||
<text class="mt-1 text-center font-bold">
|
||||
推广报告
|
||||
</text>
|
||||
<view class="mb-4 text-xs text-gray-700">
|
||||
选择所需报告类型,灵活定价,一键分享客户,客户下单即结算,佣金实时到账
|
||||
</view>
|
||||
<wd-button plain>
|
||||
立即推广
|
||||
</wd-button>
|
||||
</view>
|
||||
<image class="mx-4 my-4 h-20 w-20" :src="indexPromoteIcon" mode="aspectFit" />
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col items-center justify-center text-center" @click="toInvitation">
|
||||
<view
|
||||
class="box-content h-16 w-16 flex items-center justify-center rounded-full from-white to-blue-100/10 bg-gradient-to-b p-1 shadow-lg">
|
||||
<image :src="indexInvitationIcon" class="h-12 w-12" mode="aspectFit" />
|
||||
class="card-gradient-2 flex items-center rounded-2xl p-0 shadow-xl"
|
||||
@click="toInvitation"
|
||||
>
|
||||
<view class="flex flex-1 flex-col items-start justify-center py-4 pl-4">
|
||||
<view class="mb-1 text-[20px] text-teal-700 font-bold">
|
||||
邀请下级代理
|
||||
</view>
|
||||
<text class="mt-1 text-center font-bold">
|
||||
邀请下级
|
||||
</text>
|
||||
<view class="mb-4 text-xs text-gray-700">
|
||||
邀请好友成为代理,好友推广获客,客户支付即返佣,团队收益轻松到手
|
||||
</view>
|
||||
<wd-button plain>
|
||||
立即邀请
|
||||
</wd-button>
|
||||
</view>
|
||||
<image class="mx-4 my-4 h-20 w-20" :src="indexInvitationIcon" mode="aspectFit" />
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col items-center justify-center text-center" @click="toHistory">
|
||||
<view
|
||||
class="box-content h-16 w-16 flex items-center justify-center rounded-full from-white to-blue-100/10 bg-gradient-to-b p-1 shadow-lg">
|
||||
<image :src="indexMyReportIcon" class="h-12 w-12" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="mt-1 text-center font-bold">
|
||||
我的报告
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="relative p-4 pt-0">
|
||||
<view class="grid grid-cols-2 my-4 gap-4" style="grid-template-rows: repeat(2, 1fr);">
|
||||
<view v-for="(service, index) in services" :key="index"
|
||||
class="relative min-h-18 flex flex-col rounded-xl px-4 py-2 shadow-lg"
|
||||
:class="index === 0 ? 'row-span-2' : ''"
|
||||
:style="`background: url(${service.bg}) no-repeat; background-size: 100% 100%; background-position: center;`"
|
||||
@click="toInquire(service.name)">
|
||||
<view class="min-h-18 flex items-end">
|
||||
<!-- <text class="text-base text-gray-700 font-semibold">
|
||||
{{ service.title }}
|
||||
</text> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-x class="risk-scroll my-4 px-1 pb-4 pt-2 -mx-1">
|
||||
<view class="inline-flex gap-2">
|
||||
<view v-for="(service, index) in riskServices" :key="index"
|
||||
class="relative h-24 w-[107px] flex-shrink-0 rounded-xl shadow-lg"
|
||||
:style="`background: url(${service.bg}) no-repeat; background-size: 100% 100%; background-position: center;`"
|
||||
@click="toInquire(service.name)">
|
||||
<view class="h-full flex items-end px-2 py-2">
|
||||
<!-- <text class="text-sm text-gray-700 font-semibold">
|
||||
{{ service.title }}
|
||||
</text> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="mb-3 mt-6 flex items-center">
|
||||
<view class="bg-primary h-5 w-1.5 rounded-xl" />
|
||||
<text class="ml-2 text-lg text-gray-800">
|
||||
诚信专栏
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="mt-4 overflow-hidden rounded-xl bg-white shadow-xl">
|
||||
<image :src="honestyBanner" class="block w-full" mode="widthFix" />
|
||||
</view>
|
||||
|
||||
<view class="mt-4 box-border h-14 w-full flex items-center rounded-lg bg-white px-4 text-gray-700 shadow-lg"
|
||||
@click="toHistory">
|
||||
<view class="mr-4 h-full flex items-center justify-center">
|
||||
<image class="h-10 w-10" :src="bgIcon" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<view class="text-gray-800">
|
||||
我的历史查询记录
|
||||
</view>
|
||||
<view class="text-xs text-gray-500">
|
||||
查询记录有效期为{{ queryRetentionDaysText }}
|
||||
</view>
|
||||
</view>
|
||||
<image :src="rightIcon" class="h-6 w-6" mode="aspectFit" />
|
||||
<view class="relative mt-4 h-[120px] overflow-hidden rounded-2xl shadow-xl">
|
||||
<image class="absolute inset-0 h-full w-full" :src="honestyBanner" mode="aspectFill" />
|
||||
<view class="absolute inset-0 bg-black/5" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -172,7 +96,17 @@ function toHistory() {
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
.risk-scroll {
|
||||
white-space: nowrap;
|
||||
.card-gradient-1 {
|
||||
background: linear-gradient(135deg, #e3f0ff 0%, #fafdff 100%);
|
||||
box-shadow:
|
||||
0 6px 24px 0 rgba(60, 120, 255, 0.1),
|
||||
0 1.5px 4px 0 rgba(60, 120, 255, 0.08);
|
||||
}
|
||||
|
||||
.card-gradient-2 {
|
||||
background: linear-gradient(135deg, #e6f7fa 0%, #fafdff 100%);
|
||||
box-shadow:
|
||||
0 6px 24px 0 rgba(0, 200, 180, 0.1),
|
||||
0 1.5px 4px 0 rgba(0, 200, 180, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue'
|
||||
import { openCustomerService } from '@/composables/useCustomerService'
|
||||
import { openAgentVipApplyInExternalBrowser } from '@/composables/useAgentVipH5'
|
||||
import { useAgentStore } from '@/stores/agentStore'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import { useUserStore } from '@/stores/userStore'
|
||||
@@ -75,9 +76,7 @@ function maskName(name) {
|
||||
return `${name.substring(0, 3)}****${name.substring(7)}`
|
||||
}
|
||||
|
||||
function toHistory() {
|
||||
uni.navigateTo({ url: '/pages/history-query' })
|
||||
}
|
||||
|
||||
function toPromote() {
|
||||
uni.navigateTo({ url: '/pages/promote' })
|
||||
}
|
||||
@@ -127,7 +126,7 @@ function toVipConfig() {
|
||||
}
|
||||
|
||||
function toVipRenewal() {
|
||||
uni.navigateTo({ url: '/pages/agent-vip-apply' })
|
||||
openAgentVipApplyInExternalBrowser()
|
||||
}
|
||||
|
||||
function formatExpiryTime(expiryTimeStr) {
|
||||
@@ -298,17 +297,6 @@ onBeforeMount(() => {
|
||||
</view>
|
||||
<image src="/static/images/me/right.png" class="h-4 w-4" alt="右箭头" />
|
||||
</view>
|
||||
<view
|
||||
class="w-full flex items-center justify-between border-b border-gray-100 px-6 py-4 transition-colors hover:bg-blue-50"
|
||||
@click="toHistory">
|
||||
<view class="flex items-center gap-3">
|
||||
<image src="/static/images/index/wdbg.png" class="h-6 w-6" alt="我的报告" />
|
||||
<text class="text-gray-700 font-medium">
|
||||
我的报告
|
||||
</text>
|
||||
</view>
|
||||
<image src="/static/images/me/right.png" class="h-4 w-4" alt="右箭头" />
|
||||
</view>
|
||||
<view
|
||||
class="w-full flex items-center justify-between border-b border-gray-100 px-6 py-4 transition-colors hover:bg-blue-50"
|
||||
@click="toUserAgreement">
|
||||
|
||||
@@ -105,13 +105,13 @@ onShow(() => {
|
||||
</view>
|
||||
<view class="permissions">
|
||||
<view class="permission-item">
|
||||
1. 网络权限:用于连接服务、加载业务数据与提交操作请求。
|
||||
1. 网络权限:用于连接服务、加载页面与提交查询、支付等操作。
|
||||
</view>
|
||||
<view class="permission-item">
|
||||
2. 存储权限:用于缓存页面数据、保存登录状态与图片文件。
|
||||
2. 相册/存储写入:仅在您点击保存推广海报或二维码图片时,将应用内生成的图片保存到手机相册。
|
||||
</view>
|
||||
<view class="permission-item">
|
||||
3. 设备信息权限:用于运行稳定性、安全风控与异常排查。
|
||||
3. 支付相关能力:在您主动发起支付宝支付时,由支付服务完成交易(具体权限以支付 SDK 为准)。
|
||||
</view>
|
||||
</view>
|
||||
<view class="hint">
|
||||
|
||||
@@ -19,6 +19,9 @@ const formData = ref({
|
||||
clientPrice: null,
|
||||
})
|
||||
|
||||
/** 从首页报告卡片带入的 product_en,配置加载后预选 */
|
||||
const initialFeature = ref('')
|
||||
|
||||
const availableReportTypes = computed(() => {
|
||||
if (!productConfig.value?.length)
|
||||
return []
|
||||
@@ -84,6 +87,17 @@ function openPricePicker() {
|
||||
showPricePicker.value = true
|
||||
}
|
||||
|
||||
function toExampleReport() {
|
||||
const feature = formData.value.productType
|
||||
if (!feature) {
|
||||
uni.showToast({ title: '请先选择报告类型', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/report-example-webview?feature=${encodeURIComponent(feature)}`,
|
||||
})
|
||||
}
|
||||
|
||||
async function getPromoteConfig() {
|
||||
loadingConfig.value = true
|
||||
try {
|
||||
@@ -91,7 +105,10 @@ async function getPromoteConfig() {
|
||||
if (data.value && !error.value && data.value.code === 200) {
|
||||
const list = data.value.data.AgentProductConfig || []
|
||||
productConfig.value = list
|
||||
const availableType = availableReportTypes.value[0]
|
||||
const preset = initialFeature.value
|
||||
? availableReportTypes.value.find(item => item.value === initialFeature.value)
|
||||
: null
|
||||
const availableType = preset || availableReportTypes.value[0]
|
||||
if (availableType) {
|
||||
selectProductType(availableType.value)
|
||||
}
|
||||
@@ -143,6 +160,12 @@ async function generatePromotionCode() {
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
const feature = options?.feature
|
||||
if (feature)
|
||||
initialFeature.value = decodeURIComponent(String(feature))
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getPromoteConfig()
|
||||
})
|
||||
@@ -163,8 +186,14 @@ onMounted(() => {
|
||||
</view>
|
||||
<VipBanner />
|
||||
|
||||
<view class="card mb-4">
|
||||
<view class="mb-2 text-xl font-semibold">
|
||||
<view class="card mb-4 relative">
|
||||
<text
|
||||
class="absolute right-4 top-4 z-10 text-sm text-blue-600 active:opacity-70"
|
||||
@click="toExampleReport"
|
||||
>
|
||||
示例报告
|
||||
</text>
|
||||
<view class="mb-2 pr-20 text-xl font-semibold">
|
||||
生成推广码
|
||||
</view>
|
||||
<wd-form ref="promotionForm" :model="formData">
|
||||
|
||||
3
src/uni-pages.d.ts
vendored
3
src/uni-pages.d.ts
vendored
@@ -9,7 +9,6 @@ type _LocationUrl =
|
||||
"/pages/agent-promote-details" |
|
||||
"/pages/agent-rewards-details" |
|
||||
"/pages/agent-service-agreement" |
|
||||
"/pages/agent-vip-apply" |
|
||||
"/pages/agent-vip-config" |
|
||||
"/pages/agent-vip" |
|
||||
"/pages/agent" |
|
||||
@@ -18,8 +17,6 @@ type _LocationUrl =
|
||||
"/pages/help-detail" |
|
||||
"/pages/help-guide" |
|
||||
"/pages/help" |
|
||||
"/pages/history-query" |
|
||||
"/pages/inquire" |
|
||||
"/pages/invitation-agent-apply" |
|
||||
"/pages/invitation" |
|
||||
"/pages/launch" |
|
||||
|
||||
Reference in New Issue
Block a user