fix: 优化个人中心卡片切换交互
This commit is contained in:
@@ -1,11 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { CalendarDays, CheckCircle2, CircleMinus, LogOut, PackageCheck, Sparkles } from "@lucide/vue";
|
import {
|
||||||
|
CalendarDays,
|
||||||
|
CheckCircle2,
|
||||||
|
ChevronDown,
|
||||||
|
CircleMinus,
|
||||||
|
LifeBuoy,
|
||||||
|
LogOut,
|
||||||
|
PackageCheck,
|
||||||
|
Share2,
|
||||||
|
Sparkles,
|
||||||
|
} from "@lucide/vue";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
import type { PeriodicReport, PracticeReviewResult, ShareDraft, TeacherHelpCard, UserProfile } from "../types/api";
|
import type { PeriodicReport, PracticeReviewResult, ShareDraft, TeacherHelpCard, UserProfile } from "../types/api";
|
||||||
import AppDialog from "./AppDialog.vue";
|
import AppDialog from "./AppDialog.vue";
|
||||||
|
|
||||||
type CenterSection = "overview" | "review" | "records";
|
type CenterSection = "overview" | "review" | "records";
|
||||||
|
type CardSection = "help" | "share";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
user: UserProfile;
|
user: UserProfile;
|
||||||
@@ -23,6 +34,7 @@ defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const activeSection = ref<CenterSection>(props.initialSection);
|
const activeSection = ref<CenterSection>(props.initialSection);
|
||||||
|
const activeCardSection = ref<CardSection>("help");
|
||||||
const entitlement = computed(() => props.user.entitlement);
|
const entitlement = computed(() => props.user.entitlement);
|
||||||
const displayName = computed(() => props.user.nickname?.trim() || props.user.name);
|
const displayName = computed(() => props.user.nickname?.trim() || props.user.name);
|
||||||
const nameInitial = computed(() => displayName.value.slice(0, 1));
|
const nameInitial = computed(() => displayName.value.slice(0, 1));
|
||||||
@@ -189,21 +201,85 @@ function settlementStatusLabel(status: string) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="center-section records-section">
|
<div v-else class="center-section records-section">
|
||||||
<section class="record-group">
|
<nav class="card-type-tabs" role="tablist" aria-label="我的卡片类型">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="tab"
|
||||||
|
:aria-selected="activeCardSection === 'help'"
|
||||||
|
@click="activeCardSection = 'help'"
|
||||||
|
>
|
||||||
|
<LifeBuoy :size="17" aria-hidden="true" />
|
||||||
|
<span>老师求助卡</span>
|
||||||
|
<small>{{ helpCards.length }}</small>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="tab"
|
||||||
|
:aria-selected="activeCardSection === 'share'"
|
||||||
|
@click="activeCardSection = 'share'"
|
||||||
|
>
|
||||||
|
<Share2 :size="17" aria-hidden="true" />
|
||||||
|
<span>班级分享稿</span>
|
||||||
|
<small>{{ shareDrafts.length }}</small>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section
|
||||||
|
v-if="activeCardSection === 'help'"
|
||||||
|
class="card-history-panel"
|
||||||
|
role="tabpanel"
|
||||||
|
aria-label="老师求助卡记录"
|
||||||
|
>
|
||||||
|
<header class="card-history-head">
|
||||||
|
<div>
|
||||||
<h3>老师求助卡</h3>
|
<h3>老师求助卡</h3>
|
||||||
<article v-for="item in helpCards" :key="`help-${item.id}`" class="compact-record">
|
<p>需要老师确认方向时,可回看已经整理过的求助信息。</p>
|
||||||
<time>{{ formatDate(item.createdAt) }} · {{ item.copied ? '已复制' : '未复制' }}</time>
|
</div>
|
||||||
<p>{{ item.content.slice(0, 160) }}{{ item.content.length > 160 ? '…' : '' }}</p>
|
<span>共 {{ helpCards.length }} 张</span>
|
||||||
</article>
|
</header>
|
||||||
<p v-if="!loading && !helpCards.length" class="center-empty small">还没有生成过求助卡。</p>
|
<div v-if="helpCards.length" class="card-history-list">
|
||||||
|
<details v-for="item in helpCards" :key="`help-${item.id}`" class="card-history-item">
|
||||||
|
<summary>
|
||||||
|
<div>
|
||||||
|
<time>{{ formatDate(item.createdAt) }}</time>
|
||||||
|
<strong>{{ item.copied ? "已复制给老师" : "尚未复制" }}</strong>
|
||||||
|
<p>{{ item.content.slice(0, 72) }}{{ item.content.length > 72 ? "…" : "" }}</p>
|
||||||
|
</div>
|
||||||
|
<ChevronDown :size="17" aria-hidden="true" />
|
||||||
|
</summary>
|
||||||
|
<pre>{{ item.content }}</pre>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<p v-else-if="!loading" class="center-empty small">还没有生成过求助卡。</p>
|
||||||
</section>
|
</section>
|
||||||
<section class="record-group">
|
|
||||||
|
<section
|
||||||
|
v-else
|
||||||
|
class="card-history-panel"
|
||||||
|
role="tabpanel"
|
||||||
|
aria-label="班级分享稿记录"
|
||||||
|
>
|
||||||
|
<header class="card-history-head">
|
||||||
|
<div>
|
||||||
<h3>班级分享稿</h3>
|
<h3>班级分享稿</h3>
|
||||||
<article v-for="item in shareDrafts" :key="`share-${item.id}`" class="compact-record">
|
<p>准备在班级群分享时,可回看已经整理过的分享草稿。</p>
|
||||||
<time>{{ formatDate(item.createdAt) }} · {{ item.copied ? '已复制' : '未复制' }}</time>
|
</div>
|
||||||
<p>{{ item.content.slice(0, 160) }}{{ item.content.length > 160 ? '…' : '' }}</p>
|
<span>共 {{ shareDrafts.length }} 张</span>
|
||||||
</article>
|
</header>
|
||||||
<p v-if="!loading && !shareDrafts.length" class="center-empty small">还没有生成过分享稿。</p>
|
<div v-if="shareDrafts.length" class="card-history-list">
|
||||||
|
<details v-for="item in shareDrafts" :key="`share-${item.id}`" class="card-history-item">
|
||||||
|
<summary>
|
||||||
|
<div>
|
||||||
|
<time>{{ formatDate(item.createdAt) }}</time>
|
||||||
|
<strong>{{ item.copied ? "已复制" : "尚未复制" }}</strong>
|
||||||
|
<p>{{ item.content.slice(0, 72) }}{{ item.content.length > 72 ? "…" : "" }}</p>
|
||||||
|
</div>
|
||||||
|
<ChevronDown :size="17" aria-hidden="true" />
|
||||||
|
</summary>
|
||||||
|
<pre>{{ item.content }}</pre>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<p v-else-if="!loading" class="center-empty small">还没有生成过分享稿。</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -272,6 +348,30 @@ function settlementStatusLabel(status: string) {
|
|||||||
.report-record summary small { color: var(--chat-weak); font-size: 10px; }
|
.report-record summary small { color: var(--chat-weak); font-size: 10px; }
|
||||||
.report-record > time { display: block; padding: 0 12px 6px; }
|
.report-record > time { display: block; padding: 0 12px 6px; }
|
||||||
.report-record pre { max-height: 260px; overflow: auto; margin: 0; padding: 11px 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
.report-record pre { max-height: 260px; overflow: auto; margin: 0; padding: 11px 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
||||||
|
.records-section { align-content: start; }
|
||||||
|
.card-type-tabs { position: sticky; top: -1px; z-index: 2; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 2px 0 4px; background: #fff; }
|
||||||
|
.card-type-tabs button { min-width: 0; min-height: 52px; display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; align-items: center; gap: 7px; padding: 0 11px; border: 1px solid var(--chat-border); border-radius: 13px; background: #fff; color: var(--chat-muted); font-size: 12px; font-weight: 700; text-align: left; }
|
||||||
|
.card-type-tabs button[aria-selected="true"] { border-color: rgba(20, 148, 119, 0.42); background: var(--chat-brand-soft); color: var(--chat-brand-dark); box-shadow: 0 5px 16px rgba(27, 68, 55, 0.07); }
|
||||||
|
.card-type-tabs button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.card-type-tabs button small { min-width: 23px; padding: 3px 6px; border-radius: 999px; background: #eef3f1; color: var(--chat-muted); font-size: 10px; text-align: center; }
|
||||||
|
.card-type-tabs button[aria-selected="true"] small { background: #dbeee7; color: var(--chat-brand-dark); }
|
||||||
|
.card-history-panel { min-width: 0; display: grid; gap: 10px; }
|
||||||
|
.card-history-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
||||||
|
.card-history-head > div { min-width: 0; display: grid; gap: 3px; }
|
||||||
|
.card-history-head h3 { margin: 0; color: var(--chat-text); font-size: 14px; }
|
||||||
|
.card-history-head p { margin: 0; color: var(--chat-muted); font-size: 11px; line-height: 1.5; }
|
||||||
|
.card-history-head > span { flex: 0 0 auto; padding: 4px 7px; border-radius: 999px; background: #f1f5f3; color: var(--chat-muted); font-size: 10px; }
|
||||||
|
.card-history-list { display: grid; gap: 8px; }
|
||||||
|
.card-history-item { overflow: hidden; border: 1px solid var(--chat-border); border-radius: 13px; background: #fff; }
|
||||||
|
.card-history-item summary { min-height: 76px; display: grid; grid-template-columns: minmax(0, 1fr) 20px; align-items: center; gap: 8px; padding: 10px 11px; color: var(--chat-brand-dark); cursor: pointer; list-style: none; }
|
||||||
|
.card-history-item summary::-webkit-details-marker { display: none; }
|
||||||
|
.card-history-item summary > div { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 3px 8px; }
|
||||||
|
.card-history-item time { color: var(--chat-weak); font-size: 10px; }
|
||||||
|
.card-history-item summary strong { color: var(--chat-brand-dark); font-size: 10px; font-weight: 700; }
|
||||||
|
.card-history-item summary p { grid-column: 1 / -1; display: -webkit-box; overflow: hidden; margin: 1px 0 0; color: var(--chat-muted); font-size: 12px; line-height: 1.55; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
|
||||||
|
.card-history-item summary > svg { transition: transform 0.18s ease; }
|
||||||
|
.card-history-item[open] summary > svg { transform: rotate(180deg); }
|
||||||
|
.card-history-item pre { max-height: 300px; overflow: auto; margin: 0; padding: 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
||||||
.center-empty { margin: 0; padding: 22px 14px; border-radius: 14px; background: #f6f8f7; color: var(--chat-muted); font-size: 13px; line-height: 1.7; text-align: center; }
|
.center-empty { margin: 0; padding: 22px 14px; border-radius: 14px; background: #f6f8f7; color: var(--chat-muted); font-size: 13px; line-height: 1.7; text-align: center; }
|
||||||
.center-empty.small { padding: 14px; font-size: 12px; }
|
.center-empty.small { padding: 14px; font-size: 12px; }
|
||||||
.personal-center-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
.personal-center-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user