diff --git a/.env b/.env
index 9ea1208..48e28e9 100644
--- a/.env
+++ b/.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=赤眉
diff --git a/_backup/pages/agent-vip-apply.vue b/_backup/pages/agent-vip-apply.vue
new file mode 100644
index 0000000..2a0ea19
--- /dev/null
+++ b/_backup/pages/agent-vip-apply.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+ 无法打开会员升级页面,请检查 VITE_SITE_ORIGIN 配置
+
+
diff --git a/src/components/InquireForm.vue b/_backup/pages/components/InquireForm.vue
similarity index 100%
rename from src/components/InquireForm.vue
rename to _backup/pages/components/InquireForm.vue
diff --git a/src/pages/history-query.vue b/_backup/pages/history-query.vue
similarity index 100%
rename from src/pages/history-query.vue
rename to _backup/pages/history-query.vue
diff --git a/src/pages/inquire.vue b/_backup/pages/inquire.vue
similarity index 95%
rename from src/pages/inquire.vue
rename to _backup/pages/inquire.vue
index b73373a..682e363 100644
--- a/src/pages/inquire.vue
+++ b/_backup/pages/inquire.vue
@@ -1,6 +1,6 @@
diff --git a/src/composables/openExternalUrl.ts b/src/composables/openExternalUrl.ts
new file mode 100644
index 0000000..c42ef57
--- /dev/null
+++ b/src/composables/openExternalUrl.ts
@@ -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
+}
diff --git a/src/composables/uni-router.ts b/src/composables/uni-router.ts
index 7cd2bba..cfd0cef 100644
--- a/src/composables/uni-router.ts
+++ b/src/composables/uni-router.ts
@@ -6,7 +6,7 @@ import { pages } from 'virtual:uni-pages'
const pathToPage: Record = {
'/': '/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 = {
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 = {
'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 = {
'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 = { ...(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)
diff --git a/src/composables/useAgentVipH5.ts b/src/composables/useAgentVipH5.ts
new file mode 100644
index 0000000..0e719b3
--- /dev/null
+++ b/src/composables/useAgentVipH5.ts
@@ -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 = {}) {
+ const { buildSitePathUrl } = useReportWebview()
+ return buildSitePathUrl(VIP_APPLY_H5_PATH, params)
+}
+
+function buildTypeQuery(options?: { type?: 'vip' | 'svip' | 'VIP' | 'SVIP' }) {
+ const query: Record = {}
+ 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)
+}
diff --git a/src/composables/useNavigationAuthGuard.ts b/src/composables/useNavigationAuthGuard.ts
index 3286107..abfeabe 100644
--- a/src/composables/useNavigationAuthGuard.ts
+++ b/src/composables/useNavigationAuthGuard.ts
@@ -18,7 +18,6 @@ const HOME_URL = '/pages/index'
*/
const ROUTE_PERMISSION_MAP: Record = {
'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 },
diff --git a/src/composables/useReportWebview.ts b/src/composables/useReportWebview.ts
index da81d27..10b998e 100644
--- a/src/composables/useReportWebview.ts
+++ b/src/composables/useReportWebview.ts
@@ -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 = {}): Record {
+ const merged: Record = { ...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 {
const parts: string[] = []
@@ -20,46 +41,37 @@ export function useReportWebview() {
}
const buildReportUrl = (page: 'example' | 'report', params: Record = {}) => {
- const token = getToken() || ''
const base = resolveBase()
if (!base) {
console.warn('[report webview] 请配置 VITE_SITE_ORIGIN 或 VITE_REPORT_BASE_URL')
return ''
}
- const merged: Record = { 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 = {}) => {
- const token = getToken() || ''
const base = resolveBase()
if (!base) {
console.warn('[report webview] 请配置 VITE_SITE_ORIGIN 或 VITE_REPORT_BASE_URL')
return ''
}
- const merged: Record = { 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 = {}) => {
- 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 = { source: 'app', ...params }
- if (token)
- merged.token = token
+ const merged = appendAppAuthQueryParams(params)
const search = buildQueryString(merged)
return `${base}${normalizedPath}${search ? `?${search}` : ''}`
}
diff --git a/src/layouts/default.vue b/src/layouts/default.vue
index db18bcb..32c3db8 100644
--- a/src/layouts/default.vue
+++ b/src/layouts/default.vue
@@ -1,6 +1,6 @@
-
+
-
+ >
+
+ {{ pageTitle }}
+
+
diff --git a/src/pages/agent-vip.vue b/src/pages/agent-vip.vue
index 88881f3..385b783 100644
--- a/src/pages/agent-vip.vue
+++ b/src/pages/agent-vip.vue
@@ -1,9 +1,10 @@
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
- 推广报告
-
-
-
-
-
-
-
-
- 邀请下级
-
-
-
-
-
-
-
-
- 我的报告
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ 即刻赚佣金
-
-
-
-
-
-
+
+
+
+ 直推报告
+
+ 选择所需报告类型,灵活定价,一键分享客户,客户下单即结算,佣金实时到账
+
+
+ 立即推广
+
-
-
-
-
-
- 诚信专栏
-
+
-
-
+
+
+
+ 邀请下级代理
+
+
+ 邀请好友成为代理,好友推广获客,客户支付即返佣,团队收益轻松到手
+
+
+ 立即邀请
+
+
+
-
-
-
-
-
-
- 我的历史查询记录
-
-
- 查询记录有效期为{{ queryRetentionDaysText }}
-
-
-
+
+
+
@@ -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);
}
diff --git a/src/pages/me.vue b/src/pages/me.vue
index 92a5958..d04af07 100644
--- a/src/pages/me.vue
+++ b/src/pages/me.vue
@@ -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(() => {
-
-
-
-
- 我的报告
-
-
-
-
diff --git a/src/pages/privacy-consent.vue b/src/pages/privacy-consent.vue
index b4b912a..426e032 100644
--- a/src/pages/privacy-consent.vue
+++ b/src/pages/privacy-consent.vue
@@ -105,13 +105,13 @@ onShow(() => {
- 1. 网络权限:用于连接服务、加载业务数据与提交操作请求。
+ 1. 网络权限:用于连接服务、加载页面与提交查询、支付等操作。
- 2. 存储权限:用于缓存页面数据、保存登录状态与图片文件。
+ 2. 相册/存储写入:仅在您点击保存推广海报或二维码图片时,将应用内生成的图片保存到手机相册。
- 3. 设备信息权限:用于运行稳定性、安全风控与异常排查。
+ 3. 支付相关能力:在您主动发起支付宝支付时,由支付服务完成交易(具体权限以支付 SDK 为准)。
diff --git a/src/pages/promote.vue b/src/pages/promote.vue
index 9511459..7593664 100644
--- a/src/pages/promote.vue
+++ b/src/pages/promote.vue
@@ -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(() => {
-
-
+
+
+ 示例报告
+
+
生成推广码
diff --git a/src/uni-pages.d.ts b/src/uni-pages.d.ts
index 7e93de3..6e058bf 100644
--- a/src/uni-pages.d.ts
+++ b/src/uni-pages.d.ts
@@ -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" |