diff --git a/pages/mine/distribution/auth.vue b/pages/mine/distribution/auth.vue
index eabd78e..fd9f1c1 100644
--- a/pages/mine/distribution/auth.vue
+++ b/pages/mine/distribution/auth.vue
@@ -13,12 +13,27 @@
diff --git a/pages/mine/distribution/join.vue b/pages/mine/distribution/join.vue
index ec05a28..4193a2a 100644
--- a/pages/mine/distribution/join.vue
+++ b/pages/mine/distribution/join.vue
@@ -1,6 +1,7 @@
加载中...
+ {{ DISTRIBUTION_CLOSE_MESSAGE }}
!
@@ -23,8 +24,8 @@
{{ DISTRIBUTION_APPLY_MESSAGE }}
-
- 暂未开放线上招募
+
+ {{ DISTRIBUTION_ONLINE_ENTRY_CLOSED_MESSAGE }}
@@ -185,13 +186,20 @@ import config from '@/config/config'
import { parseGoodsImageUrl, tipsToLogin } from '@/utils/filters.js'
import { resolveRecruitDistributionIdFromScene } from '@/utils/distributionBind.js'
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 store = useStore()
const themeStyle = computed(() => getThemeStyle(store.state.theme))
const loading = ref(true)
+const isDistributionClosed = ref(false)
const pageInfo = ref({})
const progress = ref(null)
const formItems = reactive>({})
@@ -217,6 +225,11 @@ const distributionStatus = computed(() => pageInfo.value.distributionStatus || '
const currentApplication = computed(() => pageInfo.value.currentApplication || null)
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(() => {
if (isRetreated.value) return false
const p = plan.value
@@ -270,7 +283,7 @@ const isManualApply = 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(() => {
@@ -284,15 +297,15 @@ const inviterName = computed(() => inviterInfo.value?.memberName || '分销员')
const inviterAvatar = computed(() => parseGoodsImageUrl(inviterInfo.value?.memberAvatar) || defaultAvatar)
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(() => {
- 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(() => {
- 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(() => {
@@ -367,6 +380,7 @@ function syncLoginState() {
function loadPageData() {
syncLoginState()
loading.value = true
+ isDistributionClosed.value = false
applySubmitted.value = false
submitAuditMode.value = ''
submitApplicationStatus.value = ''
@@ -379,7 +393,14 @@ function loadPageData() {
Promise.all(tasks)
.then((results) => {
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
if (results.length > 1) {
progress.value = results[1].data?.result || emptyProgress()
diff --git a/pages/tabbar/user/utils/tool.vue b/pages/tabbar/user/utils/tool.vue
index 00d1ed1..716b764 100644
--- a/pages/tabbar/user/utils/tool.vue
+++ b/pages/tabbar/user/utils/tool.vue
@@ -136,7 +136,13 @@ import { distribution as fetchDistribution } from '@/api/distribution'
import configs from '@/config/config'
import storage from '@/utils/storage'
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 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() {
if (!tipsToLogin('normal')) {
return
@@ -204,7 +232,7 @@ function distribution() {
url: '/pages/mine/distribution/home',
})
} else if (type == DISTRIBUTION_STATUS.REFUSE) {
- goRecruitJoin()
+ enterSelfRecruitOrToast()
} else if (type == DISTRIBUTION_STATUS.RETREAT) {
uni.showToast({
title: DISTRIBUTION_RETREAT_MESSAGE,
@@ -225,7 +253,7 @@ function distribution() {
icon: 'none',
})
} else {
- goRecruitJoin()
+ enterSelfRecruitOrToast()
}
})
}
diff --git a/utils/distributionStatus.js b/utils/distributionStatus.js
index ba806ec..7a2633e 100644
--- a/utils/distributionStatus.js
+++ b/utils/distributionStatus.js
@@ -8,7 +8,7 @@
* @date 2026-09-09
*/
-import { distribution } from '@/api/distribution'
+import { distribution, getRecruitPage } from '@/api/distribution'
export const DISTRIBUTION_STATUS = {
PASS: 'PASS',
@@ -24,6 +24,17 @@ export const DISTRIBUTION_APPLY_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 MINE_TAB = '/pages/tabbar/user/my'
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 与入口一致。
*/
@@ -72,7 +119,19 @@ export function leaveInactiveDistributor(member) {
return
}
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
}
if (status === DISTRIBUTION_STATUS.APPLY) {