微信小程序:自定义底栏接入 AI 入口(含图标与样式)、AI 聊天相关页面与历史/报告等能力;审核模式与配置联动;资料/企业/首页/授权等页面与 app 入口配合调整。

后端 API:AI 对话/报告/余额告警 等服务与路由;小程序 tabBar 配置与分润相关能力;出站推送 Webhook 与管理端调试;飞书留资、小程序数据分析 等支撑;多份 数据库迁移(AI 聊天、灵魂文章、tabBar、分润等)。
管理端 / 超管:看板、用户/订单/分销/财务、设置与 小程序 tabBar 管理、分润规则、灵魂文章、AI 配置与监控 等大量页面与能力扩展;主题与布局更新。
抖音小程序:与微信侧在 结果页/购买/个人中心 等方向做了同步或优化。
另含 部署/脚本、开发文档 等配套更新。
邀请码与解锁:后端 分销/邀请码 接口与 add_invite_codes 等 SQL;小程序侧 邀请码弹窗组件、个人中心/推广/多类结果页 的填写入口与 解锁门控(inviteCodeGate、unlockGate);并补充 《小程序解锁引导与邀请码方案》 文档。
超管后台:路由与 企业中心(EnterpriseHub) 等交互与结构优化。
小程序体验:在 首页结果、推广、各测评结果页 上继续打磨;与 邀请码门控、审核门控 小步联动。
This commit is contained in:
Ghost
2026-04-23 11:28:16 +08:00
parent d07235b64c
commit d7137f9349
53 changed files with 1817 additions and 226 deletions

View File

@@ -1,6 +1,8 @@
// pages/promo/index.js
const app = getApp()
const { request } = require('../../utils/request')
const inviteCodeGate = require('../../utils/inviteCodeGate.js')
const { shouldHideInviteCodeEntry } = require('../../utils/miniprogramAuditGate.js')
Page({
data: {
@@ -30,7 +32,16 @@ Page({
withdrawAmountInput: '',
withdrawError: '',
withdrawFeeYuan: '0.00',
withdrawActualYuan: '0.00'
withdrawActualYuan: '0.00',
myInviteCode: '',
myInviteCodeMeta: null,
/** loading | ready | error | nodata — 避免因接口失败整块不渲染 */
myInviteCodeLoadState: 'loading',
myInviteCodeErrMsg: '',
myInviteCodeHint: '',
showInviteCodeDialog: false,
/** 与结果页一致:审核/提审不展示邀请码相关块 */
hideInviteCodeEntry: false
},
onLoad() {
@@ -41,7 +52,10 @@ Page({
wx.showToast({ title: '请先登录后查看推广中心', icon: 'none' })
return
}
const hide = shouldHideInviteCodeEntry(app.globalData || {})
this.setData({ hideInviteCodeEntry: hide })
this.loadStats()
if (!hide) this.loadMyInviteCode()
this.loadBindings(true)
})
.catch(() => {
@@ -50,8 +64,104 @@ Page({
},
onShow() {
const hide = shouldHideInviteCodeEntry(app.globalData || {})
this.setData({ hideInviteCodeEntry: hide })
const token = app.globalData.token || wx.getStorageSync('token')
if (token) this.loadStats()
if (token) {
this.loadStats()
if (!hide) this.loadMyInviteCode()
}
},
loadMyInviteCode() {
this.setData({
myInviteCodeLoadState: 'loading',
myInviteCodeErrMsg: ''
})
request({
url: '/api/distribution/my-invite-code',
method: 'GET',
success: (res) => {
const payload = res && res.data
const httpOk = res.statusCode >= 200 && res.statusCode < 300
const bizOk = payload && Number(payload.code) === 200
if (!httpOk || !bizOk) {
const msg =
(payload && payload.message) ||
(typeof res.statusCode === 'number' ? `请求失败 (${res.statusCode})` : '加载失败')
this.setData({
myInviteCode: '',
myInviteCodeMeta: null,
myInviteCodeLoadState: 'error',
myInviteCodeErrMsg: msg,
myInviteCodeHint: ''
})
return
}
const data = payload.data || {}
const raw = data.code != null ? String(data.code).trim() : ''
const code = raw ? raw.toUpperCase() : ''
const hint = (data.hint && String(data.hint).trim()) || ''
if (code) {
this.setData({
myInviteCode: code,
myInviteCodeMeta: data,
myInviteCodeLoadState: 'ready',
myInviteCodeErrMsg: '',
myInviteCodeHint: ''
})
} else {
this.setData({
myInviteCode: '',
myInviteCodeMeta: data,
myInviteCodeLoadState: 'nodata',
myInviteCodeErrMsg: '',
myInviteCodeHint: hint || '暂无可用邀请码'
})
}
},
fail: () => {
this.setData({
myInviteCode: '',
myInviteCodeMeta: null,
myInviteCodeLoadState: 'error',
myInviteCodeErrMsg: '网络异常,请稍后重试',
myInviteCodeHint: ''
})
}
})
},
retryMyInviteCode() {
this.loadMyInviteCode()
},
copyMyInviteCode() {
const code = (this.data.myInviteCode || '').trim()
if (!code) return
wx.setClipboardData({
data: code,
success: () => wx.showToast({ title: '已复制邀请码', icon: 'success' })
})
},
/** 填写他人邀请码(与结果页支付前弹框同一组件) */
openInviteCodeFill() {
app.ensureLogin().then((ok) => {
if (!ok) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
inviteCodeGate.openInviteCodeDialog(this)
})
},
onInviteCodeSkip() {
inviteCodeGate.finishInviteCodeGate(this, true)
},
onInviteCodeSuccess() {
inviteCodeGate.finishInviteCodeGate(this, true)
},
/** 加载推广统计数据 */

View File

@@ -1,5 +1,8 @@
{
"navigationBarTitleText": "推广中心",
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTextStyle": "black"
"navigationBarTextStyle": "black",
"usingComponents": {
"invite-code-dialog": "/components/invite-code-dialog/invite-code-dialog"
}
}

View File

@@ -31,6 +31,40 @@
</view>
</view>
<block wx:if="{{!hideInviteCodeEntry}}">
<view class="my-invite-card">
<view class="my-invite-card__head">
<text class="my-invite-card__title">我的邀请码</text>
<view wx:if="{{myInviteCodeLoadState === 'ready' && myInviteCode}}" class="my-invite-card__copy" bindtap="copyMyInviteCode">复制</view>
<view wx:elif="{{myInviteCodeLoadState === 'error'}}" class="my-invite-card__retry" bindtap="retryMyInviteCode">重试</view>
</view>
<block wx:if="{{myInviteCodeLoadState === 'loading'}}">
<text class="my-invite-card__code my-invite-card__code--muted">加载中…</text>
</block>
<block wx:elif="{{myInviteCodeLoadState === 'error'}}">
<text class="my-invite-card__err">{{myInviteCodeErrMsg}}</text>
<text class="my-invite-card__tip">若提示功能未就绪,请在数据库执行 api/database/add_invite_codes.sql 建表后再试。</text>
</block>
<block wx:elif="{{myInviteCodeLoadState === 'ready' && myInviteCode}}">
<text class="my-invite-card__code" user-select="true">{{myInviteCode}}</text>
<text class="my-invite-card__tip">好友在付费解锁前填写此码,即可与你建立推广绑定</text>
</block>
<block wx:else>
<text class="my-invite-card__err">{{myInviteCodeHint}}</text>
<text wx:if="{{myInviteCodeLoadState === 'nodata'}}" class="my-invite-card__tip">你可稍后下拉刷新或联系管理员。</text>
</block>
</view>
<view class="invite-fill-entry" bindtap="openInviteCodeFill">
<text class="invite-fill-entry__icon">🔗</text>
<view class="invite-fill-entry__main">
<text class="invite-fill-entry__title">填写好友邀请码</text>
<text class="invite-fill-entry__sub">绑定推广关系 · 付费前也可在此填写</text>
</view>
<text class="invite-fill-entry__arrow"></text>
</view>
</block>
<!-- 自定义提现金额弹框 -->
<view class="withdraw-mask" wx:if="{{showWithdrawDialog}}">
<view class="withdraw-dialog">
@@ -216,4 +250,10 @@
</view>
<view class="safe-bottom"></view>
<invite-code-dialog
visible="{{showInviteCodeDialog}}"
bind:skip="onInviteCodeSkip"
bind:success="onInviteCodeSuccess"
/>
</view>

View File

@@ -19,6 +19,70 @@ page {
margin-bottom: 40rpx;
}
/* 我的邀请码 */
.my-invite-card {
background: #fff;
border-radius: 24rpx;
padding: 28rpx 32rpx;
margin-bottom: 32rpx;
border: 2rpx solid #fce7f3;
box-shadow: 0 4rpx 24rpx rgba(244, 63, 94, 0.06);
}
.my-invite-card__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.my-invite-card__title {
font-size: 28rpx;
font-weight: 700;
color: #111827;
}
.my-invite-card__copy {
font-size: 24rpx;
font-weight: 600;
color: #e11d48;
padding: 10rpx 24rpx;
background: #fff1f2;
border-radius: 999rpx;
}
.my-invite-card__code {
display: block;
font-size: 40rpx;
font-weight: 800;
letter-spacing: 0.12em;
color: #0f172a;
margin-bottom: 12rpx;
}
.my-invite-card__tip {
display: block;
font-size: 22rpx;
line-height: 1.45;
color: #64748b;
}
.my-invite-card__code--muted {
font-size: 30rpx;
font-weight: 600;
letter-spacing: normal;
color: #94a3b8;
}
.my-invite-card__err {
display: block;
font-size: 26rpx;
line-height: 1.5;
color: #b45309;
margin-bottom: 8rpx;
}
.my-invite-card__retry {
font-size: 24rpx;
font-weight: 600;
color: #e11d48;
padding: 10rpx 24rpx;
background: #fff1f2;
border-radius: 999rpx;
}
.hero-header-row {
display: flex;
justify-content: space-between;