f
@@ -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` - 帮助中心
|
||||||
|
|||||||
@@ -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: '数据',
|
||||||
|
|||||||
17
src/App.vue
@@ -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 || ''
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
82
src/composables/useBindPhoneLogic.js
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取「分享给好友」的配置
|
* 获取「分享给好友」的配置
|
||||||
|
|||||||
@@ -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": "数据",
|
||||||
|
|||||||
@@ -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'"
|
||||||
>
|
>
|
||||||
月
|
月
|
||||||
|
|||||||
@@ -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' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
198
src/pages/promote/reportList.vue
Normal 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>
|
||||||
@@ -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>
|
|
||||||
297
src/pages/teamList/detail.vue
Normal 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>
|
||||||
@@ -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
|
After Width: | Height: | Size: 72 KiB |
BIN
src/static/index/05.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
src/static/index/06.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/static/index/07.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/static/index/08.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/static/index/09.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
src/static/index/10.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 38 KiB |
BIN
src/static/index/banner_1.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 272 B |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 86 KiB |
BIN
src/static/index/ycc_search.jpg
Normal file
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 16 KiB |
BIN
src/static/logo.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
7
uni-pages.d.ts
vendored
@@ -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;
|
||||||
|
|||||||