This commit is contained in:
Mrx
2026-02-11 15:16:52 +08:00
parent e84094946a
commit 3f478e2ce2
50 changed files with 1134 additions and 1051 deletions

View File

@@ -39,9 +39,8 @@
### TabBar 页面 ### TabBar 页面
1.`src/pages/index.vue` - 首页 1.`src/pages/index.vue` - 首页
2.`src/pages/promote/index.vue` - 推广 2.`src/pages/agent/index.vue` - 数据
3.`src/pages/agent/index.vue` - 数据 3.`src/pages/me/index.vue` - 我的
4.`src/pages/me/index.vue` - 我的页
### 其他页面 ### 其他页面
-`src/pages/help/index.vue` - 帮助中心 -`src/pages/help/index.vue` - 帮助中心

View File

@@ -3,13 +3,11 @@ import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
export default defineUniPages({ export default defineUniPages({
pages: [ pages: [
{ path: 'pages/index', type: 'home', layout: 'HomeLayout', style: { navigationBarTitleText: '一查查' } }, { path: 'pages/index', type: 'home', layout: 'HomeLayout', style: { navigationBarTitleText: '一查查' } },
{ path: 'pages/promote/index', layout: 'HomeLayout', style: { navigationBarTitleText: '推广' } },
{ path: 'pages/agent/index', layout: 'HomeLayout', style: { navigationBarTitleText: '数据' } }, { path: 'pages/agent/index', layout: 'HomeLayout', style: { navigationBarTitleText: '数据' } },
{ path: 'pages/me/index', layout: 'HomeLayout', style: { navigationBarTitleText: '我的' } }, { path: 'pages/me/index', layout: 'HomeLayout', style: { navigationBarTitleText: '我的' } },
{ path: 'pages/help/index', layout: 'PageLayout', style: { navigationBarTitleText: '帮助中心' } }, { path: 'pages/help/index', layout: 'PageLayout', style: { navigationBarTitleText: '帮助中心' } },
{ path: 'pages/help/detail', layout: 'PageLayout', style: { navigationBarTitleText: '帮助详情' } }, { path: 'pages/help/detail', layout: 'PageLayout', style: { navigationBarTitleText: '帮助详情' } },
{ path: 'pages/help/guide', layout: 'PageLayout', style: { navigationBarTitleText: '引导指南' } }, { path: 'pages/help/guide', layout: 'PageLayout', style: { navigationBarTitleText: '引导指南' } },
{ path: 'pages/register/index', layout: 'PageLayout', style: { navigationBarTitleText: '注册成为代理' } },
{ path: 'pages/historyQuery/index', layout: 'PageLayout', style: { navigationBarTitleText: '历史查询' } }, { path: 'pages/historyQuery/index', layout: 'PageLayout', style: { navigationBarTitleText: '历史查询' } },
{ path: 'pages/report/index', layout: 'PageLayout', style: { navigationBarTitleText: '报告详情' } }, { path: 'pages/report/index', layout: 'PageLayout', style: { navigationBarTitleText: '报告详情' } },
{ path: 'pages/service/index', layout: 'PageLayout', style: { navigationBarTitleText: '客服' } }, { path: 'pages/service/index', layout: 'PageLayout', style: { navigationBarTitleText: '客服' } },
@@ -18,10 +16,12 @@ export default defineUniPages({
{ path: 'pages/privacyPolicy/index', layout: 'PageLayout', style: { navigationBarTitleText: '隐私政策' } }, { path: 'pages/privacyPolicy/index', layout: 'PageLayout', style: { navigationBarTitleText: '隐私政策' } },
{ path: 'pages/agentManageAgreement/index', layout: 'PageLayout', style: { navigationBarTitleText: '代理管理协议' } }, { path: 'pages/agentManageAgreement/index', layout: 'PageLayout', style: { navigationBarTitleText: '代理管理协议' } },
{ path: 'pages/teamList/index', layout: 'PageLayout', style: { navigationBarTitleText: '我的团队' } }, { path: 'pages/teamList/index', layout: 'PageLayout', style: { navigationBarTitleText: '我的团队' } },
{ path: 'pages/teamList/detail', layout: 'PageLayout', style: { navigationBarTitleText: '下级详情' } },
{ path: 'pages/agentUpgrade/index', layout: 'PageLayout', style: { navigationBarTitleText: '升级代理' } }, { path: 'pages/agentUpgrade/index', layout: 'PageLayout', style: { navigationBarTitleText: '升级代理' } },
{ path: 'pages/promoteDetails/index', layout: 'PageLayout', style: { navigationBarTitleText: '我的推广收益' } }, { path: 'pages/promoteDetails/index', layout: 'PageLayout', style: { navigationBarTitleText: '我的推广收益' } },
{ path: 'pages/rewardsDetails/index', layout: 'PageLayout', style: { navigationBarTitleText: '下级推广收益' } }, { path: 'pages/rewardsDetails/index', layout: 'PageLayout', style: { navigationBarTitleText: '下级推广收益' } },
{ path: 'pages/invitation/index', layout: 'PageLayout', style: { navigationBarTitleText: '邀请下级代理' } }, { path: 'pages/invitation/index', layout: 'PageLayout', style: { navigationBarTitleText: '邀请下级代理' } },
{ path: 'pages/promote/reportList', layout: 'PageLayout', style: { navigationBarTitleText: '推广报告' } },
{ path: 'pages/promote/report', layout: 'PageLayout', style: { navigationBarTitleText: '推广报告' } }, { path: 'pages/promote/report', layout: 'PageLayout', style: { navigationBarTitleText: '推广报告' } },
{ path: 'pages/withdrawDetails/index', layout: 'PageLayout', style: { navigationBarTitleText: '提现记录' } }, { path: 'pages/withdrawDetails/index', layout: 'PageLayout', style: { navigationBarTitleText: '提现记录' } },
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
@@ -49,12 +49,6 @@ export default defineUniPages({
iconPath: '/static/homelayout/index.png', iconPath: '/static/homelayout/index.png',
selectedIconPath: '/static/homelayout/index_active.png', selectedIconPath: '/static/homelayout/index_active.png',
}, },
{
pagePath: 'pages/promote/index',
text: '推广',
iconPath: '/static/homelayout/promote.png',
selectedIconPath: '/static/homelayout/promote_active.png',
},
{ {
pagePath: 'pages/agent/index', pagePath: 'pages/agent/index',
text: '数据', text: '数据',

View File

@@ -58,7 +58,7 @@ const login = () => {
console.log(`[登录] 获取code成功${loginRetryCount}次尝试`) console.log(`[登录] 获取code成功${loginRetryCount}次尝试`)
wxminiLogin({ code }).then((result) => { wxminiLogin({ code }).then(async (result) => {
// 检查网络请求是否失败 // 检查网络请求是否失败
if (result.error.value) { if (result.error.value) {
console.error('[登录] 网络请求失败:', result.error.value) console.error('[登录] 网络请求失败:', result.error.value)
@@ -91,8 +91,19 @@ const login = () => {
console.log('[登录] 登录成功') console.log('[登录] 登录成功')
isLoggingIn = false isLoggingIn = false
loginRetryCount = 0 loginRetryCount = 0
getUser() try {
getAgentInformation() await getUser()
// 微信环境下未绑定手机号时,跳转绑定手机号页(与 BindPhoneDialog 逻辑一致)
// #ifdef MP-WEIXIN
if (!userStore.mobile) {
uni.reLaunch({ url: '/pages/auth/index' })
return
}
// #endif
getAgentInformation()
} catch (e) {
console.error('[登录] 获取用户信息失败', e)
}
} else { } else {
// 检查是否是 code 无效错误 // 检查是否是 code 无效错误
const errorMsg = result.data.value.msg || '' const errorMsg = result.data.value.msg || ''

View File

@@ -74,6 +74,14 @@ export function getTeamList(params) {
return useApiFetch(`/agent/team/list${buildQueryString(params)}`).get().json() return useApiFetch(`/agent/team/list${buildQueryString(params)}`).get().json()
} }
/**
* 获取下级贡献详情(订单/邀请列表)
* @param {object} params - { subordinate_id, page, page_size, tab_type: 'order'|'invite' }
*/
export function getSubordinateContributionDetail(params) {
return useApiFetch(`/agent/subordinate/contribution/detail${buildQueryString(params)}`).get().json()
}
export function getSubordinateList(params) { export function getSubordinateList(params) {
return useApiFetch(`/agent/subordinate/list${buildQueryString(params)}`).get().json() return useApiFetch(`/agent/subordinate/list${buildQueryString(params)}`).get().json()
} }

View File

@@ -11,20 +11,20 @@
:placeholder="pricePlaceholder" :placeholder="pricePlaceholder"
@blur="onBlurPrice" @blur="onBlurPrice"
/> />
<view class="flex justify-between mt-3 text-sm"> <view class="flex justify-between mt-2 text-sm">
<text>推广收益为</text> <text>推广收益为<text class="text-orange-500 font-medium"> {{ promotionRevenue }} </text></text>
<text class="text-orange-500 font-medium">¥ {{ promotionRevenue }}</text>
</view> </view>
<view class="flex justify-between mt-1 text-sm"> <view class="flex justify-between mt-2 text-sm">
<text>底价成本 ¥ {{ baseCost }}</text> <text>底价成本<text class="text-orange-500 font-medium"> {{ baseCost }} </text></text>
<text>提价成本 ¥ {{ raiseCost }}</text> <text>提价成本<text class="text-orange-500 font-medium"> {{ raiseCost }} </text></text>
</view> </view>
</view> </view>
<view class="rounded-xl bg-white p-4 mt-3 shadow"> <view class="rounded-xl bg-white p-4 mt-3 shadow">
<view class="text-base mb-2 text-gray-800">收益与成本说明</view> <view class="text-base mb-2 text-gray-800">收益与成本说明</view>
<text class="text-sm">推广收益 = 客户查询价 - 我的成本</text> <text class="text-sm">推广收益 = 客户查询价 - 我的成本</text>
<text class="text-sm block mt-1">我的成本 = 实际底价 + 提价成本</text> <text class="text-sm block mt-1">我的成本 = 实际底价 + 提价成本</text>
<text class="text-sm block mt-1">设定范围¥{{ productConfig?.price_range_min ?? 0 }} - ¥{{ productConfig?.price_range_max ?? 9999 }}</text> <text class="text-sm block mt-1">提价成本设置{{ productConfig?.price_threshold ?? 0 }}元以上的部分收取{{ rateFormat(productConfig?.price_fee_rate ?? 0) }}的提价成本</text>
<text class="text-sm block mt-1">设定范围<text class="text-orange-500">{{ productConfig?.price_range_min ?? 0 }}</text> - <text class="text-orange-500">{{ productConfig?.price_range_max ?? 9999 }}</text></text>
</view> </view>
<wd-button type="primary" block class="mt-4" @click="onConfirm">确认</wd-button> <wd-button type="primary" block class="mt-4" @click="onConfirm">确认</wd-button>
</view> </view>
@@ -63,6 +63,10 @@ watch(
}, },
) )
function rateFormat(rate) {
return (Number(rate) || 0) * 100 + '%'
}
function safeTruncate(num, decimals = 2) { function safeTruncate(num, decimals = 2) {
if (isNaN(num) || !isFinite(num)) return '0.00' if (isNaN(num) || !isFinite(num)) return '0.00'
const factor = 10 ** decimals const factor = 10 ** decimals

View File

@@ -0,0 +1,82 @@
/**
* 绑定手机号登录逻辑,与 ycc-proxy-webview BindPhoneDialog.vue「已有平台账号」模式保持一致
* - 发送验证码auth/sendSms actionType='bindMobile'
* - 绑定:/user/bindMobile -> 存 token -> fetchUserInfo + fetchAgentStatus -> 按 isAgent 跳转
*/
import useApiFetch from '@/composables/useApiFetch'
import { bindMobile } from '@/api/user'
import { useUserStore } from '@/stores/userStore'
import { useAgentStore } from '@/stores/agentStore'
export function useBindPhoneLogic() {
const userStore = useUserStore()
const agentStore = useAgentStore()
/**
* 发送绑定手机号验证码(与 BindPhoneDialog sendVerificationCode 一致)
* @param {string} mobile - 手机号
* @returns {Promise<{ ok: boolean, msg?: string }>}
*/
async function sendBindSms(mobile) {
const { data, error } = await useApiFetch('/auth/sendSms')
.post({ mobile, actionType: 'bindMobile' })
.json()
if (data.value && !error.value && data.value.code === 200) {
return { ok: true }
}
const msg = data.value?.msg || error.value || '发送失败'
return { ok: false, msg }
}
/**
* 提交绑定手机号并完成登录后流程(与 BindPhoneDialog handleRegister 已有账号分支一致)
* - 调用 /user/bindMobile
* - 成功:存 token、refreshAfter、accessExpire拉取用户与代理信息返回 isAgent 供跳转
* @param {string} mobile - 手机号
* @param {string} code - 6 位验证码
* @returns {Promise<{ success: boolean, isAgent?: boolean, msg?: string }>}
*/
async function submitBindMobile(mobile, code) {
const { data, error } = await bindMobile({ mobile, code })
if (!data.value || error.value) {
return { success: false, msg: error.value || '绑定失败,请重试' }
}
if (data.value.code !== 200) {
const msg = data.value.msg || '绑定失败'
if (msg.includes('已绑定其他微信号')) {
return { success: false, msg: '该手机号已绑定其他微信号,一个微信只能绑定一个手机号' }
}
return { success: false, msg }
}
const d = data.value.data
uni.setStorageSync('token', d.accessToken)
uni.setStorageSync('refreshAfter', d.refreshAfter)
uni.setStorageSync('accessExpire', d.accessExpire)
await Promise.all([
agentStore.fetchAgentStatus(),
userStore.fetchUserInfo(),
])
const isAgent = agentStore.isAgent
return { success: true, isAgent }
}
/**
* 绑定成功后按 isAgent 跳转(与 BindPhoneDialog 一致:代理 -> 数据页,否则 -> 首页)
*/
function redirectAfterBind(isAgent) {
if (isAgent) {
uni.switchTab({ url: '/pages/agent/index' })
} else {
uni.switchTab({ url: '/pages/index' })
}
}
return {
sendBindSms,
submitBindMobile,
redirectAfterBind,
}
}

View File

@@ -8,7 +8,7 @@ const APP_NAME = import.meta.env?.VITE_APP_NAME || '一查查'
export const defaultShareTitle = `${APP_NAME} - 专业大数据风控与查询服务` export const defaultShareTitle = `${APP_NAME} - 专业大数据风控与查询服务`
/** 默认分享图5:4 比例,建议 500*400不传则使用当前页截图 */ /** 默认分享图5:4 比例,建议 500*400不传则使用当前页截图 */
export const defaultShareImageUrl = '/static/index/banner.png' export const defaultShareImageUrl = '/static/index/01.jpg'
/** /**
* 获取「分享给好友」的配置 * 获取「分享给好友」的配置

View File

@@ -59,6 +59,15 @@
"navigationBarTitleText": "升级代理" "navigationBarTitleText": "升级代理"
} }
}, },
{
"path": "pages/auth/index",
"type": "page",
"layout": "PageLayout",
"style": {
"navigationBarTitleText": "绑定手机号",
"navigationStyle": "default"
}
},
{ {
"path": "pages/help/detail", "path": "pages/help/detail",
"type": "page", "type": "page",
@@ -116,15 +125,15 @@
} }
}, },
{ {
"path": "pages/promote/index", "path": "pages/promote/report",
"type": "page", "type": "page",
"layout": "HomeLayout", "layout": "PageLayout",
"style": { "style": {
"navigationBarTitleText": "推广" "navigationBarTitleText": "推广报告"
} }
}, },
{ {
"path": "pages/promote/report", "path": "pages/promote/reportList",
"type": "page", "type": "page",
"layout": "PageLayout", "layout": "PageLayout",
"style": { "style": {
@@ -139,14 +148,6 @@
"navigationBarTitleText": "我的推广收益" "navigationBarTitleText": "我的推广收益"
} }
}, },
{
"path": "pages/register/index",
"type": "page",
"layout": "PageLayout",
"style": {
"navigationBarTitleText": "注册成为代理"
}
},
{ {
"path": "pages/report/index", "path": "pages/report/index",
"type": "page", "type": "page",
@@ -171,6 +172,14 @@
"navigationBarTitleText": "客服" "navigationBarTitleText": "客服"
} }
}, },
{
"path": "pages/teamList/detail",
"type": "page",
"layout": "PageLayout",
"style": {
"navigationBarTitleText": "下级详情"
}
},
{ {
"path": "pages/teamList/index", "path": "pages/teamList/index",
"type": "page", "type": "page",
@@ -218,12 +227,6 @@
"iconPath": "/static/homelayout/index.png", "iconPath": "/static/homelayout/index.png",
"selectedIconPath": "/static/homelayout/index_active.png" "selectedIconPath": "/static/homelayout/index_active.png"
}, },
{
"pagePath": "pages/promote/index",
"text": "推广",
"iconPath": "/static/homelayout/promote.png",
"selectedIconPath": "/static/homelayout/promote_active.png"
},
{ {
"pagePath": "pages/agent/index", "pagePath": "pages/agent/index",
"text": "数据", "text": "数据",

View File

@@ -60,8 +60,8 @@ function toTeamList() {
function toRegister() { function toRegister() {
const url = mobile.value const url = mobile.value
? `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` ? `/pages/auth/index?mobile=${encodeURIComponent(mobile.value)}`
: '/pages/register/index' : '/pages/auth/index'
uni.navigateTo({ url }) uni.navigateTo({ url })
} }
@@ -136,19 +136,19 @@ function toRegister() {
</view> </view>
<view class="absolute top-3 right-3 flex p-1 rounded-2xl bg-blue-500"> <view class="absolute top-3 right-3 flex p-1 rounded-2xl bg-blue-500">
<button <button
:class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', myConversionActiveTab === 'daily' ? 'bg-white text-gray-800' : 'text-white']" :class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', myConversionActiveTab === 'daily' ? 'bg-white text-gray-900' : 'text-gray-900']"
@click="myConversionActiveTab = 'daily'" @click="myConversionActiveTab = 'daily'"
> >
</button> </button>
<button <button
:class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', myConversionActiveTab === 'weekly' ? 'bg-white text-gray-800' : 'text-white']" :class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', myConversionActiveTab === 'weekly' ? 'bg-white text-gray-900' : 'text-gray-900']"
@click="myConversionActiveTab = 'weekly'" @click="myConversionActiveTab = 'weekly'"
> >
</button> </button>
<button <button
:class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', myConversionActiveTab === 'monthly' ? 'bg-white text-gray-800' : 'text-white']" :class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', myConversionActiveTab === 'monthly' ? 'bg-white text-gray-900' : 'text-gray-900']"
@click="myConversionActiveTab = 'monthly'" @click="myConversionActiveTab = 'monthly'"
> >
@@ -228,19 +228,19 @@ function toRegister() {
</view> </view>
<view class="absolute top-3 right-3 flex p-1 rounded-2xl bg-blue-500"> <view class="absolute top-3 right-3 flex p-1 rounded-2xl bg-blue-500">
<button <button
:class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', subordinateConversionActiveTab === 'daily' ? 'bg-white text-gray-800' : 'text-white']" :class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', subordinateConversionActiveTab === 'daily' ? 'bg-white text-gray-900' : 'text-gray-900']"
@click="subordinateConversionActiveTab = 'daily'" @click="subordinateConversionActiveTab = 'daily'"
> >
</button> </button>
<button <button
:class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', subordinateConversionActiveTab === 'weekly' ? 'bg-white text-gray-800' : 'text-white']" :class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', subordinateConversionActiveTab === 'weekly' ? 'bg-white text-gray-900' : 'text-gray-900']"
@click="subordinateConversionActiveTab = 'weekly'" @click="subordinateConversionActiveTab = 'weekly'"
> >
</button> </button>
<button <button
:class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', subordinateConversionActiveTab === 'monthly' ? 'bg-white text-gray-800' : 'text-white']" :class="['px-6 py-2 text-sm font-medium rounded-lg mx-1', subordinateConversionActiveTab === 'monthly' ? 'bg-white text-gray-900' : 'text-gray-900']"
@click="subordinateConversionActiveTab = 'monthly'" @click="subordinateConversionActiveTab = 'monthly'"
> >

View File

@@ -17,7 +17,7 @@ const { isAgent, level } = storeToRefs(agentStore)
const { isLoggedIn } = storeToRefs(userStore) const { isLoggedIn } = storeToRefs(userStore)
function toRegister() { function toRegister() {
uni.navigateTo({ url: '/pages/register/index' }) uni.navigateTo({ url: '/pages/auth/index' })
} }

View File

@@ -359,7 +359,7 @@ function toService() {
function toRegister() { function toRegister() {
uni.navigateTo({ url: '/pages/register/index' }) uni.navigateTo({ url: '/pages/auth/index' })
} }
onMounted(() => { onMounted(() => {

326
src/pages/auth/index.vue Normal file
View File

@@ -0,0 +1,326 @@
<script setup>
definePage({
layout: 'PageLayout',
style: {
navigationBarTitleText: '绑定手机号',
navigationStyle: 'default',
},
})
import { ref, computed, onUnmounted, onMounted } from 'vue'
import useApiFetch from '@/composables/useApiFetch'
import { useBindPhoneLogic } from '@/composables/useBindPhoneLogic'
import { useUserStore } from '@/stores/userStore'
import { useAgentStore } from '@/stores/agentStore'
import { registerByInviteCode } from '@/api/agent'
const userStore = useUserStore()
const agentStore = useAgentStore()
const { sendBindSms, submitBindMobile, redirectAfterBind } = useBindPhoneLogic()
// 模式:'bind' 已有平台账号 | 'register' 注册成为代理
const mode = ref('bind')
const phoneNumber = ref('')
const verificationCode = ref('')
const inviteCode = ref('')
const isAgreed = ref(false)
const isCountingDown = ref(false)
const countdown = ref(60)
const submitting = ref(false)
let timer = null
onMounted(() => {
const pages = getCurrentPages()
const page = pages[pages.length - 1]
const options = page?.options || {}
if (options.invite_code) {
inviteCode.value = decodeURIComponent(options.invite_code)
mode.value = 'register'
}
if (options.mobile) {
phoneNumber.value = decodeURIComponent(options.mobile)
}
const token = uni.getStorageSync('token')
if (token && userStore.mobile) {
phoneNumber.value = userStore.mobile
// 已有手机号则默认进入注册模式
if (!options.invite_code) {
mode.value = 'register'
}
}
})
const isPhoneNumberValid = computed(() => /^1[3-9]\d{9}$/.test(phoneNumber.value))
const isInviteCodeValid = computed(() => inviteCode.value.trim().length > 0)
const canSubmitBind = computed(() =>
isPhoneNumberValid.value &&
verificationCode.value.length === 6
)
const canSubmitRegister = computed(() =>
isPhoneNumberValid.value &&
verificationCode.value.length === 6 &&
isInviteCodeValid.value &&
isAgreed.value
)
async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value) return
if (mode.value === 'register' && !isInviteCodeValid.value) {
uni.showToast({ title: '请先输入邀请码', icon: 'none' })
return
}
const actionType = mode.value === 'bind' ? 'bindMobile' : 'agentApply'
const { data, error } = await useApiFetch('/auth/sendSms')
.post({ mobile: phoneNumber.value, actionType })
.json()
if (data.value && !error.value && data.value.code === 200) {
uni.showToast({ title: '验证码已发送', icon: 'success' })
startCountdown()
} else {
uni.showToast({ title: data.value?.msg || error.value || '发送失败', icon: 'none' })
}
}
function startCountdown() {
isCountingDown.value = true
countdown.value = 60
timer = setInterval(() => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(timer)
isCountingDown.value = false
}
}, 1000)
}
async function handleBind() {
if (!canSubmitBind.value) return
submitting.value = true
try {
const { success, isAgent, msg } = await submitBindMobile(phoneNumber.value, verificationCode.value)
if (success) {
uni.showToast({ title: '绑定成功', icon: 'success' })
redirectAfterBind(isAgent)
} else {
uni.showToast({ title: msg || '绑定失败,请重试', icon: 'none' })
}
} catch (e) {
console.error(e)
uni.showToast({ title: '绑定失败,请重试', icon: 'none' })
} finally {
submitting.value = false
}
}
async function handleRegister() {
if (!isInviteCodeValid.value) {
uni.showToast({ title: '请输入邀请码', icon: 'none' })
return
}
if (!isPhoneNumberValid.value) {
uni.showToast({ title: '请输入有效的手机号', icon: 'none' })
return
}
if (verificationCode.value.length !== 6) {
uni.showToast({ title: '请输入6位验证码', icon: 'none' })
return
}
if (!isAgreed.value) {
uni.showToast({ title: '请先同意用户协议、隐私政策和代理管理协议', icon: 'none' })
return
}
submitting.value = true
try {
const { data, error } = await registerByInviteCode({
mobile: phoneNumber.value,
code: verificationCode.value,
referrer: inviteCode.value.trim(),
})
if (data.value && !error.value && data.value.code === 200) {
uni.setStorageSync('token', data.value.data.accessToken)
uni.setStorageSync('refreshAfter', data.value.data.refreshAfter)
uni.setStorageSync('accessExpire', data.value.data.accessExpire)
if (data.value.data.agent_id) {
agentStore.updateAgentInfo({
isAgent: true,
agentID: data.value.data.agent_id,
level: data.value.data.level || 1,
levelName: data.value.data.level_name || '普通代理',
})
}
uni.showToast({ title: '注册成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index' })
}, 500)
} else {
uni.showToast({ title: data.value?.msg || error.value || '注册失败,请重试', icon: 'none' })
}
} catch (err) {
console.error('注册失败:', err)
uni.showToast({ title: '注册失败,请重试', icon: 'none' })
} finally {
submitting.value = false
}
}
function toUserAgreement() {
uni.navigateTo({ url: '/pages/userAgreement/index' })
}
function toPrivacyPolicy() {
uni.navigateTo({ url: '/pages/privacyPolicy/index' })
}
function toAgentManageAgreement() {
uni.navigateTo({ url: '/pages/agentManageAgreement/index' })
}
function toIndex() {
uni.switchTab({ url: '/pages/index' })
}
onUnmounted(() => {
if (timer) {
clearInterval(timer)
timer = null
}
})
</script>
<template>
<view class="min-h-screen bg-gray-50 px-6 pt-8 pb-12">
<view class="flex flex-col items-center mb-6">
<image src="/static/logo.png" class="w-16 h-16 mb-4" mode="aspectFit" />
<view class="text-center">
<view class="text-2xl font-bold text-gray-800 mb-1">绑定 / 注册</view>
<view class="text-sm text-gray-500">已有平台账号选择绑定否则注册成为代理</view>
</view>
</view>
<!-- 模式切换 -->
<view class="flex rounded-xl overflow-hidden bg-white shadow-sm mb-6">
<view
class="flex-1 py-3 text-center text-sm font-medium"
:class="mode === 'bind' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-600'"
@click="mode = 'bind'"
>
已有平台账号
</view>
<view
class="flex-1 py-3 text-center text-sm font-medium"
:class="mode === 'register' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-600'"
@click="mode = 'register'"
>
注册成为代理
</view>
</view>
<!-- 绑定模式重要提示 -->
<view v-if="mode === 'bind'" class="bg-amber-50 border border-amber-200 rounded-xl p-4 mb-6">
<view class="flex items-start">
<text class="text-amber-600 mr-2"></text>
<view class="text-xs text-amber-800 leading-relaxed">
<text class="font-semibold">重要提示</text>
<text>一个微信只能绑定一个手机号若该手机号已绑定其他微信号将无法在此微信登录请确保输入的是您已注册的手机号</text>
</view>
</view>
</view>
<view class="space-y-4 mb-8">
<!-- 注册模式邀请码 -->
<view v-if="mode === 'register'">
<view class="text-sm text-gray-600 mb-2">邀请码</view>
<input
v-model="inviteCode"
type="text"
placeholder="请输入邀请码"
class="w-full px-4 py-3 bg-white border border-gray-200 rounded-xl text-base"
/>
</view>
<view>
<view class="text-sm text-gray-600 mb-2">手机号</view>
<input
v-model="phoneNumber"
type="number"
placeholder="请输入手机号"
maxlength="11"
class="w-full px-4 py-3 bg-white border border-gray-200 rounded-xl text-base"
/>
</view>
<view>
<view class="text-sm text-gray-600 mb-2">验证码</view>
<view class="flex gap-2">
<input
v-model="verificationCode"
type="number"
placeholder="请输入验证码"
maxlength="6"
class="flex-1 px-4 py-3 bg-white border border-gray-200 rounded-xl text-base"
/>
<button
:disabled="isCountingDown || !isPhoneNumberValid || (mode === 'register' && !isInviteCodeValid)"
:class="[
'px-4 py-3 rounded-xl text-sm font-medium whitespace-nowrap',
(isCountingDown || !isPhoneNumberValid || (mode === 'register' && !isInviteCodeValid))
? 'bg-gray-200 text-gray-400'
: 'bg-blue-500 text-white'
]"
@click="sendVerificationCode"
>
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
</button>
</view>
</view>
<!-- 注册模式协议 -->
<view v-if="mode === 'register'" class="flex items-start gap-2 pt-2">
<checkbox :checked="isAgreed" @click="isAgreed = !isAgreed" />
<view class="text-xs text-gray-500 leading-tight flex-1">
<text>我已阅读并同意</text>
<text class="text-blue-500" @click.stop="toUserAgreement">用户协议</text>
<text></text>
<text class="text-blue-500" @click.stop="toPrivacyPolicy">隐私政策</text>
<text></text>
<text class="text-blue-500" @click.stop="toAgentManageAgreement">代理管理协议</text>
</view>
</view>
</view>
<!-- 绑定模式提交 -->
<button
v-if="mode === 'bind'"
:disabled="!canSubmitBind || submitting"
:class="[
'w-full py-3 rounded-full text-base font-bold text-white',
canSubmitBind && !submitting ? 'bg-blue-500' : 'bg-gray-300 text-gray-500'
]"
@click="handleBind"
>
{{ submitting ? '提交中…' : '绑定手机号' }}
</button>
<!-- 注册模式提交 -->
<button
v-if="mode === 'register'"
:disabled="!canSubmitRegister || submitting"
:class="[
'w-full py-3 rounded-full text-base font-bold text-white',
canSubmitRegister && !submitting ? 'bg-blue-500' : 'bg-gray-300 text-gray-500'
]"
@click="handleRegister"
>
{{ submitting ? '提交中…' : '注册成为代理' }}
</button>
<view class="mt-6 text-center">
<text class="text-sm text-gray-400" @click="toIndex">暂不操作先逛逛</text>
</view>
</view>
</template>

View File

@@ -27,14 +27,14 @@ function toInvitation() {
return return
} else { } else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' }) uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` }) uni.navigateTo({ url: `/pages/auth/index?mobile=${encodeURIComponent(mobile.value)}` })
} }
return return
} }
uni.navigateTo({ url: '/pages/invitation/index' }) uni.navigateTo({ url: '/pages/invitation/index' })
} }
// 直推报告:进入推广报告页 pages/promote/report // 直推报告:进入推广报告列表页,选择报告后进入对应推广报告页
function toDirectReport() { function toDirectReport() {
if (!isAgent.value) { if (!isAgent.value) {
if (!isLoggedIn.value) { if (!isLoggedIn.value) {
@@ -42,25 +42,11 @@ function toDirectReport() {
return return
} else { } else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' }) uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` }) uni.navigateTo({ url: `/pages/auth/index?mobile=${encodeURIComponent(mobile.value)}` })
} }
return return
} }
uni.navigateTo({ url: '/pages/promote/report' }) uni.navigateTo({ url: '/pages/promote/reportList' })
}
function toPromote() {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.switchTab({ url: '/pages/promote/index' })
} }
// 一查查公众号:点击弹出二维码,长按可保存或关注 // 一查查公众号:点击弹出二维码,长按可保存或关注
@@ -73,14 +59,14 @@ function closeQrcodePopup() {
showQrcodePopup.value = false showQrcodePopup.value = false
} }
// 微信分享:分享给好友 // 微信分享:分享给好友(使用首页头图作为分享图)
onShareAppMessage(() => { onShareAppMessage(() => {
return getShareAppMessageOptions({ path: 'pages/index' }) return getShareAppMessageOptions({ path: 'pages/index', imageUrl: '/static/index/01.jpg' })
}) })
// 微信分享:分享到朋友圈(基础库 2.11.3+ // 微信分享:分享到朋友圈(使用首页头图作为分享图
onShareTimeline(() => { onShareTimeline(() => {
return getShareTimelineOptions({ query: 'from=timeline' }) return getShareTimelineOptions({ query: 'from=timeline', imageUrl: '/static/index/01.jpg' })
}) })
</script> </script>
@@ -88,7 +74,7 @@ onShareTimeline(() => {
<view class="box-border min-h-screen from-blue-100 to-white bg-gradient-to-b"> <view class="box-border min-h-screen from-blue-100 to-white bg-gradient-to-b">
<!-- 头图保留 --> <!-- 头图保留 -->
<view class="relative"> <view class="relative">
<image class="h-full w-full block" mode="widthFix" src="/static/index/n/01.jpg" /> <image class="h-full w-full block" mode="widthFix" src="/static/index/01.jpg" />
</view> </view>
<!-- 卡片一行一个文字在左图标在右 --> <!-- 卡片一行一个文字在左图标在右 -->
<view class="p-4 flex flex-col gap-4"> <view class="p-4 flex flex-col gap-4">
@@ -136,7 +122,7 @@ onShareTimeline(() => {
> >
<image <image
class="absolute inset-0 w-full h-full" class="absolute inset-0 w-full h-full"
src="/static/index/n/ycc_search.jpg" src="/static/index/ycc_search.jpg"
mode="aspectFill" mode="aspectFill"
/> />
<view class="relative z-10 text-white text-center px-4 py-2 rounded-lg bg-black/20 text-sm font-medium">关注公众号 </view> <view class="relative z-10 text-white text-center px-4 py-2 rounded-lg bg-black/20 text-sm font-medium">关注公众号 </view>

View File

@@ -13,7 +13,6 @@ import { useAgentStore } from '@/stores/agentStore'
import { useUserStore } from '@/stores/userStore' import { useUserStore } from '@/stores/userStore'
import { useEnv } from '@/composables/useEnv' import { useEnv } from '@/composables/useEnv'
import { getRevenueInfo, realNameAuth } from '@/api/agent' import { getRevenueInfo, realNameAuth } from '@/api/agent'
import { bindMobile } from '@/api/user'
import useApiFetch from '@/composables/useApiFetch' import useApiFetch from '@/composables/useApiFetch'
const agentStore = useAgentStore() const agentStore = useAgentStore()
@@ -23,13 +22,7 @@ const { userName, userAvatar, isLoggedIn, mobile } = storeToRefs(userStore)
const { isWeChat } = useEnv() const { isWeChat } = useEnv()
const revenueData = ref(null) const revenueData = ref(null)
const showWithdrawQrPopup = ref(false) const showWithdrawQrPopup = ref(false)
const showBindMobilePopup = ref(false)
const showRealNameAuthPopup = ref(false) const showRealNameAuthPopup = ref(false)
const phoneNumber = ref('')
const verificationCode = ref('')
const isCountingDown = ref(false)
const countdown = ref(60)
let countdownTimer = null
// 实名认证相关 // 实名认证相关
const realName = ref('') const realName = ref('')
@@ -152,30 +145,10 @@ function goToRebateDetail() {
uni.navigateTo({ url: '/pages/rewardsDetails/index' }) uni.navigateTo({ url: '/pages/rewardsDetails/index' })
} }
function toRegister() { function goToAuth() {
uni.navigateTo({ url: '/pages/register/index' }) uni.navigateTo({ url: '/pages/auth/index' })
} }
function openBindMobilePopup() {
showBindMobilePopup.value = true
phoneNumber.value = ''
verificationCode.value = ''
}
function closeBindMobilePopup() {
showBindMobilePopup.value = false
phoneNumber.value = ''
verificationCode.value = ''
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
isCountingDown.value = false
countdown.value = 60
}
const isPhoneNumberValid = computed(() => /^1[3-9]\d{9}$/.test(phoneNumber.value))
// 实名认证表单验证 // 实名认证表单验证
const isRealNamePhoneValid = computed(() => /^1[3-9]\d{9}$/.test(realNamePhoneNumber.value)) const isRealNamePhoneValid = computed(() => /^1[3-9]\d{9}$/.test(realNamePhoneNumber.value))
const isIdCardValid = computed(() => /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(idCard.value)) const isIdCardValid = computed(() => /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(idCard.value))
@@ -188,88 +161,6 @@ const canSubmitRealName = computed(() =>
isAgreed.value isAgreed.value
) )
async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value) return
if (!isPhoneNumberValid.value) {
uni.showToast({ title: '请输入有效的手机号', icon: 'none' })
return
}
try {
const { data, error } = await useApiFetch('/auth/sendSms')
.post({ mobile: phoneNumber.value, actionType: 'bindMobile' })
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
uni.showToast({ title: '验证码已发送', icon: 'success' })
startCountdown()
} else {
uni.showToast({ title: data.value.msg || '发送失败', icon: 'none' })
}
} else {
uni.showToast({ title: error.value || '发送失败', icon: 'none' })
}
} catch (err) {
console.error('发送验证码失败:', err)
uni.showToast({ title: '发送失败,请重试', icon: 'none' })
}
}
function startCountdown() {
isCountingDown.value = true
countdown.value = 60
countdownTimer = setInterval(() => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(countdownTimer)
countdownTimer = null
isCountingDown.value = false
}
}, 1000)
}
async function handleBindMobile() {
if (!isPhoneNumberValid.value) {
uni.showToast({ title: '请输入有效的手机号', icon: 'none' })
return
}
if (verificationCode.value.length !== 6) {
uni.showToast({ title: '请输入6位验证码', icon: 'none' })
return
}
try {
const { data, error } = await bindMobile({
mobile: phoneNumber.value,
code: verificationCode.value,
})
if (data.value && !error.value) {
if (data.value.code === 200) {
// 更新 token
uni.setStorageSync('token', data.value.data.accessToken)
uni.setStorageSync('refreshAfter', data.value.data.refreshAfter)
uni.setStorageSync('accessExpire', data.value.data.accessExpire)
// 刷新用户信息
await userStore.fetchUserInfo()
uni.showToast({ title: '绑定成功', icon: 'success' })
closeBindMobilePopup()
} else {
uni.showToast({ title: data.value.msg || '绑定失败', icon: 'none' })
}
} else {
uni.showToast({ title: error.value || '绑定失败,请重试', icon: 'none' })
}
} catch (err) {
console.error('绑定手机号失败:', err)
uni.showToast({ title: '绑定失败,请重试', icon: 'none' })
}
}
// 实名认证 - 发送验证码 // 实名认证 - 发送验证码
async function sendRealNameVerificationCode() { async function sendRealNameVerificationCode() {
if (isRealNameCountingDown.value || !isRealNamePhoneValid.value) return if (isRealNameCountingDown.value || !isRealNamePhoneValid.value) return
@@ -390,10 +281,6 @@ onMounted(() => {
}) })
onUnmounted(() => { onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
if (realNameCountdownTimer) { if (realNameCountdownTimer) {
clearInterval(realNameCountdownTimer) clearInterval(realNameCountdownTimer)
realNameCountdownTimer = null realNameCountdownTimer = null
@@ -427,21 +314,18 @@ onUnmounted(() => {
</view> </view>
</view> </view>
</view> </view>
<view class="flex-1 space-y-1"> <view class="flex-1 min-w-0 space-y-1 overflow-hidden">
<view class="text-2xl font-bold text-gray-800"> <view class="text-2xl font-bold text-gray-800 truncate">
{{ !isLoggedIn ? '点击登录' : mobile ? mobile : isWeChat ? '微信用户' : '未绑定手机号' }} {{ !isLoggedIn ? '点击登录' : mobile ? mobile : isWeChat ? '微信用户' : '未绑定手机号' }}
</view> </view>
<view v-if="isLoggedIn && !mobile && isWeChat" class="text-sm text-blue-500" @click.stop="openBindMobilePopup"> <view v-if="isLoggedIn && (!mobile || !isAgent) && isWeChat" class="text-sm text-blue-500" @click.stop="goToAuth">
绑定手机号 绑定手机号
</view> </view>
<view v-if="isLoggedIn && mobile && !isAgent" class="text-sm text-blue-500" @click.stop="toRegister">
点击申请成为代理
</view>
<view v-if="isAgent" class="font-bold" :class="levelGradient.text">ID: {{ agentCode }}</view> <view v-if="isAgent" class="font-bold" :class="levelGradient.text">ID: {{ agentCode }}</view>
</view> </view>
<view v-if="isAgent" class="text-right"> <view v-if="isAgent" class="shrink-0 text-right ml-2">
<view class="text-sm mb-1 text-gray-500">余额</view> <view class="text-sm mb-1 text-gray-500">余额</view>
<view class="text-2xl font-bold text-blue-500">¥ {{ (revenueData?.balance || 0).toFixed(2) }}</view> <view class="text-2xl font-bold text-blue-500 whitespace-nowrap">¥ {{ (revenueData?.balance || 0).toFixed(2) }}</view>
</view> </view>
</view> </view>
<template v-if="isAgent"> <template v-if="isAgent">
@@ -606,64 +490,6 @@ onUnmounted(() => {
</view> </view>
</view> </view>
<!-- 绑定手机号弹窗 -->
<view v-if="showBindMobilePopup" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="closeBindMobilePopup">
<view class="mx-4 w-full max-w-sm rounded-2xl bg-white p-6 shadow-xl" @click.stop>
<view class="text-center text-lg font-bold text-gray-800 mb-4">绑定手机号</view>
<view class="mb-4">
<view class="text-sm text-gray-600 mb-2">手机号</view>
<input
v-model="phoneNumber"
type="number"
placeholder="请输入手机号"
maxlength="11"
class="w-full px-4 py-3 border border-gray-300 rounded-lg text-base focus:border-blue-500 focus:outline-none"
/>
</view>
<view class="mb-4">
<view class="text-sm text-gray-600 mb-2">验证码</view>
<view class="flex gap-2">
<input
v-model="verificationCode"
type="number"
placeholder="请输入验证码"
maxlength="6"
class="flex-1 px-4 py-3 border border-gray-300 rounded-lg text-base focus:border-blue-500 focus:outline-none"
/>
<button
:disabled="isCountingDown || !isPhoneNumberValid"
:class="[
'px-4 py-3 rounded-lg text-sm font-medium whitespace-nowrap',
isCountingDown || !isPhoneNumberValid
? 'bg-gray-200 text-gray-400'
: 'bg-blue-500 text-white'
]"
@click="sendVerificationCode"
>
{{ isCountingDown ? `${countdown}` : '获取验证码' }}
</button>
</view>
</view>
<view class="flex gap-3">
<button
class="flex-1 rounded-lg py-3 text-base font-medium text-gray-700 bg-gray-100"
@click="closeBindMobilePopup"
>
取消
</button>
<button
class="flex-1 rounded-lg py-3 text-base font-medium text-white bg-blue-500"
@click="handleBindMobile"
>
确定
</button>
</view>
</view>
</view>
<!-- 实名认证弹窗 --> <!-- 实名认证弹窗 -->
<view v-if="showRealNameAuthPopup" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="closeRealNameAuthPopup"> <view v-if="showRealNameAuthPopup" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" @click.self="closeRealNameAuthPopup">
<view class="mx-4 w-full max-w-sm rounded-2xl bg-white p-6 shadow-xl max-h-[90vh] overflow-y-auto" @click.stop> <view class="mx-4 w-full max-w-sm rounded-2xl bg-white p-6 shadow-xl max-h-[90vh] overflow-y-auto" @click.stop>

View File

@@ -1,292 +0,0 @@
<script setup>
definePage({
layout: 'HomeLayout',
style: {
navigationBarTitleText: '推广',
navigationStyle: 'custom',
},
})
import { ref, computed, onMounted, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import SectionTitle from '@/components/SectionTitle.vue'
import { useAgentStore } from '@/stores/agentStore'
import { useUserStore } from '@/stores/userStore'
import { getProductConfig } from '@/api/agent'
import { getShareAppMessageOptions, getShareTimelineOptions } from '@/composables/useWechatShare'
const agentStore = useAgentStore()
const userStore = useUserStore()
const { isAgent } = storeToRefs(agentStore)
const { isLoggedIn, mobile } = storeToRefs(userStore)
function toInquire(name) {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.navigateTo({ url: `/pages/promote/report?feature=${encodeURIComponent(name)}` })
}
function toInvitation() {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.navigateTo({ url: '/pages/invitation/index' })
}
function toPromote() {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.navigateTo({ url: '/pages/promote/report' })
}
function toHelp() {
uni.navigateTo({ url: '/pages/agentSystemGuide/index' })
}
function toHistory() {
uni.navigateTo({ url: '/pages/historyQuery/index' })
}
function toAgent() {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.switchTab({ url: '/pages/agent/index' })
}
function toWithdraw() {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.navigateTo({ url: '/pages/withdraw/index' })
}
function toTeamList() {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
} else {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/register/index?mobile=${encodeURIComponent(mobile.value)}` })
}
return
}
uni.navigateTo({ url: '/pages/teamList/index' })
}
function toService() {
uni.navigateTo({ url: '/pages/service/index' })
}
const services = ref([
{ name: 'riskassessment', title: '个人大数据', subtitle: '个人信用 精准查询', bg: '/static/promote/personal_data_bg.png', goColor: '#6699ff', costPrice: null },
{ name: 'marriage', title: '情侣报告', subtitle: '相信才能相依 相爱才能永久', bg: '/static/promote/marriage_risk_bg.png', goColor: '#ff99cc', costPrice: null },
{ name: 'backgroundcheck', title: '入职背调', subtitle: '查询便可 慧眼识英雄', bg: '/static/promote/backgroundcheck_bg.png', goColor: '#7db3ff', costPrice: null },
{ name: 'companyinfo', title: '企业大数据', subtitle: '信任是合作 永恒的基石', bg: '/static/promote/company_bg.png', goColor: '#ffaa66', costPrice: null },
{ name: 'homeservice', title: '家政报告', subtitle: '口碑与能力 一查便知', bg: '/static/promote/housekeeping_risk_bg.png', goColor: '#66cccc', costPrice: null },
{ name: 'consumerFinanceReport', title: '消金报告', subtitle: '', bg: '/static/promote/consumer_finance_report_bg.png', goColor: '#a259ff', costPrice: null },
])
const allServices = computed(() => services.value)
function getCostPriceText(service) {
if (isLoggedIn.value && isAgent.value && service.costPrice) return `成本价 ${service.costPrice}`
if (!isLoggedIn.value) return '登录查看'
if (!isAgent.value) return '成为代理查看'
return '成本价 --'
}
async function fetchProductConfig() {
const token = typeof uni !== 'undefined' && uni.getStorageSync ? uni.getStorageSync('token') : ''
if (!token) return
try {
const { data, error } = await getProductConfig()
if (data.value && !error.value && data.value.code === 200) {
const list = data.value.data?.list || []
services.value.forEach((s) => {
const config = list.find((item) => item.product_en === s.name)
if (config?.actual_base_price != null) s.costPrice = parseFloat(config.actual_base_price).toFixed(2)
})
}
} catch (e) {
console.error(e)
}
}
onMounted(() => {})
watch([isLoggedIn, isAgent], ([loggedIn, agent]) => {
if (loggedIn && agent) fetchProductConfig()
}, { immediate: true })
// 推广页头图(单图,已去掉轮播)
const bannerImage = '/static/promote/banner_1.png'
// 微信分享:分享给好友
onShareAppMessage(() => {
return getShareAppMessageOptions({ path: 'pages/promote/index' })
})
// 微信分享:分享到朋友圈
onShareTimeline(() => {
return getShareTimelineOptions({ query: 'from=timeline' })
})
</script>
<template>
<view class="box-border from-blue-100 to-white bg-gradient-to-b">
<view class="relative" @click="toPromote">
<image :src="bannerImage" class="w-full block" mode="widthFix" />
</view>
<view class="px-6 mt-4">
<view class="grid grid-cols-4 gap-3">
<view class="text-center flex flex-col justify-center items-center" @click="toPromote">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/tgbg.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">推广报告</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toInvitation">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/yqxj.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">邀请下级</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toHelp">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/bzzx.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">帮助中心</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toHistory">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/wdbg.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">我的报告</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toAgent">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/zc.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">资产</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toWithdraw">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/wytx.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">我要提现</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toTeamList">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/wdxj.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">我的团队</view>
</view>
<view class="text-center flex flex-col justify-center items-center" @click="toService">
<view class="h-14 w-14 bg-gradient-to-b from-white to-blue-100/10 rounded-full shadow-lg flex items-center justify-center">
<image src="/static/promote/zxkf.png" class="h-14 w-14" mode="aspectFit" />
</view>
<view class="text-center mt-1 font-bold text-sm">在线客服</view>
</view>
</view>
</view>
<view class="my-2 mx-4 rounded-xl overflow-hidden shadow-xl" @click="toInvitation">
<image src="/static/promote/tghb.png" class="w-full block" mode="widthFix" />
</view>
<view class="flex items-center justify-between mx-4 mb-2">
<SectionTitle title="推广服务" />
<view class="text-xs text-gray-500 flex items-center gap-1.5">
<text></text>
<text>滑动查看更多</text>
<text></text>
</view>
</view>
<view class="relative p-4 pt-0">
<scroll-view scroll-x class="services-scroll-container" :show-scrollbar="false">
<view class="services-scroll-wrapper">
<view
v-for="(service, index) in allServices"
:key="index"
class="relative flex flex-col px-4 py-2 rounded-xl shadow service-card"
@click="toInquire(service.name)"
>
<image
:src="service.bg"
class="absolute inset-0 w-full h-full rounded-xl"
mode="aspectFill"
/>
<view class="relative z-10 flex flex-col items-start flex-1">
<view class="mt-1 text-left text-gray-600 font-bold">{{ service.title }}</view>
<view
class="mt-2 rounded-lg px-2 py-1 text-xs text-white w-max flex items-center"
:style="`background-color: ${service.goColor}`"
>
立即推广
<image src="/static/index/go_icon.png" class="ml-0.5 h-3 w-3 inline-block" mode="aspectFit" />
</view>
</view>
<view class="absolute bottom-0 left-0 right-0 z-10 rounded-b-xl px-2 py-1 text-xs text-white text-center">
{{ getCostPriceText(service) }}
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<style scoped>
.services-scroll-container {
white-space: nowrap;
padding-bottom: 0.5rem;
}
.services-scroll-wrapper {
display: inline-flex;
gap: 1rem;
padding-right: 1rem;
}
.service-card {
width: 120px;
height: 120px;
flex-shrink: 0;
display: inline-block;
}
</style>

View File

@@ -0,0 +1,198 @@
<script setup>
definePage({
layout: 'PageLayout',
style: {
navigationBarTitleText: '推广报告',
navigationStyle: 'default',
},
})
import { storeToRefs } from 'pinia'
import SectionTitle from '@/components/SectionTitle.vue'
import { useAgentStore } from '@/stores/agentStore'
import { useUserStore } from '@/stores/userStore'
const agentStore = useAgentStore()
const userStore = useUserStore()
const { isAgent } = storeToRefs(agentStore)
const { isLoggedIn, mobile } = storeToRefs(userStore)
// 参考 PromotePage 布局:左列个人大数据(2.5)+情侣报告(1.5),右列四张小卡片
const personalDataService = {
name: 'riskassessment',
title: '个人大数据',
subtitle: '个人风险 精准查询',
bg: '/static/index/05.png',
goColor: '#6699ff',
}
const marriageService = {
name: 'marriage',
title: '情侣报告',
subtitle: '相信才能相依 相爱才能永久',
bg: '/static/index/06.png',
goColor: '#ff99cc',
}
// 右列四张:企业大数据、消金报告、家政报告、入职背调
const group2 = [
{ name: 'companyinfo', title: '企业大数据', subtitle: '信任是合作 永恒的基石', bg: '/static/index/07.png', goColor: '#ffaa66' },
{ name: 'consumerFinanceReport', title: '消金报告', subtitle: '', bg: '/static/index/08.png', goColor: '#a259ff', showGo: true },
{ name: 'homeservice', title: '家政报告', subtitle: '口碑与能力 一查便知', bg: '/static/index/09.png', goColor: '#66cccc' },
{ name: 'backgroundcheck', title: '入职背调', subtitle: '查询便可 慧眼识英雄', bg: '/static/index/10.png', goColor: '#7db3ff' },
]
const formatSubtitle = (subtitle) => {
if (!subtitle) return ''
const parts = subtitle.split(/\s+/)
if (parts.length >= 2) {
const mid = Math.ceil(parts.length / 2)
const firstLine = parts.slice(0, mid).join(' ')
const secondLine = parts.slice(mid).join(' ')
return `${firstLine}\n${secondLine}`
}
return subtitle
}
function toReport(feature) {
if (!isAgent.value) {
if (!isLoggedIn.value) {
uni.showToast({ title: '正在登录中,请稍候', icon: 'none' })
return
}
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/auth/index?mobile=${encodeURIComponent(mobile.value)}` })
return
}
uni.navigateTo({ url: `/pages/promote/report?feature=${encodeURIComponent(feature)}` })
}
function toAgent() {
if (!isAgent.value && isLoggedIn.value) {
uni.showToast({ title: '请先注册成为代理', icon: 'none' })
uni.navigateTo({ url: `/pages/auth/index?mobile=${encodeURIComponent(mobile.value)}` })
return
}
uni.switchTab({ url: '/pages/index' })
}
function toAgentSystemGuide() {
uni.navigateTo({ url: '/pages/agentSystemGuide/index' })
}
function toHistory() {
uni.navigateTo({ url: '/pages/historyQuery/index' })
}
function toCooperation() {
// #ifdef H5
window.location.href = 'https://www.tianyuandata.com'
// #endif
// #ifndef H5
uni.navigateTo({ url: '/pages/service/index' })
// #endif
}
</script>
<template>
<view class="box-border min-h-screen from-blue-100 to-white bg-gradient-to-b">
<view class="relative">
<image class="h-full w-full block" mode="widthFix" src="/static/index/banner_1.png" />
</view>
<view class="px-4 mt-2">
<SectionTitle title="推广报告" />
</view>
<view class="relative p-4 pt-0">
<!-- 参考 PromotePage左列大卡片(个人大数据+情侣报告)右列四张小卡片 -->
<view class="flex flex-row gap-3 my-4">
<!-- 左侧个人大数据(2.5) + 情侣报告(1.5) -->
<view class="flex-1 flex flex-col gap-3">
<view
class="relative flex-[2.5] rounded-lg overflow-hidden shadow-md"
@click="toReport(personalDataService.name)"
>
<image :src="personalDataService.bg" class="absolute inset-0 w-full h-full" mode="aspectFill" />
<view class="relative z-10 flex flex-col items-start flex-1 px-4 py-2 min-h-40">
<view class="mt-1 text-gray-600 font-bold text-lg">{{ personalDataService.title }}</view>
<view
class="text-left text-sm text-gray-500 leading-relaxed whitespace-pre-line mt-1"
style="max-width: calc(100% - 1rem)"
>
{{ formatSubtitle(personalDataService.subtitle) }}
</view>
<view
class="mt-2 rounded-lg px-2 py-1 text-sm text-white shadow-xl flex items-center"
:style="`background-color: ${personalDataService.goColor}`"
>
GO
<text class="ml-1"></text>
</view>
</view>
</view>
<view
class="relative flex-[1.5] rounded-lg overflow-hidden shadow-md"
@click="toReport(marriageService.name)"
>
<image :src="marriageService.bg" class="absolute inset-0 w-full h-full" mode="aspectFill" />
<view class="relative z-10 flex flex-col items-start flex-1 px-3 py-2 min-h-24">
<view class="mt-1 text-gray-600 font-bold text-lg">{{ marriageService.title }}</view>
<view
class="text-left text-sm text-gray-500 leading-relaxed whitespace-pre-line mt-1"
style="max-width: calc(100% - 1rem)"
>
{{ formatSubtitle(marriageService.subtitle) }}
</view>
</view>
</view>
</view>
<!-- 右侧四张小卡片 -->
<view class="flex-1 flex flex-col gap-3">
<view
v-for="item in group2"
:key="item.name"
class="relative rounded-lg overflow-hidden shadow-md min-h-28 flex flex-col px-3 py-2"
@click="toReport(item.name)"
>
<image :src="item.bg" class="absolute inset-0 w-full h-full" mode="aspectFill" />
<view class="relative z-10 flex flex-col items-start flex-1">
<view class="mt-1 text-gray-600 font-bold text-lg">{{ item.title }}</view>
<view
v-if="item.subtitle"
class="text-left text-sm text-gray-500 leading-relaxed whitespace-pre-line mt-1"
style="max-width: calc(100% - 1rem)"
>
{{ formatSubtitle(item.subtitle) }}
</view>
<view
v-if="item.showGo"
class="mt-1 rounded-lg px-1.5 py-1 text-xs text-white shadow-xl flex items-center"
:style="`background-color: ${item.goColor}`"
>
GO
<text class="ml-0.5"></text>
</view>
</view>
</view>
</view>
</view>
<!-- 我的历史查询记录 -->
<view
class="mt-4 box-border h-14 w-full flex items-center rounded-lg shadow-lg bg-white text-gray-700 px-4"
@click="toHistory"
>
<image class="w-10 h-10 mr-4" src="/static/promote/wdbg.png" mode="aspectFit" />
<view class="flex-1">
<view class="text-gray-800">我的历史查询记录</view>
<view class="text-xs text-gray-500">查询记录有效期为30天</view>
</view>
<text class="text-gray-400 text-xl"></text>
</view>
</view>
</view>
</template>
<style scoped>
/* 卡片点击效果 */
view[class*="rounded-lg"]:active {
opacity: 0.9;
}
</style>

View File

@@ -1,424 +0,0 @@
<script setup>
definePage({
layout: 'PageLayout',
style: {
navigationBarTitleText: '注册成为代理',
navigationStyle: 'default',
},
})
import { ref, computed, onUnmounted, onMounted } from 'vue'
import { registerByInviteCode, applyForAgent } from '@/api/agent'
import { useAgentStore } from '@/stores/agentStore'
import { useUserStore } from '@/stores/userStore'
import useApiFetch from '@/composables/useApiFetch'
const agentStore = useAgentStore()
const userStore = useUserStore()
const appName = import.meta.env?.VITE_APP_NAME || '全能查'
const phoneNumber = ref('')
const verificationCode = ref('')
const inviteCode = ref('')
const isAgreed = ref(false)
const isCountingDown = ref(false)
const countdown = ref(60)
const isPhoneDisabled = ref(false)
let timer = null
function fillInviteCode() {
inviteCode.value = '16800'
}
onMounted(async () => {
const pages = getCurrentPages()
const page = pages[pages.length - 1]
const options = page?.options || {}
if (options.invite_code) {
inviteCode.value = decodeURIComponent(options.invite_code)
}
if (options.mobile) {
phoneNumber.value = decodeURIComponent(options.mobile)
isPhoneDisabled.value = true
} else {
const token = uni.getStorageSync('token')
if (token && userStore.mobile) {
phoneNumber.value = userStore.mobile
isPhoneDisabled.value = true
} else if (token) {
try {
await userStore.fetchUserInfo()
if (userStore.mobile) {
phoneNumber.value = userStore.mobile
isPhoneDisabled.value = true
}
} catch (e) {
console.error(e)
}
}
}
})
const isPhoneNumberValid = computed(() => /^1[3-9]\d{9}$/.test(phoneNumber.value))
const isInviteCodeValid = computed(() => inviteCode.value.trim().length > 0)
const canRegister = computed(() =>
isPhoneNumberValid.value &&
verificationCode.value.length === 6 &&
isInviteCodeValid.value &&
isAgreed.value
)
async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value) return
if (!isPhoneNumberValid.value) {
uni.showToast({ title: '请输入有效的手机号', icon: 'none' })
return
}
if (!isInviteCodeValid.value) {
uni.showToast({ title: '请先输入邀请码', icon: 'none' })
return
}
const { data, error } = await useApiFetch('auth/sendSms')
.post({ mobile: phoneNumber.value, actionType: 'agentApply' })
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
uni.showToast({ title: '获取成功', icon: 'success' })
startCountdown()
} else {
uni.showToast({ title: data.value.msg || '获取失败', icon: 'none' })
}
} else {
uni.showToast({ title: error.value || '获取失败', icon: 'none' })
}
}
function startCountdown() {
isCountingDown.value = true
countdown.value = 60
timer = setInterval(() => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(timer)
isCountingDown.value = false
}
}, 1000)
}
async function handleRegister() {
if (!isPhoneNumberValid.value) {
uni.showToast({ title: '请输入有效的手机号', icon: 'none' })
return
}
if (!isInviteCodeValid.value) {
uni.showToast({ title: '请输入邀请码', icon: 'none' })
return
}
if (verificationCode.value.length !== 6) {
uni.showToast({ title: '请输入6位验证码', icon: 'none' })
return
}
if (!isAgreed.value) {
uni.showToast({ title: '请先同意用户协议、隐私政策和代理管理协议', icon: 'none' })
return
}
await performRegister()
}
async function performRegister() {
try {
const { data, error } = await registerByInviteCode({
mobile: phoneNumber.value,
code: verificationCode.value,
referrer: inviteCode.value.trim(),
})
if (data.value && !error.value) {
if (data.value.code === 200) {
uni.setStorageSync('token', data.value.data.accessToken)
uni.setStorageSync('refreshAfter', data.value.data.refreshAfter)
uni.setStorageSync('accessExpire', data.value.data.accessExpire)
if (data.value.data.agent_id) {
agentStore.updateAgentInfo({
isAgent: true,
agentID: data.value.data.agent_id,
level: data.value.data.level || 1,
levelName: data.value.data.level_name || '普通代理',
})
}
uni.showToast({ title: '注册成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index' })
}, 500)
} else {
uni.showToast({ title: data.value.msg || '注册失败', icon: 'none' })
}
} else {
uni.showToast({ title: error.value || '注册失败,请重试', icon: 'none' })
}
} catch (err) {
console.error('注册失败:', err)
uni.showToast({ title: '注册失败,请重试', icon: 'none' })
}
}
function toUserAgreement() {
uni.navigateTo({ url: '/pages/userAgreement/index' })
}
function toPrivacyPolicy() {
uni.navigateTo({ url: '/pages/privacyPolicy/index' })
}
function toAgentManageAgreement() {
uni.navigateTo({ url: '/pages/agentManageAgreement/index' })
}
onUnmounted(() => {
if (timer) clearInterval(timer)
})
function onClickLeft() {
uni.navigateBack()
}
</script>
<template>
<view class="login-layout">
<image
class="login-bg-image"
src="/static/login/login_bg.png"
mode="aspectFill"
/>
<view class="login px-4 relative z-10" style="padding-top: 44px;">
<view class="mb-8 pt-20 text-left">
<view class="flex flex-col items-center">
<image class="h-16 w-16 rounded-full shadow block" src="/static/login/logo.png" mode="aspectFit" />
<image class="h-12 mt-4 block" src="/static/login/logo_title.png" mode="widthFix" />
</view>
</view>
<view class="login-form">
<view class="form-item">
<view class="form-label">邀请码</view>
<view class="input-with-btn flex-1">
<input
v-model="inviteCode"
class="form-input"
type="text"
placeholder="请输入邀请码"
/>
<view class="get-invite-code-btn" @click="fillInviteCode">获取邀请码</view>
</view>
</view>
<view class="form-item">
<view class="form-label">手机号</view>
<input
v-model="phoneNumber"
class="form-input flex-1"
type="number"
placeholder="请输入手机号"
maxlength="11"
:disabled="isPhoneDisabled"
/>
</view>
<view class="form-item">
<view class="form-label">验证码</view>
<view class="verification-input-wrapper flex-1">
<input
v-model="verificationCode"
class="form-input verification-input"
type="number"
placeholder="请输入验证码"
maxlength="6"
/>
<view
class="get-code-btn"
:class="{ disabled: isCountingDown || !isPhoneNumberValid || !isInviteCodeValid }"
@click="sendVerificationCode"
>
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
</view>
</view>
</view>
<view class="agreement-wrapper">
<wd-checkbox v-model="isAgreed" />
<view class="agreement-text">
我已阅读并同意
<text class="agreement-link" @click.stop="toUserAgreement">用户协议</text>
<text class="agreement-link" @click.stop="toPrivacyPolicy">隐私政策</text>
<text class="agreement-link" @click.stop="toAgentManageAgreement">代理管理协议</text>
</view>
</view>
<view class="notice-text">
未注册手机号注册后将自动生成账号并成为代理并且代表您已阅读并同意
</view>
<button
class="login-btn"
:class="{ disabled: !canRegister }"
:disabled="!canRegister"
@click="handleRegister"
>
注册成为代理
</button>
</view>
</view>
</view>
</template>
<style scoped>
.login-layout {
min-height: 100vh;
position: relative;
overflow: hidden;
}
.login-bg-image {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.login-form {
background-color: #fff;
padding: 2rem;
margin-top: 0.5rem;
box-shadow: 0 0 24px rgba(63, 63, 63, 0.06);
border-radius: 8px;
}
.form-item {
margin-bottom: 1.5rem;
display: flex;
align-items: center;
border: none;
border-bottom: 1px solid #ebedf0;
}
.input-with-btn {
position: relative;
display: flex;
align-items: center;
}
.input-with-btn .form-input {
padding-right: 5rem;
}
.get-invite-code-btn {
position: absolute;
right: 0;
color: #1989fa;
font-size: 14px;
padding: 0.5rem;
font-weight: 500;
}
.form-label {
font-size: 15px;
color: #323233;
margin-right: 1rem;
font-weight: 500;
min-width: 4rem;
flex-shrink: 0;
}
.form-input {
width: 100%;
padding: 14px 0;
font-size: 15px;
color: #323233;
background-color: transparent;
}
.form-input:disabled {
color: #969799;
background-color: transparent;
}
.verification-input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.verification-input {
flex: 1;
padding-right: 5rem;
}
.get-code-btn {
position: absolute;
right: 0;
color: #1989fa;
font-size: 14px;
padding: 0.5rem;
font-weight: 500;
}
.get-code-btn.disabled {
color: #c8c9cc;
}
.agreement-wrapper {
display: flex;
align-items: flex-start;
margin-top: 1.5rem;
margin-bottom: 1rem;
}
.agreement-wrapper :deep(.wd-checkbox) {
margin-right: 8px;
flex-shrink: 0;
}
.agreement-text {
font-size: 12px;
color: #646566;
line-height: 1.4;
}
.agreement-link {
color: #1989fa;
}
.notice-text {
font-size: 11px;
color: #969799;
line-height: 1.5;
margin-bottom: 2rem;
}
.login-btn {
width: 100%;
padding: 14px;
background-color: #1989fa;
color: #fff;
border: none;
border-radius: 24px;
font-size: 16px;
font-weight: 500;
letter-spacing: 0.25em;
}
.login-btn.disabled {
background-color: #c8c9cc;
}
</style>

View File

@@ -0,0 +1,297 @@
<script setup>
definePage({
layout: 'PageLayout',
style: {
navigationBarTitleText: '下级详情',
navigationStyle: 'default',
},
})
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getSubordinateContributionDetail } from '@/api/agent'
const subordinateId = ref('')
const loading = ref(false)
const refreshing = ref(false)
const finished = ref(false)
const page = ref(1)
const pageSize = 8
const activeTab = ref(0) // 0=order, 1=invite
const userInfo = ref({})
const orderStats = ref({})
const rebateStats = ref({})
const inviteStats = ref({})
const orderList = ref([])
const inviteList = ref([])
const initialized = ref(false)
const tabType = () => (activeTab.value === 0 ? 'order' : 'invite')
const fetchDetail = async () => {
if (!subordinateId.value) return
if (loading.value) return
if (finished.value && page.value > 1) return
loading.value = true
try {
const { data, error } = await getSubordinateContributionDetail({
subordinate_id: subordinateId.value,
page: page.value,
page_size: pageSize,
tab_type: tabType(),
})
if (data.value && !error.value && data.value.code === 200) {
const res = data.value.data
if (page.value === 1) {
userInfo.value = {
createTime: res.create_time,
level: res.level_name || '普通',
mobile: res.mobile,
}
orderStats.value = res.order_stats || {}
rebateStats.value = res.rebate_stats || {}
inviteStats.value = res.invite_stats || {}
}
if (tabType() === 'order') {
const items = res.order_list || []
if (page.value === 1) {
orderList.value = items
} else {
orderList.value = orderList.value.concat(items)
}
finished.value = items.length < pageSize
} else {
const items = res.invite_list || []
if (page.value === 1) {
inviteList.value = items
} else {
inviteList.value = inviteList.value.concat(items)
}
finished.value = items.length < pageSize
}
if (!finished.value) page.value++
} else {
finished.value = true
}
initialized.value = true
} finally {
loading.value = false
refreshing.value = false
}
}
const switchTab = () => {
if (!initialized.value) return
page.value = 1
finished.value = false
fetchDetail()
}
const onRefresh = () => {
refreshing.value = true
page.value = 1
finished.value = false
fetchDetail()
}
const onLoadMore = () => {
if (!finished.value && !loading.value) {
fetchDetail()
}
}
const getLevelClass = (level) => {
const n = typeof level === 'number' ? level : parseInt(level)
switch (n) {
case 3:
return 'bg-purple-100 text-purple-600'
case 2:
return 'bg-yellow-100 text-yellow-600'
default:
return 'bg-gray-100 text-gray-600'
}
}
const getLevelName = (level) => {
const n = typeof level === 'number' ? level : parseInt(level)
const map = { 1: '普通', 2: '黄金', 3: '钻石' }
return map[n] || '普通'
}
const formatTime = (str) => {
if (!str) return '-'
return String(str).split(' ')[0]
}
const formatNumber = (num) => {
if (num == null || num === '') return '0.00'
return Number(num).toFixed(2)
}
onLoad((options) => {
subordinateId.value = options?.id || ''
if (subordinateId.value) fetchDetail()
})
</script>
<template>
<view class="min-h-screen bg-gray-50">
<view v-if="!subordinateId" class="p-8 text-center text-gray-500">参数错误</view>
<template v-else>
<!-- 用户信息 -->
<view class="p-4">
<view class="rounded-xl bg-white shadow-sm p-5 mb-4">
<view class="flex items-center flex-wrap gap-2 mb-2">
<text class="text-lg font-semibold text-gray-800">{{ userInfo.mobile }}</text>
<text class="px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-600">
{{ userInfo.level }}代理
</text>
</view>
<view class="text-sm text-gray-500">成为下级代理时间{{ formatTime(userInfo.createTime) }}</view>
</view>
<!-- 订单统计 -->
<view class="rounded-xl bg-white shadow-sm p-4 mb-4">
<view class="text-base font-medium text-gray-800 mb-3">订单统计仅统计有返佣的订单</view>
<view class="grid grid-cols-3 gap-3">
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">总订单量</view>
<view class="text-xl font-semibold text-blue-600">{{ orderStats.total_orders || 0 }}</view>
</view>
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">月订单</view>
<view class="text-xl font-semibold text-green-600">{{ orderStats.month_orders || 0 }}</view>
</view>
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">今日订单</view>
<view class="text-xl font-semibold text-orange-600">{{ orderStats.today_orders || 0 }}</view>
</view>
</view>
</view>
<!-- 返佣统计 -->
<view class="rounded-xl bg-white shadow-sm p-4 mb-4">
<view class="text-base font-medium text-gray-800 mb-3">返佣统计</view>
<view class="grid grid-cols-3 gap-3">
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">总返佣金额</view>
<view class="text-xl font-semibold text-blue-600">¥{{ formatNumber(rebateStats.total_rebate_amount) }}</view>
</view>
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">月返佣金额</view>
<view class="text-xl font-semibold text-green-600">¥{{ formatNumber(rebateStats.month_rebate_amount) }}</view>
</view>
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">今日返佣金额</view>
<view class="text-xl font-semibold text-orange-600">¥{{ formatNumber(rebateStats.today_rebate_amount) }}</view>
</view>
</view>
</view>
<!-- 邀请统计 -->
<view class="rounded-xl bg-white shadow-sm p-4 mb-4">
<view class="text-base font-medium text-gray-800 mb-3">邀请统计</view>
<view class="grid grid-cols-3 gap-3">
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">总邀请</view>
<view class="text-xl font-semibold text-blue-600">{{ inviteStats.total_invites || 0 }}</view>
</view>
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">月邀请</view>
<view class="text-xl font-semibold text-green-600">{{ inviteStats.month_invites || 0 }}</view>
</view>
<view class="text-center">
<view class="text-gray-500 text-sm mb-1">今日邀请</view>
<view class="text-xl font-semibold text-orange-600">{{ inviteStats.today_invites || 0 }}</view>
</view>
</view>
</view>
<!-- Tab -->
<view class="rounded-xl bg-white shadow-sm overflow-hidden mb-4">
<wd-tabs v-model="activeTab" sticky @change="switchTab">
<wd-tab title="订单列表">
<scroll-view
scroll-y
class="tab-scroll"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
>
<view class="p-4">
<view v-if="orderList.length === 0" class="text-center text-gray-500 py-8">暂无订单记录</view>
<view
v-else
v-for="item in orderList"
:key="item.order_no"
class="border-b border-gray-200 pb-3 mb-3"
>
<view class="flex justify-between">
<view class="flex-1">
<view class="font-medium text-gray-800 mb-1">{{ item.product_name || '未知产品' }}</view>
<view class="text-xs text-gray-500 mb-1">订单号{{ item.order_no }}</view>
<view class="text-xs text-gray-500">{{ formatTime(item.create_time) }}</view>
</view>
<view class="text-right ml-4">
<view class="text-sm text-gray-500 mb-1">订单金额</view>
<view class="text-base font-semibold text-blue-600 mb-2">¥{{ formatNumber(item.order_amount) }}</view>
<view class="text-sm text-gray-500 mb-1">返佣金额</view>
<view class="text-base font-semibold text-green-600">¥{{ formatNumber(item.rebate_amount) }}</view>
</view>
</view>
</view>
<view v-if="loading && orderList.length > 0" class="py-3 text-center text-gray-400 text-sm">加载中</view>
<view v-else-if="finished && orderList.length > 0" class="py-3 text-center text-gray-400 text-sm">没有更多了</view>
</view>
</scroll-view>
</wd-tab>
<wd-tab title="邀请列表">
<scroll-view
scroll-y
class="tab-scroll"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
@scrolltolower="onLoadMore"
>
<view class="p-4">
<view v-if="inviteList.length === 0" class="text-center text-gray-500 py-8">暂无邀请记录</view>
<view
v-else
v-for="item in inviteList"
:key="item.agent_id"
class="border-b border-gray-200 pb-3 mb-3"
>
<view class="flex justify-between items-center">
<view class="flex items-center flex-wrap gap-2 flex-1">
<text class="text-base font-semibold text-gray-800">{{ item.mobile }}</text>
<text :class="['px-3 py-1 rounded-full text-sm font-medium', getLevelClass(item.level)]">
{{ item.level_name || getLevelName(item.level) }}代理
</text>
</view>
<view class="text-xs text-gray-500">{{ formatTime(item.create_time) }}</view>
</view>
</view>
<view v-if="loading && inviteList.length > 0" class="py-3 text-center text-gray-400 text-sm">加载中</view>
<view v-else-if="finished && inviteList.length > 0" class="py-3 text-center text-gray-400 text-sm">没有更多了</view>
</view>
</scroll-view>
</wd-tab>
</wd-tabs>
</view>
</view>
</template>
</view>
</template>
<style scoped>
.tab-scroll {
height: 400px;
}
</style>

View File

@@ -69,14 +69,38 @@ function loadMore() {
loadList(false) loadList(false)
} }
function toRegister() { function toRegister() {
uni.navigateTo({ url: '/pages/register/index' }) uni.navigateTo({ url: '/pages/auth/index' })
} }
function levelName(level) { function getLevelName(level) {
const map = { 1: '普通代理', 2: '黄金代理', 3: '钻石代理' } const levelNum = typeof level === 'number' ? level : parseInt(level)
return map[level] || '代理' const map = { 1: '普通', 2: '黄金', 3: '钻石' }
return map[levelNum] || '普通'
}
function getLevelClass(level) {
const levelNum = typeof level === 'number' ? level : parseInt(level)
switch (levelNum) {
case 3:
return 'bg-purple-100 text-purple-600'
case 2:
return 'bg-yellow-100 text-yellow-600'
default:
return 'bg-gray-100 text-gray-600'
}
}
function formatCount(num) {
if (num == null || num === '') return '0'
return Number(num).toLocaleString()
}
function viewDetail(item) {
const id = item.agent_id || item.id
if (id) {
uni.navigateTo({ url: `/pages/teamList/detail?id=${id}` })
}
} }
onMounted(() => { onMounted(() => {
@@ -89,99 +113,139 @@ watch(isAgent, (val) => {
</script> </script>
<template> <template>
<view class="p-4 min-h-screen bg-gray-50"> <view class="min-h-screen bg-gray-50">
<view v-if="!isLoggedIn" class="rounded-xl bg-white shadow p-6 text-center"> <view v-if="!isLoggedIn" class="rounded-xl bg-white shadow p-6 m-4 text-center">
<view class="text-gray-600">正在登录中请稍候...</view> <view class="text-gray-600">正在登录中请稍候...</view>
</view> </view>
<view v-else-if="!isAgent" class="rounded-xl bg-white shadow p-6 text-center"> <view v-else-if="!isAgent" class="rounded-xl bg-white shadow p-6 m-4 text-center">
<view class="text-gray-600 mb-4">注册成为代理后即可查看团队列表</view> <view class="text-gray-600 mb-4">注册成为代理后即可查看团队列表</view>
<wd-button type="primary" block @click="toRegister">注册成为代理</wd-button> <wd-button type="primary" block @click="toRegister">注册成为代理</wd-button>
</view> </view>
<template v-else> <template v-else>
<!-- 统计 -->
<view v-if="statistics" class="rounded-xl bg-white shadow p-4 mb-4">
<view class="text-base font-semibold text-gray-800 mb-3">团队概览</view>
<view class="grid grid-cols-3 gap-3">
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-lg font-bold text-primary">{{ statistics.total_members || 0 }}</view>
<view class="text-xs text-gray-500 mt-1">团队人数</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-lg font-bold text-primary">{{ statistics.today_new_members || 0 }}</view>
<view class="text-xs text-gray-500 mt-1">今日新增</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-lg font-bold text-primary">{{ statistics.month_new_members || 0 }}</view>
<view class="text-xs text-gray-500 mt-1">本月新增</view>
</view>
</view>
<view class="mt-3 flex gap-3">
<view class="flex-1 text-center p-2 rounded-lg bg-gray-50">
<view class="text-sm font-semibold text-gray-700">总收益</view>
<view class="text-sm text-primary">¥{{ (statistics.total_earnings ?? 0).toFixed(2) }}</view>
</view>
<view class="flex-1 text-center p-2 rounded-lg bg-gray-50">
<view class="text-sm font-semibold text-gray-700">今日收益</view>
<view class="text-sm text-primary">¥{{ (statistics.today_earnings ?? 0).toFixed(2) }}</view>
</view>
</view>
</view>
<!-- 搜索 --> <!-- 搜索 -->
<view class="mb-3"> <view class="px-4 pt-4 pb-3">
<wd-search <wd-search
v-model="keyword" v-model="keyword"
placeholder="手机号搜索" placeholder="请输入手机号搜索"
shape="round" shape="round"
@search="onRefresh" @search="onRefresh"
@clear="onRefresh" @clear="onRefresh"
/> />
</view> </view>
<!-- 列表 --> <view class="p-4 pb-8">
<view class="rounded-xl bg-white shadow overflow-hidden"> <!-- 统计 -->
<view v-if="loading && list.length === 0" class="p-6"> <view v-if="statistics" class="rounded-xl bg-white shadow-sm p-4 mb-4">
<wd-skeleton :row="5" /> <view class="text-base font-semibold text-gray-800 mb-3">团队概览</view>
</view> <view class="grid grid-cols-3 gap-3">
<view v-else-if="list.length === 0" class="p-8 text-center text-gray-500 text-sm"> <view class="text-center p-2 rounded-lg bg-gray-50">
暂无团队成员 <view class="text-lg font-bold text-primary">{{ statistics.total_members || 0 }}</view>
</view> <view class="text-xs text-gray-500 mt-1">团队人数</view>
<view v-else class="divide-y divide-gray-100">
<view
v-for="item in list"
:key="item.agent_id"
class="p-4 flex items-center justify-between"
>
<view class="flex-1 min-w-0">
<view class="flex items-center gap-2">
<text class="font-medium text-gray-800">{{ item.mobile || '—' }}</text>
<text
v-if="item.is_direct"
class="text-xs px-1.5 py-0.5 rounded bg-primary/10 text-primary"
>
直推
</text>
</view> </view>
<view class="text-xs text-gray-500 mt-1"> <view class="text-center p-2 rounded-lg bg-gray-50">
{{ levelName(item.level) }} · 加入 {{ item.create_time || '—' }} <view class="text-lg font-bold text-primary">{{ statistics.today_new_members || 0 }}</view>
<view class="text-xs text-gray-500 mt-1">今日新增</view>
</view> </view>
<view class="text-xs text-gray-500 mt-0.5"> <view class="text-center p-2 rounded-lg bg-gray-50">
返佣 ¥{{ (item.total_rebate_amount ?? 0).toFixed(2) }} · 查询 {{ item.total_queries ?? 0 }} <view class="text-lg font-bold text-primary">{{ statistics.month_new_members || 0 }}</view>
<view class="text-xs text-gray-500 mt-1">本月新增</view>
</view>
</view>
<view class="mt-3 flex gap-3">
<view class="flex-1 text-center p-2 rounded-lg bg-gray-50">
<view class="text-sm font-semibold text-gray-700">总收益</view>
<view class="text-sm text-primary">¥{{ (statistics.total_earnings ?? 0).toFixed(2) }}</view>
</view>
<view class="flex-1 text-center p-2 rounded-lg bg-gray-50">
<view class="text-sm font-semibold text-gray-700">今日收益</view>
<view class="text-sm text-primary">¥{{ (statistics.today_earnings ?? 0).toFixed(2) }}</view>
</view> </view>
</view> </view>
</view> </view>
<!-- 成员列表 -->
<view v-if="loading && list.length === 0" class="rounded-xl bg-white shadow p-6">
<wd-skeleton :row="5" />
</view>
<view v-else-if="list.length === 0" class="rounded-xl bg-white shadow p-8 text-center text-gray-500 text-sm">
暂无团队成员
</view>
<view v-else>
<view
v-for="(item, index) in list"
:key="item.agent_id || item.id"
class="rounded-xl bg-white shadow-sm p-4 mb-3"
>
<!-- 顶部信息 -->
<view class="flex items-center flex-wrap gap-2 mb-2">
<view class="w-6 h-6 flex items-center justify-center bg-gray-200 text-gray-600 rounded text-xs font-medium">
{{ index + 1 }}
</view>
<text class="text-base font-semibold text-gray-800">{{ item.mobile || '—' }}</text>
<text
:class="['px-2 py-0.5 rounded text-xs font-medium', getLevelClass(item.level)]"
>
{{ getLevelName(item.level) }}代理
</text>
<text
:class="[
'px-2 py-0.5 rounded text-xs font-medium',
item.is_direct ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700',
]"
>
{{ item.is_direct ? '直接' : '间接' }}
</text>
</view>
<!-- 加入时间 -->
<view class="text-xs text-gray-500 mb-3">加入团队时间{{ item.create_time || '—' }}</view>
<!-- 数据统计 -->
<view class="grid grid-cols-3 gap-2 mb-3">
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-[10px] text-gray-500 mb-0.5">今日邀请</view>
<view class="text-sm font-semibold text-gray-800">{{ formatCount(item.today_invites ?? 0) }}</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-[10px] text-gray-500 mb-0.5">本月邀请</view>
<view class="text-sm font-semibold text-gray-800">{{ formatCount(item.month_invites ?? 0) }}</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-[10px] text-gray-500 mb-0.5">邀请总人数</view>
<view class="text-sm font-semibold text-gray-800">{{ formatCount(item.total_invites ?? 0) }}</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-[10px] text-gray-500 mb-0.5">今日查询</view>
<view class="text-sm font-semibold text-gray-800">{{ formatCount(item.today_queries ?? 0) }}</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-[10px] text-gray-500 mb-0.5">本月查询</view>
<view class="text-sm font-semibold text-gray-800">{{ formatCount(item.month_queries ?? 0) }}</view>
</view>
<view class="text-center p-2 rounded-lg bg-gray-50">
<view class="text-[10px] text-gray-500 mb-0.5">查询总单量</view>
<view class="text-sm font-semibold text-gray-800">{{ formatCount(item.total_queries ?? 0) }}</view>
</view>
</view>
<!-- 查看详情 -->
<view class="flex justify-end">
<wd-button size="small" type="primary" @click="viewDetail(item)">
查看详情
</wd-button>
</view>
</view>
<view v-if="loading && list.length > 0" class="py-3 text-center text-gray-400 text-sm">加载中</view>
<view
v-else-if="hasMore && list.length > 0"
class="py-3 text-center text-primary text-sm"
@click="loadMore"
>
加载更多
</view>
</view>
</view> </view>
<view v-if="loading && list.length > 0" class="p-3 text-center text-gray-400 text-sm">
加载中
</view>
<view
v-else-if="hasMore && list.length > 0"
class="p-3 text-center text-primary text-sm"
@click="loadMore"
>
加载更多
</view>
</view>
</template> </template>
</view> </view>
</template> </template>

BIN
src/static/index/01.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
src/static/index/05.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
src/static/index/06.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/static/index/07.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/static/index/08.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
src/static/index/09.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
src/static/index/10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

BIN
src/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

7
uni-pages.d.ts vendored
View File

@@ -10,6 +10,7 @@ type _LocationUrl =
"/pages/agentPromotionQueryList/index" | "/pages/agentPromotionQueryList/index" |
"/pages/agentSystemGuide/index" | "/pages/agentSystemGuide/index" |
"/pages/agentUpgrade/index" | "/pages/agentUpgrade/index" |
"/pages/auth/index" |
"/pages/help/detail" | "/pages/help/detail" |
"/pages/help/guide" | "/pages/help/guide" |
"/pages/help/index" | "/pages/help/index" |
@@ -17,13 +18,13 @@ type _LocationUrl =
"/pages/invitation/index" | "/pages/invitation/index" |
"/pages/me/index" | "/pages/me/index" |
"/pages/privacyPolicy/index" | "/pages/privacyPolicy/index" |
"/pages/promote/index" |
"/pages/promote/report" | "/pages/promote/report" |
"/pages/promote/reportList" |
"/pages/promoteDetails/index" | "/pages/promoteDetails/index" |
"/pages/register/index" |
"/pages/report/index" | "/pages/report/index" |
"/pages/rewardsDetails/index" | "/pages/rewardsDetails/index" |
"/pages/service/index" | "/pages/service/index" |
"/pages/teamList/detail" |
"/pages/teamList/index" | "/pages/teamList/index" |
"/pages/upgradeSubordinate/index" | "/pages/upgradeSubordinate/index" |
"/pages/userAgreement/index" | "/pages/userAgreement/index" |
@@ -35,7 +36,7 @@ interface NavigateToOptions {
interface RedirectToOptions extends NavigateToOptions {} interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions { interface SwitchTabOptions {
url: "/pages/index" | "/pages/promote/index" | "/pages/agent/index" | "/pages/me/index" url: "/pages/index" | "/pages/agent/index" | "/pages/me/index"
} }
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions; type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;