t-4-29
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { installPrivacyGuards } from '@/composables/usePrivacyConsent'
|
import { installPrivacyGuards } from '@/composables/usePrivacyConsent'
|
||||||
|
|
||||||
|
const NOTIFICATION_SESSION_KEY = 'bdrp_app_shown_notifications'
|
||||||
|
|
||||||
onLaunch(async () => {
|
onLaunch(async () => {
|
||||||
installPrivacyGuards()
|
installPrivacyGuards()
|
||||||
|
uni.removeStorageSync(NOTIFICATION_SESSION_KEY)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
4
src/auto-imports.d.ts
vendored
4
src/auto-imports.d.ts
vendored
@@ -56,6 +56,7 @@ declare global {
|
|||||||
const getToken: typeof import('./utils/storage')['getToken']
|
const getToken: typeof import('./utils/storage')['getToken']
|
||||||
const getUserInfo: typeof import('./utils/storage')['getUserInfo']
|
const getUserInfo: typeof import('./utils/storage')['getUserInfo']
|
||||||
const getWebPathForNotification: typeof import('./composables/uni-router')['getWebPathForNotification']
|
const getWebPathForNotification: typeof import('./composables/uni-router')['getWebPathForNotification']
|
||||||
|
const getWebPathsForNotification: typeof import('./composables/uni-router')['getWebPathsForNotification']
|
||||||
const h: typeof import('vue')['h']
|
const h: typeof import('vue')['h']
|
||||||
const handlePosterRenderMergeDone: typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeDone']
|
const handlePosterRenderMergeDone: typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeDone']
|
||||||
const handlePosterRenderMergeFailed: typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeFailed']
|
const handlePosterRenderMergeFailed: typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeFailed']
|
||||||
@@ -250,6 +251,7 @@ declare global {
|
|||||||
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||||
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useGlobalNotification: typeof import('./composables/useGlobalNotification.js')['useGlobalNotification']
|
||||||
const useHotUpdate: typeof import('./composables/useHotUpdate')['useHotUpdate']
|
const useHotUpdate: typeof import('./composables/useHotUpdate')['useHotUpdate']
|
||||||
const useHttp: typeof import('./composables/useHttp.js')['useHttp']
|
const useHttp: typeof import('./composables/useHttp.js')['useHttp']
|
||||||
const useId: typeof import('vue')['useId']
|
const useId: typeof import('vue')['useId']
|
||||||
@@ -449,6 +451,7 @@ declare module 'vue' {
|
|||||||
readonly getToken: UnwrapRef<typeof import('./utils/storage')['getToken']>
|
readonly getToken: UnwrapRef<typeof import('./utils/storage')['getToken']>
|
||||||
readonly getUserInfo: UnwrapRef<typeof import('./utils/storage')['getUserInfo']>
|
readonly getUserInfo: UnwrapRef<typeof import('./utils/storage')['getUserInfo']>
|
||||||
readonly getWebPathForNotification: UnwrapRef<typeof import('./composables/uni-router')['getWebPathForNotification']>
|
readonly getWebPathForNotification: UnwrapRef<typeof import('./composables/uni-router')['getWebPathForNotification']>
|
||||||
|
readonly getWebPathsForNotification: UnwrapRef<typeof import('./composables/uni-router')['getWebPathsForNotification']>
|
||||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
readonly handlePosterRenderMergeDone: UnwrapRef<typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeDone']>
|
readonly handlePosterRenderMergeDone: UnwrapRef<typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeDone']>
|
||||||
readonly handlePosterRenderMergeFailed: UnwrapRef<typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeFailed']>
|
readonly handlePosterRenderMergeFailed: UnwrapRef<typeof import('./utils/posterRenderMergeBridge')['handlePosterRenderMergeFailed']>
|
||||||
@@ -640,6 +643,7 @@ declare module 'vue' {
|
|||||||
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
||||||
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
||||||
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
|
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
|
||||||
|
readonly useGlobalNotification: UnwrapRef<typeof import('./composables/useGlobalNotification.js')['useGlobalNotification']>
|
||||||
readonly useHotUpdate: UnwrapRef<typeof import('./composables/useHotUpdate')['useHotUpdate']>
|
readonly useHotUpdate: UnwrapRef<typeof import('./composables/useHotUpdate')['useHotUpdate']>
|
||||||
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
||||||
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
|
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
|
||||||
|
|||||||
@@ -155,52 +155,67 @@ export function getLayoutPageTitle(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 与 webview vue-router path 对齐,用于全局通知 notificationPage 匹配
|
* 与 webview vue-router path 对齐,用于全局通知 notificationPage 匹配。
|
||||||
|
* 一个 uni 页面可对应多个 web 路径(如 webview 中 /withdraw 和 /agent/withdraw 是同一页面)。
|
||||||
*/
|
*/
|
||||||
const UNI_TO_WEB_NOTIFY_PATH: Record<string, string> = {
|
const UNI_TO_WEB_NOTIFY_PATHS: Record<string, string[]> = {
|
||||||
'pages/index': '/',
|
'pages/index': ['/'],
|
||||||
'pages/agent': '/agent',
|
'pages/agent': ['/agent'],
|
||||||
'pages/me': '/me',
|
'pages/me': ['/me'],
|
||||||
'pages/promote': '/agent/promote',
|
'pages/promote': ['/agent/promote'],
|
||||||
'pages/history-query': '/historyQuery',
|
'pages/history-query': ['/historyQuery'],
|
||||||
'pages/help': '/help',
|
'pages/help': ['/help'],
|
||||||
'pages/help-detail': '/help/detail',
|
'pages/help-detail': ['/help/detail'],
|
||||||
'pages/help-guide': '/help/guide',
|
'pages/help-guide': ['/help/guide'],
|
||||||
'pages/withdraw': '/withdraw',
|
'pages/withdraw': ['/withdraw', '/agent/withdraw'],
|
||||||
'pages/report-result-webview': '/app/report',
|
'pages/report-result-webview': ['/app/report', '/report'],
|
||||||
'pages/report-example-webview': '/app/example',
|
'pages/report-example-webview': ['/app/example', '/example'],
|
||||||
'pages/privacy-policy': '/privacyPolicy',
|
'pages/privacy-policy': ['/privacyPolicy'],
|
||||||
'pages/user-agreement': '/userAgreement',
|
'pages/user-agreement': ['/userAgreement'],
|
||||||
'pages/agent-manage-agreement': '/agentManageAgreement',
|
'pages/agent-manage-agreement': ['/agentManageAgreement'],
|
||||||
'pages/agent-service-agreement': '/agentSerivceAgreement',
|
'pages/agent-service-agreement': ['/agentSerivceAgreement'],
|
||||||
'pages/authorization': '/authorization',
|
'pages/authorization': ['/authorization'],
|
||||||
'pages/payment-result': '/payment/result',
|
'pages/payment-result': ['/payment/result'],
|
||||||
'pages/inquire': '/inquire',
|
'pages/inquire': ['/inquire'],
|
||||||
'pages/login': '/login',
|
'pages/login': ['/login'],
|
||||||
'pages/invitation': '/agent/invitation',
|
'pages/invitation': ['/agent/invitation'],
|
||||||
'pages/agent-promote-details': '/agent/promoteDetails',
|
'pages/agent-promote-details': ['/agent/promoteDetails'],
|
||||||
'pages/agent-rewards-details': '/agent/rewardsDetails',
|
'pages/agent-rewards-details': ['/agent/rewardsDetails'],
|
||||||
'pages/agent-vip': '/agent/agentVip',
|
'pages/agent-vip': ['/agent/agentVip'],
|
||||||
'pages/agent-vip-apply': '/agent/vipApply',
|
'pages/agent-vip-apply': ['/agent/vipApply'],
|
||||||
'pages/agent-vip-config': '/agent/vipConfig',
|
'pages/agent-vip-config': ['/agent/vipConfig'],
|
||||||
'pages/withdraw-details': '/agent/withdrawDetails',
|
'pages/withdraw-details': ['/agent/withdrawDetails'],
|
||||||
'pages/subordinate-list': '/agent/subordinateList',
|
'pages/subordinate-list': ['/agent/subordinateList'],
|
||||||
|
'pages/cancel-account': ['/cancelAccount'],
|
||||||
|
'pages/subordinate-detail': ['/agent/subordinateDetail'],
|
||||||
|
'pages/invitation-agent-apply': ['/agent/invitationAgentApply'],
|
||||||
|
'pages/report-share': ['/report/share'],
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWebPathForNotification(): string {
|
/**
|
||||||
|
* 获取当前 uni 页面在 web 端可能的所有路径(用于通知匹配)
|
||||||
|
*/
|
||||||
|
export function getWebPathsForNotification(): string[] {
|
||||||
const r = getCurrentUniRoute()
|
const r = getCurrentUniRoute()
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
const page = pages[pages.length - 1] as any
|
const page = pages[pages.length - 1] as any
|
||||||
const q: Record<string, string> = { ...(page?.options || {}) }
|
const q: Record<string, string> = { ...(page?.options || {}) }
|
||||||
|
|
||||||
|
// 动态路由:带参数的路径
|
||||||
if (r === 'pages/inquire' && q.feature)
|
if (r === 'pages/inquire' && q.feature)
|
||||||
return `/inquire/${q.feature}`
|
return [`/inquire/${q.feature}`]
|
||||||
if (r === 'pages/subordinate-detail' && q.id)
|
if (r === 'pages/subordinate-detail' && q.id)
|
||||||
return `/agent/subordinateDetail/${q.id}`
|
return [`/agent/subordinateDetail/${q.id}`]
|
||||||
if (r === 'pages/invitation-agent-apply' && q.linkIdentifier)
|
if (r === 'pages/invitation-agent-apply' && q.linkIdentifier)
|
||||||
return `/agent/invitationAgentApply/${q.linkIdentifier}`
|
return [`/agent/invitationAgentApply/${q.linkIdentifier}`]
|
||||||
if (r === 'pages/report-share' && q.linkIdentifier)
|
if (r === 'pages/report-share' && q.linkIdentifier)
|
||||||
return `/report/share/${q.linkIdentifier}`
|
return [`/report/share/${q.linkIdentifier}`]
|
||||||
return UNI_TO_WEB_NOTIFY_PATH[r] || '/'
|
|
||||||
|
return UNI_TO_WEB_NOTIFY_PATHS[r] || ['/']
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWebPathForNotification(): string {
|
||||||
|
return getWebPathsForNotification()[0] ?? '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniRouteToName: Record<string, string> = {
|
const uniRouteToName: Record<string, string> = {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ function uniRequest<T>(
|
|||||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||||
url: string,
|
url: string,
|
||||||
data?: unknown,
|
data?: unknown,
|
||||||
): Promise<{ statusCode: number, data: ApiEnvelope<T> }> {
|
): Promise<{ statusCode: number, data: ApiEnvelope<T>, header: Record<string, string> }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!hasAcceptedPrivacyPolicy()) {
|
if (!hasAcceptedPrivacyPolicy()) {
|
||||||
reject(new Error('用户未同意隐私政策,禁止请求'))
|
reject(new Error('用户未同意隐私政策,禁止请求'))
|
||||||
@@ -114,6 +114,7 @@ function uniRequest<T>(
|
|||||||
resolve({
|
resolve({
|
||||||
statusCode: res.statusCode || 0,
|
statusCode: res.statusCode || 0,
|
||||||
data: res.data as ApiEnvelope<T>,
|
data: res.data as ApiEnvelope<T>,
|
||||||
|
header: (res.header || {}) as Record<string, string>,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
@@ -134,10 +135,24 @@ async function executeJson<T>(
|
|||||||
if (!silent)
|
if (!silent)
|
||||||
showLoading()
|
showLoading()
|
||||||
try {
|
try {
|
||||||
const { statusCode, data: body } = await uniRequest<T>(method, url, data)
|
const { statusCode, data: body, header: respHeader } = await uniRequest<T>(method, url, data)
|
||||||
if (!silent)
|
if (!silent)
|
||||||
hideLoading()
|
hideLoading()
|
||||||
|
|
||||||
|
// 检测会员过期响应头,自动刷新代理信息
|
||||||
|
console.log('[MembershipExpired] respHeader:', JSON.stringify(respHeader))
|
||||||
|
if (respHeader && (respHeader['X-Membership-Expired'] === 'true' || respHeader['x-membership-expired'] === 'true')) {
|
||||||
|
console.log('[MembershipExpired] 检测到过期响应头')
|
||||||
|
const agentStore = useAgentStore()
|
||||||
|
console.log('[MembershipExpired] 当前 level:', agentStore.level)
|
||||||
|
if (agentStore.level !== 'normal') {
|
||||||
|
console.log('[MembershipExpired] 触发 fetchAgentStatus')
|
||||||
|
agentStore.fetchAgentStatus()
|
||||||
|
} else {
|
||||||
|
console.log('[MembershipExpired] 已是 normal,跳过刷新')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (statusCode === 401) {
|
if (statusCode === 401) {
|
||||||
clearAuthStorage()
|
clearAuthStorage()
|
||||||
navigateLogin()
|
navigateLogin()
|
||||||
|
|||||||
174
src/composables/useGlobalNotification.js
Normal file
174
src/composables/useGlobalNotification.js
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { getWebPathForNotification, getWebPathsForNotification } from '@/composables/uni-router'
|
||||||
|
import useApiFetch from '@/composables/useApiFetch'
|
||||||
|
|
||||||
|
const SESSION_KEY = 'bdrp_app_shown_notifications'
|
||||||
|
|
||||||
|
const showPopup = ref(false)
|
||||||
|
const currentNotify = ref(null)
|
||||||
|
const notify = ref([])
|
||||||
|
const pendingNotifyQueue = ref([])
|
||||||
|
const shownNotificationKeys = ref(new Set())
|
||||||
|
|
||||||
|
function buildNotificationKey(n) {
|
||||||
|
return [
|
||||||
|
n.title ?? '',
|
||||||
|
n.content ?? '',
|
||||||
|
n.notificationPage ?? '',
|
||||||
|
n.startDate ?? '',
|
||||||
|
n.endDate ?? '',
|
||||||
|
n.startTime ?? '',
|
||||||
|
n.endTime ?? '',
|
||||||
|
].join('|')
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadShownNotificationKeys() {
|
||||||
|
try {
|
||||||
|
const raw = uni.getStorageSync(SESSION_KEY)
|
||||||
|
if (raw) {
|
||||||
|
const parsed = JSON.parse(raw)
|
||||||
|
if (Array.isArray(parsed))
|
||||||
|
shownNotificationKeys.value = new Set(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
shownNotificationKeys.value = new Set()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveShownNotificationKeys() {
|
||||||
|
uni.setStorageSync(SESSION_KEY, JSON.stringify([...shownNotificationKeys.value]))
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasShownNotification(n) {
|
||||||
|
return shownNotificationKeys.value.has(buildNotificationKey(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
function markNotificationShown(n) {
|
||||||
|
shownNotificationKeys.value.add(buildNotificationKey(n))
|
||||||
|
saveShownNotificationKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNextNotification() {
|
||||||
|
const next = pendingNotifyQueue.value.shift()
|
||||||
|
if (!next) {
|
||||||
|
currentNotify.value = null
|
||||||
|
showPopup.value = false
|
||||||
|
console.log('[通知DEBUG] showNextNotification: 队列为空,关闭弹窗')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentNotify.value = next
|
||||||
|
showPopup.value = true
|
||||||
|
markNotificationShown(next)
|
||||||
|
console.log('[通知DEBUG] showNextNotification: 展示通知, title=', next.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWithinTimeRange(startTime, endTime) {
|
||||||
|
const s = (startTime || '').trim()
|
||||||
|
const e = (endTime || '').trim()
|
||||||
|
if (!s && !e)
|
||||||
|
return true
|
||||||
|
if (s === e)
|
||||||
|
return true
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const currentMinutes = now.getHours() * 60 + now.getMinutes()
|
||||||
|
const toMinutes = (t) => {
|
||||||
|
const parts = t.split(':').map(Number)
|
||||||
|
return (parts[0] ?? 0) * 60 + (parts[1] ?? 0)
|
||||||
|
}
|
||||||
|
const startMinutes = toMinutes(s)
|
||||||
|
const endMinutes = toMinutes(e)
|
||||||
|
|
||||||
|
if (endMinutes < startMinutes)
|
||||||
|
return currentMinutes >= startMinutes || currentMinutes <= endMinutes
|
||||||
|
return currentMinutes >= startMinutes && currentMinutes <= endMinutes
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchesNotificationPage(page) {
|
||||||
|
const p = (page || '').trim()
|
||||||
|
const paths = getWebPathsForNotification()
|
||||||
|
// 当前页面的所有可能 web 路径中,有一个与配置路径匹配即可
|
||||||
|
for (const cur of paths) {
|
||||||
|
if (p === cur)
|
||||||
|
return true
|
||||||
|
if (p === '/' && (cur === '/' || cur === ''))
|
||||||
|
return true
|
||||||
|
// 兼容:/report 与 /app/report
|
||||||
|
if (cur.startsWith('/app/') && p === cur.replace('/app/', '/'))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNotification() {
|
||||||
|
if (showPopup.value) {
|
||||||
|
console.log('[通知DEBUG] checkNotification: 弹窗已显示,跳过')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const curPath = getWebPathForNotification()
|
||||||
|
console.log('[通知DEBUG] checkNotification: 当前页面路径=', curPath, '通知数量=', notify.value.length)
|
||||||
|
|
||||||
|
const matched = []
|
||||||
|
for (const notification of notify.value) {
|
||||||
|
const isTimeValid = isWithinTimeRange(notification.startTime, notification.endTime)
|
||||||
|
const isPageValid = matchesNotificationPage(notification.notificationPage)
|
||||||
|
const isShown = hasShownNotification(notification)
|
||||||
|
console.log('[通知DEBUG] 通知项:', JSON.stringify({
|
||||||
|
title: notification.title,
|
||||||
|
page: notification.notificationPage,
|
||||||
|
curPath,
|
||||||
|
startTime: notification.startTime,
|
||||||
|
endTime: notification.endTime,
|
||||||
|
isTimeValid,
|
||||||
|
isPageValid,
|
||||||
|
isShown,
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (isTimeValid && isPageValid && !isShown)
|
||||||
|
matched.push(notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[通知DEBUG] 匹配到的通知数量:', matched.length)
|
||||||
|
pendingNotifyQueue.value = matched
|
||||||
|
showNextNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getGlobalNotify() {
|
||||||
|
console.log('[通知DEBUG] getGlobalNotify 开始请求')
|
||||||
|
const { data, error } = await useApiFetch('/notification/list', { silent: true }).get().json()
|
||||||
|
console.log('[通知DEBUG] 请求完成, data:', JSON.stringify(data.value), 'error:', error.value)
|
||||||
|
if (!data.value || error.value) {
|
||||||
|
console.log('[通知DEBUG] 数据为空或出错,退出')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (data.value.code !== 200) {
|
||||||
|
console.log('[通知DEBUG] code 不为 200,code:', data.value.code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const raw = data.value.data
|
||||||
|
console.log('[通知DEBUG] raw data:', JSON.stringify(raw))
|
||||||
|
const list = raw && typeof raw === 'object' && 'notifications' in raw
|
||||||
|
? raw.notifications
|
||||||
|
: []
|
||||||
|
notify.value = Array.isArray(list) ? list : []
|
||||||
|
console.log('[通知DEBUG] notify list 长度:', notify.value.length, '内容:', JSON.stringify(notify.value))
|
||||||
|
checkNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClosePopup() {
|
||||||
|
showNextNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGlobalNotification() {
|
||||||
|
return {
|
||||||
|
showPopup,
|
||||||
|
currentNotify,
|
||||||
|
notify,
|
||||||
|
loadShownNotificationKeys,
|
||||||
|
getGlobalNotify,
|
||||||
|
checkNotification,
|
||||||
|
onClosePopup,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,21 +4,26 @@ import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|||||||
import {
|
import {
|
||||||
getCurrentUniRoute,
|
getCurrentUniRoute,
|
||||||
getLayoutPageTitle,
|
getLayoutPageTitle,
|
||||||
getWebPathForNotification,
|
|
||||||
useRouter,
|
useRouter,
|
||||||
} from '@/composables/uni-router'
|
} from '@/composables/uni-router'
|
||||||
import useApiFetch from '@/composables/useApiFetch'
|
|
||||||
import { ensurePageAccessByUrl } from '@/composables/useNavigationAuthGuard'
|
import { ensurePageAccessByUrl } from '@/composables/useNavigationAuthGuard'
|
||||||
|
import { useGlobalNotification } from '@/composables/useGlobalNotification'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const showPopup = ref(false)
|
|
||||||
const notify = ref([])
|
|
||||||
const currentNotify = ref(null)
|
|
||||||
const pageTitle = ref('赤眉')
|
const pageTitle = ref('赤眉')
|
||||||
const currentRoute = ref('')
|
const currentRoute = ref('')
|
||||||
const safeAreaTop = ref(0)
|
const safeAreaTop = ref(0)
|
||||||
const immersiveNavbarSolid = ref(false)
|
const immersiveNavbarSolid = ref(false)
|
||||||
|
|
||||||
|
const {
|
||||||
|
showPopup,
|
||||||
|
currentNotify,
|
||||||
|
loadShownNotificationKeys,
|
||||||
|
getGlobalNotify,
|
||||||
|
checkNotification,
|
||||||
|
onClosePopup,
|
||||||
|
} = useGlobalNotification()
|
||||||
|
|
||||||
const immersiveRoutes = new Set([
|
const immersiveRoutes = new Set([
|
||||||
'pages/inquire',
|
'pages/inquire',
|
||||||
])
|
])
|
||||||
@@ -63,55 +68,8 @@ function getSafeAreaTop() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
function onClickOverlay() {
|
||||||
getSafeAreaTop()
|
onClosePopup()
|
||||||
syncTitle()
|
|
||||||
getGlobalNotify()
|
|
||||||
})
|
|
||||||
|
|
||||||
onShow(() => {
|
|
||||||
syncTitle()
|
|
||||||
ensureAuthIfNeeded()
|
|
||||||
checkNotification()
|
|
||||||
})
|
|
||||||
|
|
||||||
async function getGlobalNotify() {
|
|
||||||
const { data, error } = await useApiFetch('/notification/list', { silent: true }).get().json()
|
|
||||||
if (!data.value || error.value)
|
|
||||||
return
|
|
||||||
if (data.value.code !== 200)
|
|
||||||
return
|
|
||||||
const raw = data.value.data
|
|
||||||
const list = raw && typeof raw === 'object' && 'notifications' in raw
|
|
||||||
? raw.notifications
|
|
||||||
: []
|
|
||||||
notify.value = Array.isArray(list) ? list : []
|
|
||||||
checkNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWithinTimeRange(startTime, endTime) {
|
|
||||||
const now = new Date()
|
|
||||||
const currentMinutes = now.getHours() * 60 + now.getMinutes()
|
|
||||||
const startParts = startTime.split(':').map(Number)
|
|
||||||
const endParts = endTime.split(':').map(Number)
|
|
||||||
const startMinutes = startParts[0] * 60 + startParts[1]
|
|
||||||
const endMinutes = endParts[0] * 60 + endParts[1]
|
|
||||||
if (endMinutes < startMinutes)
|
|
||||||
return currentMinutes >= startMinutes || currentMinutes < endMinutes
|
|
||||||
return currentMinutes >= startMinutes && currentMinutes <= endMinutes
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkNotification() {
|
|
||||||
const webPath = getWebPathForNotification()
|
|
||||||
showPopup.value = false
|
|
||||||
for (const notification of notify.value) {
|
|
||||||
const isTimeValid = isWithinTimeRange(notification.startTime, notification.endTime)
|
|
||||||
if (isTimeValid && notification.notificationPage === webPath) {
|
|
||||||
currentNotify.value = notification
|
|
||||||
showPopup.value = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickLeft() {
|
function onClickLeft() {
|
||||||
@@ -124,6 +82,21 @@ function onImmersiveNavbarChange(payload) {
|
|||||||
immersiveNavbarSolid.value = Boolean(payload.solid)
|
immersiveNavbarSolid.value = Boolean(payload.solid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getSafeAreaTop()
|
||||||
|
syncTitle()
|
||||||
|
loadShownNotificationKeys()
|
||||||
|
console.log('[通知DEBUG] default layout onMounted, 即将请求通知')
|
||||||
|
getGlobalNotify()
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
syncTitle()
|
||||||
|
ensureAuthIfNeeded()
|
||||||
|
console.log('[通知DEBUG] default layout onShow')
|
||||||
|
checkNotification()
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
uni.$on('immersive-navbar-change', onImmersiveNavbarChange)
|
uni.$on('immersive-navbar-change', onImmersiveNavbarChange)
|
||||||
})
|
})
|
||||||
@@ -150,11 +123,14 @@ onUnmounted(() => {
|
|||||||
/>
|
/>
|
||||||
<wd-navbar v-else :title="pageTitle" safe-area-inset-top left-arrow placeholder fixed @click-left="onClickLeft" />
|
<wd-navbar v-else :title="pageTitle" safe-area-inset-top left-arrow placeholder fixed @click-left="onClickLeft" />
|
||||||
<slot />
|
<slot />
|
||||||
<wd-popup v-model="showPopup" round>
|
<wd-popup v-model="showPopup" round :close-on-click-modal="true" @close="onClickOverlay">
|
||||||
<view class="popup-content p-8 text-center">
|
<view class="popup-content p-8 text-center" style="width: 85vw;">
|
||||||
|
<view v-if="currentNotify?.title" class="text-lg font-bold mb-4">
|
||||||
|
{{ currentNotify.title }}
|
||||||
|
</view>
|
||||||
<view class="notify-html" v-html="currentNotify?.content" />
|
<view class="notify-html" v-html="currentNotify?.content" />
|
||||||
<view class="flex justify-center">
|
<view class="flex justify-center mt-6">
|
||||||
<wd-button type="primary" class="w-24" @click="showPopup = false">
|
<wd-button type="primary" class="w-24" @click="onClosePopup">
|
||||||
关闭
|
关闭
|
||||||
</wd-button>
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
@@ -189,6 +165,7 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.popup-content {
|
.popup-content {
|
||||||
max-width: 90vw;
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,6 +5,16 @@ import { getCurrentUniRoute } from '@/composables/uni-router'
|
|||||||
import { openCustomerService } from '@/composables/useCustomerService'
|
import { openCustomerService } from '@/composables/useCustomerService'
|
||||||
import { ensurePageAccessByUrl } from '@/composables/useNavigationAuthGuard'
|
import { ensurePageAccessByUrl } from '@/composables/useNavigationAuthGuard'
|
||||||
import { getPrivacyConsentPageUrl } from '@/composables/usePrivacyConsent'
|
import { getPrivacyConsentPageUrl } from '@/composables/usePrivacyConsent'
|
||||||
|
import { useGlobalNotification } from '@/composables/useGlobalNotification'
|
||||||
|
|
||||||
|
const {
|
||||||
|
showPopup: showNotifyPopup,
|
||||||
|
currentNotify,
|
||||||
|
loadShownNotificationKeys,
|
||||||
|
getGlobalNotify,
|
||||||
|
checkNotification,
|
||||||
|
onClosePopup,
|
||||||
|
} = useGlobalNotification()
|
||||||
|
|
||||||
const tabbar = ref('index')
|
const tabbar = ref('index')
|
||||||
const safeAreaTop = ref(0)
|
const safeAreaTop = ref(0)
|
||||||
@@ -43,6 +53,11 @@ function getSafeAreaTop() {
|
|||||||
|
|
||||||
onMounted(getSafeAreaTop)
|
onMounted(getSafeAreaTop)
|
||||||
onMounted(syncTabbar)
|
onMounted(syncTabbar)
|
||||||
|
onMounted(() => {
|
||||||
|
loadShownNotificationKeys()
|
||||||
|
console.log('[通知DEBUG] home layout onMounted, 即将请求通知')
|
||||||
|
getGlobalNotify()
|
||||||
|
})
|
||||||
|
|
||||||
function buildCurrentPageUrl() {
|
function buildCurrentPageUrl() {
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
@@ -62,6 +77,8 @@ function ensureAuthIfNeeded() {
|
|||||||
onShow(() => {
|
onShow(() => {
|
||||||
syncTabbar()
|
syncTabbar()
|
||||||
ensureAuthIfNeeded()
|
ensureAuthIfNeeded()
|
||||||
|
console.log('[通知DEBUG] home layout onShow')
|
||||||
|
checkNotification()
|
||||||
})
|
})
|
||||||
|
|
||||||
function tabChange(payload) {
|
function tabChange(payload) {
|
||||||
@@ -122,6 +139,20 @@ function clearCacheForPrivacyTest() {
|
|||||||
<view class="clear-cache-button" @click="clearCacheForPrivacyTest">
|
<view class="clear-cache-button" @click="clearCacheForPrivacyTest">
|
||||||
<text>清除缓存</text>
|
<text>清除缓存</text>
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
|
<wd-popup v-model="showNotifyPopup" round :close-on-click-modal="true" @close="onClosePopup">
|
||||||
|
<view class="notify-popup-content p-8 text-center" style="width: 85vw;">
|
||||||
|
<view v-if="currentNotify?.title" class="text-lg font-bold mb-4">
|
||||||
|
{{ currentNotify.title }}
|
||||||
|
</view>
|
||||||
|
<view class="notify-html" v-html="currentNotify?.content" />
|
||||||
|
<view class="flex justify-center mt-6">
|
||||||
|
<wd-button type="primary" class="w-24" @click="onClosePopup">
|
||||||
|
关闭
|
||||||
|
</wd-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</wd-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -192,4 +223,9 @@ function clearCacheForPrivacyTest() {
|
|||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notify-popup-content {
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -107,10 +107,10 @@ function toSubordinateList() {
|
|||||||
累计收益:¥ {{ (data?.total_earnings || 0).toFixed(2) }}
|
累计收益:¥ {{ (data?.total_earnings || 0).toFixed(2) }}
|
||||||
</view>
|
</view>
|
||||||
<view class="mb-1 text-sm text-gray-500">
|
<view class="mb-1 text-sm text-gray-500">
|
||||||
待结账金额:¥ {{ (data?.frozen_balance || 0).toFixed(2) }}
|
冻结余额:¥ {{ (data?.frozen_balance || 0).toFixed(2) }}
|
||||||
</view>
|
</view>
|
||||||
<view class="mb-6 text-xs text-gray-400">
|
<view class="mb-6 text-xs text-gray-400">
|
||||||
待结账金额将在订单创建24小时后自动结账
|
冻结余额将在订单创建24小时后自动结账
|
||||||
</view>
|
</view>
|
||||||
<view class="grid grid-cols-2 gap-3">
|
<view class="grid grid-cols-2 gap-3">
|
||||||
<wd-button type="primary" block @click="toWithdraw">
|
<wd-button type="primary" block @click="toWithdraw">
|
||||||
@@ -140,13 +140,10 @@ function toSubordinateList() {
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="grid grid-cols-3 mb-6 gap-2">
|
<view class="grid grid-cols-3 mb-6 gap-2">
|
||||||
<view
|
<view v-for="item in promoteDateOptions" :key="item.value"
|
||||||
v-for="item in promoteDateOptions"
|
|
||||||
:key="item.value"
|
|
||||||
class="rounded-full px-4 py-1 text-center text-sm transition-all"
|
class="rounded-full px-4 py-1 text-center text-sm transition-all"
|
||||||
:class="selectedPromoteDate === item.value ? 'bg-blue-500 text-white shadow-md' : 'border border-gray-200/50 bg-white/90 text-gray-600'"
|
:class="selectedPromoteDate === item.value ? 'bg-blue-500 text-white shadow-md' : 'border border-gray-200/50 bg-white/90 text-gray-600'"
|
||||||
@click="selectedPromoteDate = item.value"
|
@click="selectedPromoteDate = item.value">
|
||||||
>
|
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -193,13 +190,10 @@ function toSubordinateList() {
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="grid grid-cols-3 mb-6 gap-2">
|
<view class="grid grid-cols-3 mb-6 gap-2">
|
||||||
<view
|
<view v-for="item in teamDateOptions" :key="item.value"
|
||||||
v-for="item in teamDateOptions"
|
|
||||||
:key="item.value"
|
|
||||||
class="rounded-full px-4 py-1 text-center text-sm transition-all"
|
class="rounded-full px-4 py-1 text-center text-sm transition-all"
|
||||||
:class="selectedTeamDate === item.value ? 'bg-green-500 text-white shadow-md' : 'border border-gray-200/50 bg-white/90 text-gray-600'"
|
:class="selectedTeamDate === item.value ? 'bg-green-500 text-white shadow-md' : 'border border-gray-200/50 bg-white/90 text-gray-600'"
|
||||||
@click="selectedTeamDate = item.value"
|
@click="selectedTeamDate = item.value">
|
||||||
>
|
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -206,34 +206,24 @@ async function submitCancelAccount() {
|
|||||||
</view>
|
</view>
|
||||||
<view v-else class="min-h-0 flex flex-1 flex-col">
|
<view v-else class="min-h-0 flex flex-1 flex-col">
|
||||||
<view class="min-h-0 flex flex-1 flex-col">
|
<view class="min-h-0 flex flex-1 flex-col">
|
||||||
<scroll-view
|
<scroll-view scroll-y :show-scrollbar="true" class="box-border min-h-0 flex-1 bg-white"
|
||||||
scroll-y
|
style="flex: 1; height: 0; width: 100%;">
|
||||||
:show-scrollbar="true"
|
|
||||||
class="box-border min-h-0 flex-1 bg-white"
|
|
||||||
style="flex: 1; height: 0; width: 100%;"
|
|
||||||
>
|
|
||||||
<AccountCancelAgreement />
|
<AccountCancelAgreement />
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<view
|
<view v-if="showAnyReminder" class="flex-shrink-0 border-t border-gray-100 bg-gray-50 px-4 py-3 space-y-2">
|
||||||
v-if="showAnyReminder"
|
<view v-if="showBalanceWarning"
|
||||||
class="flex-shrink-0 border-t border-gray-100 bg-gray-50 px-4 py-3 space-y-2"
|
class="border border-amber-200 rounded-lg bg-amber-50 p-3 text-sm text-amber-900">
|
||||||
>
|
|
||||||
<view
|
|
||||||
v-if="showBalanceWarning"
|
|
||||||
class="border border-amber-200 rounded-lg bg-amber-50 p-3 text-sm text-amber-900"
|
|
||||||
>
|
|
||||||
<text class="font-medium">
|
<text class="font-medium">
|
||||||
钱包提示
|
钱包提示
|
||||||
</text>
|
</text>
|
||||||
<text class="mt-1 block leading-relaxed">
|
<text class="mt-1 block leading-relaxed">
|
||||||
检测到您为代理且账户仍有余额(¥{{ (revenueData?.balance ?? 0).toFixed(2) }})或待结账金额(¥{{ (revenueData?.frozen_balance ?? 0).toFixed(2) }})。注销后将无法通过本账号提现,请确认已了解风险。
|
检测到您为代理且账户仍有余额(¥{{ (revenueData?.balance ?? 0).toFixed(2) }})或冻结余额(¥{{ (revenueData?.frozen_balance ??
|
||||||
|
0).toFixed(2) }})。注销后将无法通过本账号提现,请确认已了解风险。
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view v-if="showVipLevelReminder"
|
||||||
v-if="showVipLevelReminder"
|
class="border border-violet-200 rounded-lg bg-violet-50 p-3 text-sm text-violet-900">
|
||||||
class="border border-violet-200 rounded-lg bg-violet-50 p-3 text-sm text-violet-900"
|
|
||||||
>
|
|
||||||
<text class="font-medium">
|
<text class="font-medium">
|
||||||
会员提示
|
会员提示
|
||||||
</text>
|
</text>
|
||||||
@@ -246,22 +236,10 @@ async function submitCancelAccount() {
|
|||||||
|
|
||||||
<view class="cancel-footer flex-shrink-0 border-t border-gray-200 bg-white px-4 pt-3">
|
<view class="cancel-footer flex-shrink-0 border-t border-gray-200 bg-white px-4 pt-3">
|
||||||
<view class="footer-btns flex gap-3">
|
<view class="footer-btns flex gap-3">
|
||||||
<wd-button
|
<wd-button class="footer-btn" hairline plain block type="info" @click="onExit">
|
||||||
class="footer-btn"
|
|
||||||
hairline
|
|
||||||
plain
|
|
||||||
block
|
|
||||||
type="info"
|
|
||||||
@click="onExit"
|
|
||||||
>
|
|
||||||
退出
|
退出
|
||||||
</wd-button>
|
</wd-button>
|
||||||
<wd-button
|
<wd-button class="footer-btn" type="error" block @click="onConfirmTap">
|
||||||
class="footer-btn"
|
|
||||||
type="error"
|
|
||||||
block
|
|
||||||
@click="onConfirmTap"
|
|
||||||
>
|
|
||||||
确认注销
|
确认注销
|
||||||
</wd-button>
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
@@ -269,20 +247,14 @@ async function submitCancelAccount() {
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 有余额时的二次确认(Wot) -->
|
<!-- 有余额时的二次确认(Wot) -->
|
||||||
<wd-popup
|
<wd-popup v-model="showBalancePopup" position="center" round :close-on-click-modal="true"
|
||||||
v-model="showBalancePopup"
|
custom-style="width: 86%; max-width: 360px;" @close="onBalanceCancel">
|
||||||
position="center"
|
|
||||||
round
|
|
||||||
:close-on-click-modal="true"
|
|
||||||
custom-style="width: 86%; max-width: 360px;"
|
|
||||||
@close="onBalanceCancel"
|
|
||||||
>
|
|
||||||
<view class="balance-popup p-5">
|
<view class="balance-popup p-5">
|
||||||
<view class="mb-2 text-center text-base text-gray-900 font-semibold">
|
<view class="mb-2 text-center text-base text-gray-900 font-semibold">
|
||||||
确认注销
|
确认注销
|
||||||
</view>
|
</view>
|
||||||
<view class="text-center text-sm text-gray-600 leading-relaxed">
|
<view class="text-center text-sm text-gray-600 leading-relaxed">
|
||||||
您的代理账户仍有余额或待结账金额,注销后将无法通过本账号提现,确定继续注销?
|
您的代理账户仍有余额或冻结余额,注销后将无法通过本账号提现,确定继续注销?
|
||||||
</view>
|
</view>
|
||||||
<view class="mt-5 flex gap-3">
|
<view class="mt-5 flex gap-3">
|
||||||
<wd-button plain hairline type="info" block class="flex-1" @click="onBalanceCancel">
|
<wd-button plain hairline type="info" block class="flex-1" @click="onBalanceCancel">
|
||||||
@@ -296,15 +268,8 @@ async function submitCancelAccount() {
|
|||||||
</wd-popup>
|
</wd-popup>
|
||||||
|
|
||||||
<!-- 短信验证(与登录页表单风格一致) -->
|
<!-- 短信验证(与登录页表单风格一致) -->
|
||||||
<wd-popup
|
<wd-popup v-model="showSmsPopup" position="bottom" round :safe-area-inset-bottom="true"
|
||||||
v-model="showSmsPopup"
|
:style="{ maxHeight: '85vh' }" :z-index="2000" @close="closeSmsPopup">
|
||||||
position="bottom"
|
|
||||||
round
|
|
||||||
:safe-area-inset-bottom="true"
|
|
||||||
:style="{ maxHeight: '85vh' }"
|
|
||||||
:z-index="2000"
|
|
||||||
@close="closeSmsPopup"
|
|
||||||
>
|
|
||||||
<view class="sms-popup">
|
<view class="sms-popup">
|
||||||
<view class="sms-popup-title">
|
<view class="sms-popup-title">
|
||||||
<text class="sms-popup-title-text">
|
<text class="sms-popup-title-text">
|
||||||
@@ -322,24 +287,11 @@ async function submitCancelAccount() {
|
|||||||
验证码
|
验证码
|
||||||
</text>
|
</text>
|
||||||
<view class="sms-verification-wrap">
|
<view class="sms-verification-wrap">
|
||||||
<wd-input
|
<wd-input ref="verificationCodeInputRef" v-model="cancelAccountCode" class="sms-verification-input"
|
||||||
ref="verificationCodeInputRef"
|
type="number" placeholder="请输入验证码" maxlength="6" no-border clearable>
|
||||||
v-model="cancelAccountCode"
|
|
||||||
class="sms-verification-input"
|
|
||||||
type="number"
|
|
||||||
placeholder="请输入验证码"
|
|
||||||
maxlength="6"
|
|
||||||
no-border
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<wd-button
|
<wd-button size="small" type="primary" plain :disabled="isCountingDown || !isPhoneNumberValid"
|
||||||
size="small"
|
@click="sendVerificationCode">
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
:disabled="isCountingDown || !isPhoneNumberValid"
|
|
||||||
@click="sendVerificationCode"
|
|
||||||
>
|
|
||||||
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
|
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
|
||||||
</wd-button>
|
</wd-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -351,13 +303,7 @@ async function submitCancelAccount() {
|
|||||||
<wd-button plain hairline type="info" block class="flex-1" @click="closeSmsPopup">
|
<wd-button plain hairline type="info" block class="flex-1" @click="closeSmsPopup">
|
||||||
取消
|
取消
|
||||||
</wd-button>
|
</wd-button>
|
||||||
<wd-button
|
<wd-button type="error" block class="flex-1" :disabled="!canSubmitCancel" @click="submitCancelAccount">
|
||||||
type="error"
|
|
||||||
block
|
|
||||||
class="flex-1"
|
|
||||||
:disabled="!canSubmitCancel"
|
|
||||||
@click="submitCancelAccount"
|
|
||||||
>
|
|
||||||
确认注销
|
确认注销
|
||||||
</wd-button>
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ function getDefaultAvatar() {
|
|||||||
|
|
||||||
switch (normalizedLevel.value) {
|
switch (normalizedLevel.value) {
|
||||||
case 'NORMAL':
|
case 'NORMAL':
|
||||||
return '/static/images/shot_nonal.png'
|
return '/static/images/shot_nornal.png'
|
||||||
case 'VIP':
|
case 'VIP':
|
||||||
return '/static/images/shot_vip.png'
|
return '/static/images/shot_vip.png'
|
||||||
case 'SVIP':
|
case 'SVIP':
|
||||||
|
|||||||
@@ -243,14 +243,14 @@ function getRewardTypeClass(type) {
|
|||||||
// 获取收益类型图标
|
// 获取收益类型图标
|
||||||
function getRewardTypeIcon(type) {
|
function getRewardTypeIcon(type) {
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
descendant_promotion: 'gift',
|
descendant_promotion: '/static/images/tgjl.svg',
|
||||||
cost: 'gold-coin',
|
cost: '/static/images/cbgx.svg',
|
||||||
pricing: 'balance-pay',
|
pricing: '/static/images/djgx.svg',
|
||||||
descendant_withdraw: 'cash-back-record',
|
descendant_withdraw: '/static/images/txsy.svg',
|
||||||
descendant_upgrade_vip: 'fire',
|
descendant_upgrade_vip: '/static/images/zhvipjl.svg',
|
||||||
descendant_upgrade_svip: 'fire',
|
descendant_upgrade_svip: '/static/images/zhsvipjl.svg',
|
||||||
}
|
}
|
||||||
return iconMap[type] || 'balance-o'
|
return iconMap[type] || '/static/images/tgjl.svg'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取收益类型描述
|
// 获取收益类型描述
|
||||||
@@ -340,11 +340,7 @@ function formatNumber(num) {
|
|||||||
class="flex items-center rounded-lg p-2"
|
class="flex items-center rounded-lg p-2"
|
||||||
:class="getRewardTypeClass(item.type).split(' ')[0]"
|
:class="getRewardTypeClass(item.type).split(' ')[0]"
|
||||||
>
|
>
|
||||||
<wd-icon
|
<image :src="getRewardTypeIcon(item.type)" class="mr-2 h-5 w-5 flex-shrink-0" />
|
||||||
:name="getRewardTypeIcon(item.type)"
|
|
||||||
class="mr-2 text-lg"
|
|
||||||
:class="getRewardTypeClass(item.type).split(' ')[1]"
|
|
||||||
/>
|
|
||||||
<view class="flex-1">
|
<view class="flex-1">
|
||||||
<view class="text-sm font-medium" :class="getRewardTypeClass(item.type).split(' ')[1]">
|
<view class="text-sm font-medium" :class="getRewardTypeClass(item.type).split(' ')[1]">
|
||||||
{{ item.description }}
|
{{ item.description }}
|
||||||
@@ -374,11 +370,7 @@ function formatNumber(num) {
|
|||||||
<view class="mb-3 border-b border-gray-200 pb-3">
|
<view class="mb-3 border-b border-gray-200 pb-3">
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex items-center space-x-3">
|
<view class="flex items-center space-x-3">
|
||||||
<wd-icon
|
<image :src="getRewardTypeIcon(item.type)" class="h-5 w-5 flex-shrink-0" />
|
||||||
:name="getRewardTypeIcon(item.type)"
|
|
||||||
class="text-lg"
|
|
||||||
:class="getRewardTypeClass(item.type).split(' ')[1]"
|
|
||||||
/>
|
|
||||||
<view>
|
<view>
|
||||||
<view class="text-gray-800 font-medium">
|
<view class="text-gray-800 font-medium">
|
||||||
{{ getRewardTypeDescription(item.type) }}
|
{{ getRewardTypeDescription(item.type) }}
|
||||||
|
|||||||
1
src/static/images/cbgx.svg
Normal file
1
src/static/images/cbgx.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777473236250" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4620" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M449.376731 437.437817c-8.572236 5.121648-15.429411 11.394515-20.572548 18.806321-5.143137 7.424086-7.714705 16.252149-7.714705 26.482141 0 9.210779 1.849114 16.767895 5.571902 22.644743 3.709485 5.889127 9.991562 11.261485 18.85851 16.119119 8.852622 4.869914 20.572548 9.474792 35.145451 13.81668 2.831488 0.846274 5.812379 1.716084 8.93551 2.610454L489.600851 424.285263c-3.89061 0.529049-7.728008 1.199315-11.508102 2.023076C467.51074 428.610778 457.948967 432.328449 449.376731 437.437817z" fill="#16A34A" p-id="4621"></path><path d="M588.671217 628.955712c-8.290827-5.876847-19.862373-11.514241-34.716686-16.887622-6.109138-2.208295-12.95301-4.436032-20.507056-6.686283l0 133.818775c18.307971-2.535753 34.434254-8.438183 48.366567-17.747199 19.138896-12.786211 28.716019-29.169343 28.716019-49.127907 0-9.210779-1.579985-17.270339-4.713348-24.179702C602.666976 641.237433 596.948741 634.844839 588.671217 628.955712z" fill="#16A34A" p-id="4622"></path><path d="M637.607766 253.888408l108.787682-186.695053-469.744617 0 111.330598 185.849802c-148.377352 49.076742-254.959809 184.081529-254.959809 342.947769 0 200.322421 169.461553 362.714975 378.50152 362.714975 209.04099 0 378.502543-162.392554 378.502543-362.714975C890.025683 438.032358 784.659937 303.658951 637.607766 253.888408zM661.1039 719.9174c-9.723455 15.352663-22.581298 28.401864-38.573527 39.148625-16.005532 10.746762-33.859155 19.070334-53.574173 24.947182-11.685133 3.483334-23.52274 5.930059-35.507702 7.351432l0 27.892257c0 12.107759-9.815553 21.923312-21.924335 21.923312s-21.923312-9.815553-21.923312-21.923312l0-27.034726c-16.762779-1.107217-32.461319-3.712555-47.080272-7.825222-20.010753-5.637394-37.006845-12.533454-51.003628-20.72502-14.010085-8.18031-24.859177-16.503882-32.573883-24.948205-7.714705-8.443299-11.57257-15.471366-11.57257-21.10876 0-6.14086 1.715061-13.049201 5.143137-20.72502 3.429099-7.676843 9.710153-11.250228 18.85851-10.746762 5.705955 0 11.7056 2.950192 18.000979 8.827039 6.282077 5.889127 14.425547 12.161994 24.430412 18.806321 9.992585 6.656607 22.849404 12.797467 38.573527 18.422581 10.538007 3.781116 22.953781 6.282077 37.222763 7.52437L489.599827 593.910545c-21.082154-5.122671-39.499619-10.458189-55.223742-16.000416-17.439184-6.14086-31.864732-13.300934-43.288922-21.493523-11.438517-8.179286-19.862373-18.038842-25.287943-29.55206-5.436826-11.514241-8.143471-25.714661-8.143471-42.602284 0-17.391089 3.991917-33.006741 12.001335-46.824445 7.996115-13.81668 18.993586-25.582655 33.002648-35.310204 13.996782-9.715269 30.283724-17.139356 48.859801-22.25998 12.138458-3.341095 24.835641-5.587252 38.080294-6.746658l0-22.745027c0-12.107759 9.815553-21.923312 21.923312-21.923312s21.924335 9.815553 21.924335 21.923312l0 22.614044c13.684674 0.985444 26.530236 2.883677 38.508036 5.726421 18.282389 4.353145 33.994232 9.858532 47.145763 16.502859 13.139252 6.656607 23.426549 13.696953 30.858822 21.10876 7.419993 7.424086 11.143805 14.464433 11.143805 21.10876 0 8.191566-2.00875 15.867386-5.999644 23.028483-4.00522 7.172353-10.580986 10.745738-19.716041 10.745738-4.00522 0-8.009418-1.402953-12.001335-4.221138-4.004197-2.806929-9.723455-7.028067-17.144472-12.665461-13.152555-9.210779-26.290783-16.623609-39.430034-22.261003-8.729825-3.733021-19.859303-6.334265-33.365922-7.817036L533.446451 549.641295c0.832971 0.211824 1.661849 0.421602 2.506077 0.635473 19.421328 5.121648 37.715997 10.627035 54.859445 16.503882 17.144472 5.889127 31.998785 13.313214 44.575218 22.261003 12.56313 8.959046 22.420639 19.705807 29.572526 32.239262 7.139607 12.545734 10.716063 28.282137 10.716063 47.208185C675.676803 687.426405 670.814052 704.56576 661.1039 719.9174z" fill="#16A34A" p-id="4623"></path></svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
1
src/static/images/djgx.svg
Normal file
1
src/static/images/djgx.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777473250794" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5709" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M128.50996 700.685259q0 26.517928 18.358566 45.386454t44.876494 18.868526l253.960159 0 0 127.49004-317.195219 0q-26.517928 0-49.466135-9.689243t-40.286853-27.027888-27.537849-40.796813-10.199203-49.976096l0-637.450199q0-26.517928 10.199203-49.466135t27.537849-40.286853 40.286853-27.537849 49.466135-10.199203l764.940239 0q26.517928 0 49.466135 10.199203t40.286853 27.537849 27.537849 40.286853 10.199203 49.466135l0 188.685259-127.49004 4.079681 0-2.039841q0-26.517928-18.868526-44.876494t-45.386454-18.358566l-637.450199 0q-26.517928 0-44.876494 18.358566t-18.358566 44.876494l0 382.47012zM733.322709 315.155378q60.175299 0 112.701195 22.948207t91.792829 62.215139 62.215139 92.302789 22.948207 113.211155-22.948207 112.701195-62.215139 91.282869-91.792829 61.705179-112.701195 22.948207-112.701195-22.948207-91.792829-61.705179-62.215139-91.282869-22.948207-112.701195 22.948207-113.211155 62.215139-92.302789 91.792829-62.215139 112.701195-22.948207zM804.717131 573.195219l89.752988-84.653386-44.876494-46.916335-100.972112 97.912351 32.63745 33.657371-107.091633 0 32.63745-41.816733-93.832669-91.792829-38.756972 50.996016 85.673307 82.613546-85.673307 0 0 64.25498 127.49004 0 0 63.23506-127.49004 0 0 64.25498 127.49004 0 0 63.23506 64.25498 0 0-63.23506 127.49004 0 0-64.25498-127.49004 0 0-63.23506 127.49004 0 0-64.25498-88.733068 0z" p-id="5710" fill="#9333EA"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
1
src/static/images/tgjl.svg
Normal file
1
src/static/images/tgjl.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777473196138" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2644" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M942.23317 237.091541h-124.741297c-17.841076 35.682153-44.602691 53.961944-80.138605 74.727787h204.441187v147.408238H81.620592V311.965566h204.441187c-35.682153-20.765843-62.297529-39.045634-80.138606-74.727787H81.035639c-41.531686 0-74.289072 32.611148-74.289073 74.289072v148.431906c0 41.531686 32.611148 74.289072 74.289073 74.289073h861.197531c41.531686 0 74.289072-32.611148 74.289072-74.289073V311.380613c0-41.531686-32.611148-74.289072-74.289072-74.289072z m0 0" fill="#2563EB" p-id="2645"></path><path d="M707.666887 240.016308H205.776935c17.841076 35.682153 44.602691 65.368534 80.138605 71.80302h451.437728c35.682153-6.434487 62.297529-36.267106 80.138605-71.80302h-109.824986zM852.735311 534.101591v414.731907H170.679735V534.101591h667.72422m89.059143-74.727787H95.951948v475.128337c0 50.452224 38.606919 89.059144 89.059144 89.059144h653.246624c50.452224 0 89.059144-38.606919 89.059144-89.059144V459.373804z m0 0" fill="#2563EB" p-id="2646"></path><path d="M796.72603 44.056944C764.114883 11.299558 716.587425-3.470513 663.210434 2.525258c-62.297529 8.920538-118.745525 47.527458-151.502911 100.904448-32.611148-53.376991-86.134377-91.98391-151.502911-100.904448-53.523229-8.920538-100.904448 5.995772-133.661834 41.531686-32.611148 35.682153-47.527458 83.20961-41.531686 133.661835 2.924767 23.69061 11.845305 44.602691 20.765843 62.297529h109.824986c-20.765843-17.841076-35.682153-44.602691-41.531686-74.289073-2.924767-20.765843 0-44.602691 14.91631-59.372762 14.91631-14.91631 35.682153-17.841076 59.372763-14.91631 50.452224 5.995772 91.98391 47.527458 103.975453 97.979682l11.845305 50.452224h2.924766v783.544978h89.059144V240.016308h2.924767l11.845304-50.452225c11.845305-50.452224 53.523229-89.059144 103.975454-97.979681 20.765843-2.924767 44.602691 0 59.372762 14.916309 14.91631 14.91631 17.841076 35.682153 14.91631 59.372763-2.924767 29.686381-20.765843 53.523229-41.531686 74.289072h109.824986c8.920538-17.841076 14.91631-38.606919 17.841077-62.297529 8.920538-50.744701-5.849533-98.12592-38.60692-133.808073z m0 0" fill="#2563EB" p-id="2647"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
1
src/static/images/txsy.svg
Normal file
1
src/static/images/txsy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777473287310" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6829" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M552.64 235.072v178.4a38.592 38.592 0 0 1-77.216 0V231.168l-72.512 72.544a38.592 38.592 0 1 1-54.592-54.592l163.776-163.776 163.776 163.776a38.592 38.592 0 1 1-54.592 54.592L552.64 235.072z m38.592 120.48h270.24c21.312 0 38.592 17.28 38.592 38.592v115.84H128v-115.84c0-21.312 17.28-38.592 38.592-38.592h270.24v57.92a77.216 77.216 0 1 0 154.4 0v-57.92z m308.832 231.616v270.24c0 21.312-17.28 38.592-38.592 38.592H166.592C145.28 896 128 878.72 128 857.408v-270.24h772.064zM205.216 722.272h212.32V645.12H205.216v77.184z" fill="#CA8A04" p-id="6830"></path></svg>
|
||||||
|
After Width: | Height: | Size: 891 B |
1
src/static/images/zhsvipjl.svg
Normal file
1
src/static/images/zhsvipjl.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777473314560" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10006" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M336 972.8c-60.8-128-28.8-201.6 19.2-268.8 51.2-76.8 64-150.4 64-150.4s41.6 51.2 25.6 134.4c70.4-80 83.2-208 73.6-256 160 112 230.4 358.4 137.6 537.6 492.8-281.6 121.6-700.8 57.6-745.6 22.4 48 25.6 128-19.2 166.4-73.6-281.6-256-336-256-336 22.4 144-76.8 300.8-172.8 419.2-3.2-57.6-6.4-96-38.4-153.6-6.4 105.6-86.4 188.8-108.8 294.4C89.6 758.4 140.8 860.8 336 972.8L336 972.8z" p-id="10007" fill="#EA580C"></path></svg>
|
||||||
|
After Width: | Height: | Size: 752 B |
1
src/static/images/zhvipjl.svg
Normal file
1
src/static/images/zhvipjl.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777473314560" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10006" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M336 972.8c-60.8-128-28.8-201.6 19.2-268.8 51.2-76.8 64-150.4 64-150.4s41.6 51.2 25.6 134.4c70.4-80 83.2-208 73.6-256 160 112 230.4 358.4 137.6 537.6 492.8-281.6 121.6-700.8 57.6-745.6 22.4 48 25.6 128-19.2 166.4-73.6-281.6-256-336-256-336 22.4 144-76.8 300.8-172.8 419.2-3.2-57.6-6.4-96-38.4-153.6-6.4 105.6-86.4 188.8-108.8 294.4C89.6 758.4 140.8 860.8 336 972.8L336 972.8z" p-id="10007" fill="#DC2626"></path></svg>
|
||||||
|
After Width: | Height: | Size: 752 B |
Reference in New Issue
Block a user