feat: 将成长档案调整为近期实修回顾
This commit is contained in:
@@ -9,7 +9,7 @@ import MessageList, { type DisplayMessage } from "./components/MessageList.vue";
|
||||
import SessionDrawer from "./components/SessionDrawer.vue";
|
||||
import SessionQuota from "./components/SessionQuota.vue";
|
||||
import { ApiError, api, clearToken, getToken, streamChat } from "./services/api";
|
||||
import type { ChatMessage as ApiMessage, ChatSession, GrowthProfileResult, PeriodicReport, ShareDraft, TeacherHelpCard, UserProfile } from "./types/api";
|
||||
import type { ChatMessage as ApiMessage, ChatSession, PeriodicReport, PracticeReviewResult, ShareDraft, TeacherHelpCard, UserProfile } from "./types/api";
|
||||
|
||||
const user = ref<UserProfile | null>(null);
|
||||
const sessions = ref<ChatSession[]>([]);
|
||||
@@ -25,7 +25,7 @@ const logoutDialogOpen = ref(false);
|
||||
const profileDialogOpen = ref(false);
|
||||
const helpCardDialogOpen = ref(false);
|
||||
const shareDraftDialogOpen = ref(false);
|
||||
const growthProfile = ref<GrowthProfileResult | null>(null);
|
||||
const practiceReview = ref<PracticeReviewResult | null>(null);
|
||||
const helpCard = ref<TeacherHelpCard | null>(null);
|
||||
const helpCardContent = ref("");
|
||||
const shareDraft = ref<ShareDraft | null>(null);
|
||||
@@ -218,7 +218,7 @@ async function finishCurrentTopic() {
|
||||
try {
|
||||
const result = await api.finishTopic(activeSessionId.value);
|
||||
if (result.settlementStatus === "success" || result.settlementStatus === "fallback") {
|
||||
showToast(result.growthProfileEnabled ? "本主题已沉淀,并更新了你的成长档案" : "本主题已沉淀");
|
||||
showToast(result.growthProfileEnabled ? "本主题已沉淀,并更新了近期实修回顾" : "本主题已沉淀");
|
||||
} else {
|
||||
showToast("主题已结束,实修记录正在后台沉淀,可以继续使用其他功能");
|
||||
void watchTopicSettlement(result.summary.id);
|
||||
@@ -240,7 +240,7 @@ async function watchTopicSettlement(summaryId: number) {
|
||||
try {
|
||||
const result = await api.topicSettlement(summaryId);
|
||||
if (result.settlementStatus === "success" || result.settlementStatus === "fallback") {
|
||||
showToast(result.growthProfileEnabled ? "实修记录已沉淀,并更新了成长档案" : "实修记录已沉淀完成");
|
||||
showToast(result.growthProfileEnabled ? "实修记录已沉淀,并更新了近期实修回顾" : "实修记录已沉淀完成");
|
||||
await refreshProfile();
|
||||
return;
|
||||
}
|
||||
@@ -330,22 +330,22 @@ async function copyShareDraft() {
|
||||
}
|
||||
}
|
||||
|
||||
async function openGrowthProfile() {
|
||||
async function openPracticeReview() {
|
||||
profileDialogOpen.value = true;
|
||||
profileLoading.value = true;
|
||||
try {
|
||||
const [profileResult, helpCards, shareDrafts, reports] = await Promise.all([
|
||||
api.growthProfile(),
|
||||
api.practiceReview(),
|
||||
api.helpCards(10),
|
||||
api.shareDrafts(10),
|
||||
api.periodicReports(10),
|
||||
]);
|
||||
growthProfile.value = profileResult;
|
||||
practiceReview.value = profileResult;
|
||||
helpCardHistory.value = helpCards;
|
||||
shareDraftHistory.value = shareDrafts;
|
||||
reportHistory.value = reports;
|
||||
} catch (error) {
|
||||
handleError(error, "成长档案加载失败");
|
||||
handleError(error, "近期实修回顾加载失败");
|
||||
} finally {
|
||||
profileLoading.value = false;
|
||||
}
|
||||
@@ -484,7 +484,7 @@ async function copyText(text: string) {
|
||||
:generating-help-card="generatingHelpCard"
|
||||
:generating-share-draft="generatingShareDraft"
|
||||
@finish-topic="finishCurrentTopic"
|
||||
@open-profile="openGrowthProfile"
|
||||
@open-profile="openPracticeReview"
|
||||
@generate-help-card="generateHelpCard"
|
||||
@generate-share-draft="generateShareDraft"
|
||||
/>
|
||||
@@ -512,23 +512,19 @@ async function copyText(text: string) {
|
||||
|
||||
<div v-if="toastText" class="chat-toast" role="status">{{ toastText }}</div>
|
||||
|
||||
<AppDialog v-if="profileDialogOpen" title="我的实修档案" labelled-by="growth-profile-title" @close="profileDialogOpen = false">
|
||||
<AppDialog v-if="profileDialogOpen" title="近期实修回顾" labelled-by="growth-profile-title" @close="profileDialogOpen = false">
|
||||
<section class="growth-profile-dialog" :aria-busy="profileLoading">
|
||||
<p v-if="profileLoading" class="profile-empty">正在加载成长档案...</p>
|
||||
<template v-else-if="growthProfile?.profile">
|
||||
<pre>{{ growthProfile.profile.profileText }}</pre>
|
||||
<div class="growth-profile-fields">
|
||||
<p v-if="growthProfile.profile.recurringTopics"><strong>反复议题</strong>{{ growthProfile.profile.recurringTopics }}</p>
|
||||
<p v-if="growthProfile.profile.commonEmotions"><strong>常见情绪</strong>{{ growthProfile.profile.commonEmotions }}</p>
|
||||
<p v-if="growthProfile.profile.bodyPatterns"><strong>身体感受</strong>{{ growthProfile.profile.bodyPatterns }}</p>
|
||||
<p v-if="growthProfile.profile.homeworkDone"><strong>做过的功课</strong>{{ growthProfile.profile.homeworkDone }}</p>
|
||||
<p v-if="growthProfile.profile.recentProgress"><strong>最近进展</strong>{{ growthProfile.profile.recentProgress }}</p>
|
||||
<p v-if="profileLoading" class="profile-empty">正在加载近期回顾...</p>
|
||||
<template v-else-if="practiceReview?.profile">
|
||||
<pre>{{ practiceReview.profile.reviewText }}</pre>
|
||||
<div v-if="practiceReview.profile.currentFocus" class="growth-profile-fields">
|
||||
<p><strong>近期关注</strong>{{ practiceReview.profile.currentFocus }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<p v-else class="profile-empty">还没有成长档案。你可以在完成一次主题对话后点击“沉淀本主题”。</p>
|
||||
<div v-if="growthProfile?.recentSummaries.length" class="recent-topic-summaries">
|
||||
<p v-else class="profile-empty">还没有近期回顾。你可以在完成一次主题对话后点击“沉淀本主题”。</p>
|
||||
<div v-if="practiceReview?.recentSummaries.length" class="recent-topic-summaries">
|
||||
<h3>最近主题沉淀</h3>
|
||||
<article v-for="item in growthProfile.recentSummaries" :key="item.id">
|
||||
<article v-for="item in practiceReview.recentSummaries" :key="item.id">
|
||||
<time>{{ item.generatedAt }} · {{ settlementStatusLabel(item.status) }}</time>
|
||||
<p v-if="item.status === 'success' || item.status === 'fallback'">{{ item.summary }}</p>
|
||||
<p v-else-if="item.status === 'failed'">沉淀暂时失败,对话内容仍已保留。</p>
|
||||
@@ -536,7 +532,7 @@ async function copyText(text: string) {
|
||||
</article>
|
||||
</div>
|
||||
<div v-if="reportHistory.length" class="recent-topic-summaries periodic-report-list">
|
||||
<h3>周期实修报告</h3>
|
||||
<h3>周期实修回顾</h3>
|
||||
<article v-for="item in reportHistory" :key="`report-${item.id}`">
|
||||
<time>{{ item.reportTypeLabel }} · {{ item.periodStart }} 至 {{ item.periodEnd }} · {{ item.generatedAt }}</time>
|
||||
<p class="report-title">{{ item.title }}</p>
|
||||
|
||||
@@ -52,7 +52,7 @@ const guidanceText = computed(() => {
|
||||
class="quota-link"
|
||||
@click="$emit('openProfile')"
|
||||
>
|
||||
我的档案
|
||||
近期回顾
|
||||
</button>
|
||||
<button type="button" class="quota-link" :disabled="finishing" @click="$emit('finishTopic')">
|
||||
{{ finishing ? '沉淀中' : '沉淀本主题' }}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
ChatMessage,
|
||||
ChatSession,
|
||||
FinishTopicResult,
|
||||
GrowthProfileResult,
|
||||
PracticeReviewResult,
|
||||
LoginResult,
|
||||
PeriodicReport,
|
||||
ShareDraft,
|
||||
@@ -96,7 +96,7 @@ export const api = {
|
||||
generateShareDraft: (sessionId: number) => request<ShareDraft>(`/chat/session/${sessionId}/share-draft`, { method: "POST", body: JSON.stringify({}) }),
|
||||
markShareDraftCopied: (draftId: number) => request<ShareDraft>(`/chat/share-draft/${draftId}/copied`, { method: "POST", body: JSON.stringify({}) }),
|
||||
shareDrafts: (limit = 20) => request<ShareDraft[]>(`/chat/share-draft/list?limit=${limit}`),
|
||||
growthProfile: () => request<GrowthProfileResult>("/user/growth-profile"),
|
||||
practiceReview: () => request<PracticeReviewResult>("/user/growth-profile"),
|
||||
periodicReports: (limit = 10) => request<PeriodicReport[]>(`/user/periodic-report/list?limit=${limit}`),
|
||||
stop: (sessionId: number) => request<null>("/chat/stop", { method: "POST", body: JSON.stringify({ sessionId }) }),
|
||||
};
|
||||
|
||||
@@ -35,8 +35,9 @@ export interface UserEntitlementSummary {
|
||||
export interface TopicSummary {
|
||||
id: number;
|
||||
topicSessionId: number;
|
||||
schemaVersion: number;
|
||||
summary: string;
|
||||
recommendedHomework?: string | null;
|
||||
currentFocus?: string | null;
|
||||
nextObservation?: string | null;
|
||||
status: "pending" | "running" | "success" | "fallback" | "failed" | string;
|
||||
errorMessage?: string | null;
|
||||
@@ -48,27 +49,24 @@ export interface TopicSummary {
|
||||
generatedAt: string;
|
||||
}
|
||||
|
||||
export interface GrowthProfile {
|
||||
export interface PracticeReview {
|
||||
id: number;
|
||||
userId: number;
|
||||
profileText: string;
|
||||
recurringTopics?: string | null;
|
||||
commonEmotions?: string | null;
|
||||
bodyPatterns?: string | null;
|
||||
relationPatterns?: string | null;
|
||||
homeworkDone?: string | null;
|
||||
effectiveHomework?: string | null;
|
||||
recentProgress?: string | null;
|
||||
schemaVersion: number;
|
||||
reviewText: string;
|
||||
currentFocus?: string | null;
|
||||
sourceSummaryIds: number[];
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface GrowthProfileResult {
|
||||
profile: GrowthProfile | null;
|
||||
export interface PracticeReviewResult {
|
||||
profile: PracticeReview | null;
|
||||
recentSummaries: TopicSummary[];
|
||||
}
|
||||
|
||||
export interface PeriodicReport {
|
||||
id: number;
|
||||
schemaVersion: number;
|
||||
reportType: "weekly" | "monthly" | "stage" | string;
|
||||
reportTypeLabel: string;
|
||||
periodStart: string;
|
||||
@@ -82,7 +80,7 @@ export interface PeriodicReport {
|
||||
export interface FinishTopicResult {
|
||||
topic: Record<string, unknown>;
|
||||
summary: TopicSummary & Record<string, unknown>;
|
||||
profile: GrowthProfile | null;
|
||||
profile: PracticeReview | null;
|
||||
growthProfileEnabled: boolean;
|
||||
settlementStatus: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user