feat(ECR-012–016): 合规、题库、时辰刷新、头像、MBTI OEJTS 与埋点

落地输入合规、探索题库、报告日/时辰刷新、账号头像、OEJTS 量表,并补齐 H5 埋点与 Admin 漏斗;同步 ESS 工件、切至自建 Git、清理 GitHub Actions。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-13 01:27:58 +08:00
co-authored by Cursor
parent 13860bf1ef
commit 89756f65b4
227 changed files with 7405 additions and 1407 deletions
+18 -6
View File
@@ -1,7 +1,6 @@
<template>
<PageShell>
<template #hero>
<BackButton />
<div class="head">
<HomeToolIcon name="growth" :size="48" />
<div>
@@ -16,7 +15,7 @@
<label class="lbl">新计划标题</label>
<input class="inp" v-model="title" maxlength="40" placeholder="例如:每晚 11 点前放下手机" />
<label class="lbl">焦点可选</label>
<input class="inp" v-model="focus" maxlength="80" placeholder="例如:保护睡眠与情绪" />
<input class="inp" v-model="focus" maxlength="40" placeholder="例如:保护睡眠与情绪" />
<button class="cta" type="button" :disabled="saving || !title.trim()" @click="create">创建计划</button>
<p v-if="err" class="err">{{ err }}</p>
</div>
@@ -42,11 +41,12 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { validateUserText } from '@yuxingu/utils'
import { api } from '../api/client'
import BackButton from '../components/BackButton.vue'
import HomeToolIcon from '../components/HomeToolIcon.vue'
import PageShell from '../components/PageShell.vue'
import { ensureAccount } from '../lib/authSession'
import { AnalyticsEvent, track } from '../lib/analytics'
const router = useRouter()
const route = useRoute()
@@ -59,7 +59,7 @@ const saving = ref(false)
const checking = ref('')
const err = ref('')
async function load() {
async function load(opts?: { trackView?: boolean }) {
loading.value = true
if (!(await ensureAccount(router, route.fullPath))) {
loading.value = false
@@ -76,6 +76,7 @@ async function load() {
err.value = e instanceof Error ? e.message : '加载失败'
} finally {
loading.value = false
if (opts?.trackView) track(AnalyticsEvent.GrowthPlanViewed)
}
}
@@ -83,9 +84,17 @@ async function create() {
saving.value = true
err.value = ''
try {
await api.createGrowthPlan({ title: title.value.trim(), focus: focus.value.trim() || undefined })
const titleCheck = validateUserText('title', title.value)
if (!titleCheck.ok) throw new Error(titleCheck.message)
const focusCheck = validateUserText('focus', focus.value)
if (!focusCheck.ok) throw new Error(focusCheck.message)
await api.createGrowthPlan({
title: titleCheck.value,
focus: focusCheck.value || undefined,
})
title.value = ''
focus.value = ''
track(AnalyticsEvent.GrowthPlanCreated)
await load()
} catch (e) {
err.value = e instanceof Error ? e.message : '创建失败'
@@ -101,6 +110,7 @@ async function checkin(id: string) {
await api.createGrowthCheckin(id, {})
const ck = await api.listGrowthCheckins(id)
checkins.value[id] = ck.items || []
track(AnalyticsEvent.GrowthPlanCheckin)
} catch (e) {
err.value = e instanceof Error ? e.message : '打卡失败'
} finally {
@@ -108,7 +118,9 @@ async function checkin(id: string) {
}
}
onMounted(load)
onMounted(() => {
void load({ trackView: true })
})
</script>
<style scoped>