feat: 小程序 AI 底栏与聊天页、出站推送调试与 tabbar 迁移

- 自定义 tabBar:AI 入口图标与样式,相关页面与审核门控调整

- API:MpTabbar、出站推送 Hook、后台设置与迁移 SQL(含 FAB 居中排序)

- 管理端:推送 Hook 面板、出站推送调试工具与生产环境注释说明

Made-with: Cursor
This commit is contained in:
Ghost
2026-04-20 15:29:10 +08:00
parent ce55c48c64
commit d07235b64c
26 changed files with 486 additions and 317 deletions

View File

@@ -17,37 +17,7 @@
留空订阅事件表示三类事件全部推送
</p>
<div class="form-section test-tools">
<h4 class="test-tools-title">测试与调试</h4>
<p class="hint test-tools-hint">
<strong>发送连接测试</strong>只验证 URL 是否可达hook.ping不写去重表<strong>手动测试测评完成</strong>按库表
<code>test_results.id</code> 重放真实 <code>test.result_completed</code>人脸问卷简历等均可
</p>
<div class="test-tools-actions">
<el-button plain :loading="testLoading" @click="sendTest">发送连接测试</el-button>
</div>
<div class="manual-test-row">
<span class="manual-test-label">记录 ID</span>
<el-input
v-model.number="replayTestResultId"
type="number"
:min="1"
step="1"
placeholder="test_results 主键"
class="manual-test-input"
/>
<span class="manual-test-label force-label">强制</span>
<el-switch v-model="replayForce" />
<el-button
type="primary"
class="manual-test-btn"
:loading="replayLoading"
@click="replayTestResult"
>
手动测试测评完成
</el-button>
</div>
</div>
/api/ai/chat
</div>
<div class="form-section">
<div class="form-item row-line">
@@ -81,7 +51,11 @@
{{ opt.label }}
</el-checkbox>
</el-checkbox-group>
<p class="hint">与文档一致<code>lead.order_paid</code><code>lead.phone_bound</code><code>test.result_completed</code>全部勾选保存后将以空列表存库表示订阅全部上方手动测试测评完成与真实落库推送使用同一套订阅校验</p>
<p class="hint">
与文档一致<code>lead.order_paid</code><code>lead.phone_bound</code><code>test.result_completed</code>全部勾选保存后将以空列表存库表示订阅全部
<template v-if="showTestTools">上方手动测试测评完成与真实落库推送使用同一套订阅校验</template>
<template v-else>保存后与真实落库推送使用同一套订阅校验</template>
</p>
</div>
</div>
<div class="save-actions save-actions-row">
@@ -103,8 +77,10 @@ const props = withDefaults(
defineProps<{
/** 如 /admin 或 /superadmin对应 request 基路径下的 settings */
apiPrefix: string
/** 是否显示「测试与调试」(默认 false超管由父组件按环境传入 */
showTestTools?: boolean
}>(),
{ apiPrefix: '/admin' }
{ apiPrefix: '/admin', showTestTools: false }
)
const eventOptions = [

View File

@@ -153,7 +153,7 @@
</div>
<div v-if="activeTab === 'pushhook'" class="tab-content">
<PushHookConfigPanel api-prefix="/admin" />
<PushHookConfigPanel api-prefix="/admin" :show-test-tools="false" />
</div>
<div v-if="activeTab === 'finance'" class="embed-wrap">

View File

@@ -385,7 +385,7 @@
</div>
<div v-if="activeTab === 'pushhook'" class="tab-content">
<PushHookConfigPanel api-prefix="/superadmin" />
<PushHookConfigPanel api-prefix="/superadmin" :show-test-tools="showOutboundPushDebug" />
</div>
<!-- 账户安全 -->
@@ -474,6 +474,7 @@ import {
} from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { request } from '@/utils/request'
import { isOutboundPushDebugVisible } from '@/utils/outboundPushDebug'
import PosterEditor from './PosterEditor.vue'
import Questions from './Questions.vue'
import PushHookConfigPanel from '../admin/PushHookConfigPanel.vue'
@@ -496,6 +497,9 @@ function isTabId(s: string): s is TabId {
const route = useRoute()
const router = useRouter()
/** 出站推送测试区:仅开发或 VITE_SHOW_OUTBOUND_PUSH_DEBUG=true生产包默认隐藏 */
const showOutboundPushDebug = isOutboundPushDebugVisible()
const activeTab = ref<TabId>('review')
const saveSuccess = ref<string | null>(null)