Merge branch 'fix/recruit-online-entry-visible'

This commit is contained in:
田香琪
2026-09-16 09:48:06 +08:00
4 changed files with 137 additions and 14 deletions

View File

@@ -13,12 +13,27 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from '@/store' import { useStore } from '@/store'
import {
checkSelfRecruitEntry,
DISTRIBUTION_CLOSE_MESSAGE,
DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE,
} from '@/utils/distributionStatus'
const store = useStore() const store = useStore()
const lightColor = computed(() => store.getters.lightColor) const lightColor = computed(() => store.getters.lightColor)
function goJoin() { function goJoin() {
uni.redirectTo({ url: '/pages/mine/distribution/join' }) checkSelfRecruitEntry().then((gate) => {
if (gate === 'distribution_closed') {
uni.showToast({ title: DISTRIBUTION_CLOSE_MESSAGE, icon: 'none' })
return
}
if (gate === 'entry_closed') {
uni.showToast({ title: DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE, icon: 'none' })
return
}
uni.redirectTo({ url: '/pages/mine/distribution/join' })
})
} }
</script> </script>

View File

@@ -1,6 +1,7 @@
<template> <template>
<view class="wrapper" :style="themeStyle"> <view class="wrapper" :style="themeStyle">
<view v-if="loading" class="loading">加载中...</view> <view v-if="loading" class="loading">加载中...</view>
<view v-else-if="isDistributionClosed" class="empty">{{ DISTRIBUTION_CLOSE_MESSAGE }}</view>
<view v-else-if="isRetreated" class="page-body"> <view v-else-if="isRetreated" class="page-body">
<view class="section result-section"> <view class="section result-section">
<view class="result-icon retreated">!</view> <view class="result-icon retreated">!</view>
@@ -23,8 +24,8 @@
<view class="result-desc">{{ DISTRIBUTION_APPLY_MESSAGE }}</view> <view class="result-desc">{{ DISTRIBUTION_APPLY_MESSAGE }}</view>
</view> </view>
</view> </view>
<view v-else-if="!plan"> <view v-else-if="onlineEntryClosed || !plan">
<view class="empty">暂未开放线上招募</view> <view class="empty">{{ DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE }}</view>
</view> </view>
<view v-else class="page-body" :class="{ 'with-footer': showLoginFooter || showManualApply }"> <view v-else class="page-body" :class="{ 'with-footer': showLoginFooter || showManualApply }">
<!-- 邀请人信息来自图文邀请卡 --> <!-- 邀请人信息来自图文邀请卡 -->
@@ -185,13 +186,20 @@ import config from '@/config/config'
import { parseGoodsImageUrl, tipsToLogin } from '@/utils/filters.js' import { parseGoodsImageUrl, tipsToLogin } from '@/utils/filters.js'
import { resolveRecruitDistributionIdFromScene } from '@/utils/distributionBind.js' import { resolveRecruitDistributionIdFromScene } from '@/utils/distributionBind.js'
import { getRecruitPage, getRecruitProgress, getRecruitInviter, submitRecruitApplication } from '@/api/distribution' import { getRecruitPage, getRecruitProgress, getRecruitInviter, submitRecruitApplication } from '@/api/distribution'
import { DISTRIBUTION_APPLY_MESSAGE, DISTRIBUTION_RETREAT_MESSAGE, DISTRIBUTION_STATUS } from '@/utils/distributionStatus' import {
DISTRIBUTION_APPLY_MESSAGE,
DISTRIBUTION_CLOSE_MESSAGE,
DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE,
DISTRIBUTION_RETREAT_MESSAGE,
DISTRIBUTION_STATUS,
} from '@/utils/distributionStatus'
const defaultAvatar = config.defaultUserPhoto const defaultAvatar = config.defaultUserPhoto
const store = useStore() const store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme)) const themeStyle = computed(() => getThemeStyle(store.state.theme))
const loading = ref(true) const loading = ref(true)
const isDistributionClosed = ref(false)
const pageInfo = ref<any>({}) const pageInfo = ref<any>({})
const progress = ref<any>(null) const progress = ref<any>(null)
const formItems = reactive<Record<string, string>>({}) const formItems = reactive<Record<string, string>>({})
@@ -217,6 +225,11 @@ const distributionStatus = computed(() => pageInfo.value.distributionStatus || '
const currentApplication = computed(() => pageInfo.value.currentApplication || null) const currentApplication = computed(() => pageInfo.value.currentApplication || null)
const isRetreated = computed(() => distributionStatus.value === DISTRIBUTION_STATUS.RETREAT) const isRetreated = computed(() => distributionStatus.value === DISTRIBUTION_STATUS.RETREAT)
const hasRecruitToken = computed(() => !!recruitToken.value)
const onlineEntryClosed = computed(() =>
pageInfo.value.onlineEntryVisible !== true && !hasRecruitToken.value
)
const showPoster = computed(() => { const showPoster = computed(() => {
if (isRetreated.value) return false if (isRetreated.value) return false
const p = plan.value const p = plan.value
@@ -270,7 +283,7 @@ const isManualApply = computed(() => {
}) })
const showLoginFooter = computed(() => { const showLoginFooter = computed(() => {
return !isRetreated.value && !!plan.value && !isUserLoggedIn.value && !loading.value return !isDistributionClosed.value && !onlineEntryClosed.value && !isRetreated.value && !!plan.value && !isUserLoggedIn.value && !loading.value
}) })
const showInviterHeader = computed(() => { const showInviterHeader = computed(() => {
@@ -284,15 +297,15 @@ const inviterName = computed(() => inviterInfo.value?.memberName || '分销员')
const inviterAvatar = computed(() => parseGoodsImageUrl(inviterInfo.value?.memberAvatar) || defaultAvatar) const inviterAvatar = computed(() => parseGoodsImageUrl(inviterInfo.value?.memberAvatar) || defaultAvatar)
const showManualApply = computed(() => { const showManualApply = computed(() => {
return !isRetreated.value && isUserLoggedIn.value && !showApplyResult.value && isManualApply.value return !isDistributionClosed.value && !onlineEntryClosed.value && !isRetreated.value && isUserLoggedIn.value && !showApplyResult.value && isManualApply.value
}) })
const showAutoApplyTip = computed(() => { const showAutoApplyTip = computed(() => {
return !isRetreated.value && isUserLoggedIn.value && !showApplyResult.value && conditionsMet.value && plan.value?.applyMode === 'AUTO' return !isDistributionClosed.value && !onlineEntryClosed.value && !isRetreated.value && isUserLoggedIn.value && !showApplyResult.value && conditionsMet.value && plan.value?.applyMode === 'AUTO'
}) })
const showApplyForm = computed(() => { const showApplyForm = computed(() => {
return !isRetreated.value && !!plan.value?.requireApplyInfo && fields.value.length > 0 return !isDistributionClosed.value && !onlineEntryClosed.value && !isRetreated.value && !!plan.value?.requireApplyInfo && fields.value.length > 0
}) })
const canApply = computed(() => { const canApply = computed(() => {
@@ -367,6 +380,7 @@ function syncLoginState() {
function loadPageData() { function loadPageData() {
syncLoginState() syncLoginState()
loading.value = true loading.value = true
isDistributionClosed.value = false
applySubmitted.value = false applySubmitted.value = false
submitAuditMode.value = '' submitAuditMode.value = ''
submitApplicationStatus.value = '' submitApplicationStatus.value = ''
@@ -379,7 +393,14 @@ function loadPageData() {
Promise.all(tasks) Promise.all(tasks)
.then((results) => { .then((results) => {
const pageRes = results[0] const pageRes = results[0]
const pageResult = pageRes.data?.result || {} const pageData = pageRes.data || {}
if (pageData.code == 22000) {
isDistributionClosed.value = true
pageInfo.value = {}
progress.value = emptyProgress()
return
}
const pageResult = pageData.result || {}
pageInfo.value = pageResult pageInfo.value = pageResult
if (results.length > 1) { if (results.length > 1) {
progress.value = results[1].data?.result || emptyProgress() progress.value = results[1].data?.result || emptyProgress()

View File

@@ -136,7 +136,13 @@ import { distribution as fetchDistribution } from '@/api/distribution'
import configs from '@/config/config' import configs from '@/config/config'
import storage from '@/utils/storage' import storage from '@/utils/storage'
import { tipsToLogin, setClipboard } from '@/utils/filters.js' import { tipsToLogin, setClipboard } from '@/utils/filters.js'
import { DISTRIBUTION_RETREAT_MESSAGE, DISTRIBUTION_STATUS } from '@/utils/distributionStatus' import {
checkSelfRecruitEntry,
DISTRIBUTION_CLOSE_MESSAGE,
DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE,
DISTRIBUTION_RETREAT_MESSAGE,
DISTRIBUTION_STATUS,
} from '@/utils/distributionStatus'
const sharingShow = ref(false) const sharingShow = ref(false)
const sharingLink = ref('') const sharingLink = ref('')
@@ -191,6 +197,28 @@ function goRecruitJoin() {
}) })
} }
function enterSelfRecruitOrToast() {
checkSelfRecruitEntry().then((gate) => {
if (gate === 'distribution_closed') {
uni.showToast({
title: DISTRIBUTION_CLOSE_MESSAGE,
duration: 2000,
icon: 'none',
})
return
}
if (gate === 'entry_closed') {
uni.showToast({
title: DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE,
duration: 2000,
icon: 'none',
})
return
}
goRecruitJoin()
})
}
function distribution() { function distribution() {
if (!tipsToLogin('normal')) { if (!tipsToLogin('normal')) {
return return
@@ -204,7 +232,7 @@ function distribution() {
url: '/pages/mine/distribution/home', url: '/pages/mine/distribution/home',
}) })
} else if (type == DISTRIBUTION_STATUS.REFUSE) { } else if (type == DISTRIBUTION_STATUS.REFUSE) {
goRecruitJoin() enterSelfRecruitOrToast()
} else if (type == DISTRIBUTION_STATUS.RETREAT) { } else if (type == DISTRIBUTION_STATUS.RETREAT) {
uni.showToast({ uni.showToast({
title: DISTRIBUTION_RETREAT_MESSAGE, title: DISTRIBUTION_RETREAT_MESSAGE,
@@ -225,7 +253,7 @@ function distribution() {
icon: 'none', icon: 'none',
}) })
} else { } else {
goRecruitJoin() enterSelfRecruitOrToast()
} }
}) })
} }

View File

@@ -8,7 +8,7 @@
* @date 2026-09-09 * @date 2026-09-09
*/ */
import { distribution } from '@/api/distribution' import { distribution, getRecruitPage } from '@/api/distribution'
export const DISTRIBUTION_STATUS = { export const DISTRIBUTION_STATUS = {
PASS: 'PASS', PASS: 'PASS',
@@ -24,6 +24,17 @@ export const DISTRIBUTION_APPLY_MESSAGE = '您的信息正在审核'
export const DISTRIBUTION_NOT_MEMBER_MESSAGE = '您还不是分销员' export const DISTRIBUTION_NOT_MEMBER_MESSAGE = '您还不是分销员'
/** 线上自助招募入口关闭22009 */
export const DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE = '暂未开放线上招募'
/** 邀请 token 无效或已失效22010 */
export const DISTRIBUTION_RECRUIT_INVITE_INVALID_MESSAGE = '邀请无效或已失效'
/** 分销总闸关闭时的前端展示文案(接口码 22000 */
export const DISTRIBUTION_CLOSE_MESSAGE = '分销功能暂未开启'
const DISTRIBUTION_CLOSE_CODE = 22000
const JOIN_PATH = '/pages/mine/distribution/join' const JOIN_PATH = '/pages/mine/distribution/join'
const MINE_TAB = '/pages/tabbar/user/my' const MINE_TAB = '/pages/tabbar/user/my'
const TOAST_DURATION = 2000 const TOAST_DURATION = 2000
@@ -61,6 +72,42 @@ function goRecruitJoin() {
}) })
} }
/**
* 关闭判断与后端一致onlineEntryVisible !== true含 false / null / 缺字段)。
*/
export function isOnlineEntryVisible(page) {
return !!(page && page.onlineEntryVisible === true)
}
/**
* 解析招募页响应,决定自助入口是否可进。
* 22000 不算 fail-open其它非 success 或网络失败为 fail-open。
* @returns {'open'|'entry_closed'|'distribution_closed'|'fail_open'}
*/
export function resolveRecruitPageGate(res) {
const data = res && res.data ? res.data : res
if (!data) {
return 'fail_open'
}
if (data.code == DISTRIBUTION_CLOSE_CODE) {
return 'distribution_closed'
}
if (data.success) {
return isOnlineEntryVisible(data.result) ? 'open' : 'entry_closed'
}
return 'fail_open'
}
/**
* 拉招募页判断自助入口。仅成功且入口关闭时拦截22000 走总闸;失败 fail-open。
* @returns {Promise<'open'|'entry_closed'|'distribution_closed'|'fail_open'>}
*/
export function checkSelfRecruitEntry() {
return getRecruitPage()
.then((res) => resolveRecruitPageGate(res))
.catch((err) => resolveRecruitPageGate(err && err.data ? err : null))
}
/** /**
* 能力页非 PASS 提示并退出。RETREAT 用统一清退文案APPLY / REFUSE 与入口一致。 * 能力页非 PASS 提示并退出。RETREAT 用统一清退文案APPLY / REFUSE 与入口一致。
*/ */
@@ -72,7 +119,19 @@ export function leaveInactiveDistributor(member) {
return return
} }
if (status === DISTRIBUTION_STATUS.REFUSE) { if (status === DISTRIBUTION_STATUS.REFUSE) {
goRecruitJoin() checkSelfRecruitEntry().then((gate) => {
if (gate === 'distribution_closed') {
showStatusToast(DISTRIBUTION_CLOSE_MESSAGE)
leaveCapabilityPage()
return
}
if (gate === 'entry_closed') {
showStatusToast(DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE)
leaveCapabilityPage()
return
}
goRecruitJoin()
})
return return
} }
if (status === DISTRIBUTION_STATUS.APPLY) { if (status === DISTRIBUTION_STATUS.APPLY) {