feat(ops): ECR-007 行为分析与 ECR-008 内容运营后台
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s

落地埋点 ingest/数据看板、首页宫格 CMS 与测评上下架;含账号引导、问答流式与免责声明去重,以及 review P1 审计同事务修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-07 02:26:16 +08:00
co-authored by Cursor
parent 7e9023f0a8
commit 7ab9add5dd
132 changed files with 8276 additions and 491 deletions
+71 -4
View File
@@ -1,14 +1,82 @@
import { computed, ref } from 'vue'
import { computed, onMounted, reactive, ref, toRefs } from 'vue'
import { useRouter } from 'vue-router'
import { api } from '../api/client'
import { AnalyticsEvent, track } from '../lib/analytics'
import { homeFeeds, homeGridRow1, homeGridRow2, homeSearchHints } from '../lib/homeCatalog'
import {
homeFeeds,
homeGridRow1,
homeGridRow2,
homeSearchHints,
type HomeTool,
} from '../lib/homeCatalog'
import type { HomeToolIconName } from '../components/HomeToolIcon.vue'
import { useHomeMood } from './useHomeMood'
const ICONS = new Set([
'mbti', 'star', 'portrait', 'rhythm', 'synastry', 'astro',
'companion', 'ask', 'cards', 'reports', 'growth', 'relation',
])
function mapTools(
items: Array<{
row_index: number
sort_order: number
path: string
icon: string
label: string
badge?: string | null
badge_tone?: string | null
}>,
): { row1: HomeTool[]; row2: HomeTool[] } {
const toTool = (it: (typeof items)[0]): HomeTool | null => {
if (!ICONS.has(it.icon)) return null
const t: HomeTool = {
to: it.path,
icon: it.icon as HomeToolIconName,
label: it.label,
}
if (it.badge) t.badge = it.badge
if (it.badge_tone === 'hot' || it.badge_tone === 'new') t.badgeTone = it.badge_tone
return t
}
const sorted = [...items].sort(
(a, b) => a.row_index - b.row_index || a.sort_order - b.sort_order,
)
const row1: HomeTool[] = []
const row2: HomeTool[] = []
for (const it of sorted) {
const t = toTool(it)
if (!t) continue
if (it.row_index === 1) row1.push(t)
else if (it.row_index === 2) row2.push(t)
}
return { row1, row2 }
}
export function useHomePage() {
const router = useRouter()
const profileLabel = ref('自己')
const plusOpen = ref(false)
const mood = useHomeMood()
const grid = reactive({
gridRow1: [...homeGridRow1] as HomeTool[],
gridRow2: [...homeGridRow2] as HomeTool[],
})
onMounted(() => {
void api
.getHomeTools()
.then((res) => {
const mapped = mapTools(res.items || [])
if (mapped.row1.length || mapped.row2.length) {
grid.gridRow1 = mapped.row1
grid.gridRow2 = mapped.row2
}
})
.catch(() => {
/* keep static fallback */
})
})
const searchHint = computed(() => {
const i = new Date().getHours() % homeSearchHints.length
@@ -39,8 +107,7 @@ export function useHomePage() {
plusOpen,
mood,
searchHint,
gridRow1: homeGridRow1,
gridRow2: homeGridRow2,
...toRefs(grid),
feeds: homeFeeds,
goPlus,
trackGrid,