微信小程序:自定义底栏接入 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

@@ -133,17 +133,7 @@ interface NavItem {
const navMainItems: NavItem[] = [
{ path: '/superadmin/ops', icon: TrendCharts, label: '总览' },
{
path: '/superadmin/enterprises',
icon: OfficeBuilding,
label: '企业管理',
children: [
{ path: '/superadmin/enterprises', icon: OfficeBuilding, label: '企业列表' },
{ path: '/superadmin/soul-articles', icon: Share, label: 'Soul 引流文章' },
{ path: '/superadmin/mp-tabbar', icon: Setting, label: '小程序底部菜单' },
{ path: '/superadmin/profit-rules', icon: Share, label: '分账规则' }
]
},
{ path: '/superadmin/enterprises', icon: OfficeBuilding, label: '企业管理' },
{ path: '/superadmin/commerce', icon: ShoppingCart, label: '订单和财务' },
{ path: '/superadmin/distribution', icon: Share, label: '分销管理' },
{ path: '/superadmin/ai-config', icon: Cpu, label: '智能算力' }

View File

@@ -149,18 +149,20 @@ const routes: RouteRecordRaw[] = [
{
path: 'mp-tabbar',
name: 'SuperAdminMpTabBar',
redirect: to => ({
path: '/superadmin/enterprises',
query: { ...to.query, tab: 'mpTabbar' }
})
redirect: to => {
const q = { ...to.query } as Record<string, unknown>
delete q.tab
return { path: '/superadmin/enterprises', query: q }
}
},
{
path: 'profit-rules',
name: 'SuperAdminProfitRules',
redirect: to => ({
path: '/superadmin/enterprises',
query: { ...to.query, tab: 'profitRules' }
})
redirect: to => {
const q = { ...to.query } as Record<string, unknown>
delete q.tab
return { path: '/superadmin/enterprises', query: q }
}
},
{
path: 'settings',

View File

@@ -27,8 +27,6 @@
<Enterprises v-if="activeTab === 'companies'" embedded />
<Users v-if="activeTab === 'users'" :key="usersRefreshKey" embedded />
<SoulArticles v-if="activeTab === 'soulArticles'" />
<MpTabBar v-if="activeTab === 'mpTabbar'" />
<ProfitRules v-if="activeTab === 'profitRules'" />
</div>
</div>
</template>
@@ -41,18 +39,19 @@ import { request } from '@/utils/request'
import Enterprises from './Enterprises.vue'
import Users from './Users.vue'
import SoulArticles from './SoulArticles.vue'
import MpTabBar from './MpTabBar.vue'
import ProfitRules from './ProfitRules.vue'
import SaPageHeader from '@/components/superadmin/SaPageHeader.vue'
import SaTabs from '@/components/superadmin/SaTabs.vue'
const TAB_IDS = ['companies', 'users', 'soulArticles', 'mpTabbar', 'profitRules'] as const
const TAB_IDS = ['companies', 'users', 'soulArticles'] as const
type TabId = (typeof TAB_IDS)[number]
function isTabId(s: string): s is TabId {
return (TAB_IDS as readonly string[]).includes(s)
}
/** 已从页签隐藏的 tab旧链接兼容 */
const LEGACY_TAB_IDS = ['mpTabbar', 'profitRules']
const route = useRoute()
const router = useRouter()
const activeTab = ref<TabId>('companies')
@@ -97,14 +96,28 @@ async function runOrphanMigrate() {
const innerTabs: { label: string; value: TabId }[] = [
{ label: '企业列表', value: 'companies' },
{ label: '用户总览', value: 'users' },
{ label: '引流文章', value: 'soulArticles' },
{ label: '底部菜单', value: 'mpTabbar' },
{ label: '分账规则', value: 'profitRules' }
{ label: '引流文章', value: 'soulArticles' }
]
function queryWithoutTab(): Record<string, string> {
const q: Record<string, string> = {}
Object.entries(route.query).forEach(([k, v]) => {
if (v !== undefined && v !== null && k !== 'tab') {
q[k] = Array.isArray(v) ? String(v[0]) : String(v)
}
})
return q
}
const applyRouteTab = () => {
const t = route.query.tab
if (typeof t === 'string' && isTabId(t)) {
const t = typeof route.query.tab === 'string' ? route.query.tab : ''
if (t && LEGACY_TAB_IDS.includes(t)) {
activeTab.value = 'companies'
const q = queryWithoutTab()
router.replace({ path: '/superadmin/enterprises', query: Object.keys(q).length ? q : {} })
return
}
if (isTabId(t)) {
activeTab.value = t
} else {
activeTab.value = 'companies'