feat(frontend): clarify chart profile management

This commit is contained in:
Jesse_Chen
2026-08-30 14:24:04 +08:00
parent f4fb39e0f3
commit 796e4cf8cf
2 changed files with 73 additions and 38 deletions
+10 -1
View File
@@ -1644,11 +1644,20 @@ button:disabled { cursor: default; opacity: .45; }
.default-chart-card strong { display: block; margin: 4px 0; color: var(--color-ink); font-size: var(--type-body-md); font-weight: 500; }
.chart-library-panel { display: grid; gap: var(--space-5); margin-top: var(--space-5); }
.chart-library-group { display: grid; gap: var(--space-3); }
.chart-library-group > b { color: var(--color-ink); font-size: var(--type-caption); font-weight: 600; }
.chart-library-group-heading { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.chart-library-group-heading b, .chart-library-group-heading small { display: block; }
.chart-library-group-heading b { color: var(--color-ink); font-size: var(--type-caption); font-weight: 650; }
.chart-library-group-heading small { margin-top: 3px; color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 400; }
.chart-library-count { display: grid; min-width: 24px; height: 24px; place-items: center; border-radius: 999px; background: var(--color-canvas-muted); color: var(--color-ink-secondary); font-size: 11px; }
.chart-library-item { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); }
.chart-library-item-main { min-width: 0; }
.chart-library-item strong, .chart-library-item small { display: block; }
.chart-library-item strong { color: var(--color-ink); font-size: var(--type-body-md); font-weight: 500; }
.chart-library-item small, .chart-library-item > span, .empty-library-copy { color: var(--color-ink-secondary); font-size: var(--type-caption); }
.chart-library-item small + small { margin-top: 3px; }
.chart-library-item-title { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-bottom: 5px; }
.chart-role-badge, .chart-default-badge { display: inline-flex; align-items: center; min-height: 20px; padding: 0 7px; border-radius: 999px; background: var(--color-canvas-muted); color: var(--color-ink-secondary); font-size: 10px; font-weight: 650; }
.chart-role-badge.is-self, .chart-default-badge { background: var(--color-action-soft); color: var(--color-action-hover); }
.chart-library-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
.chart-library-actions > * { max-width: 100%; }
.chart-library-form { padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
+63 -37
View File
@@ -601,6 +601,24 @@ function profilePlaceLabel(profile: Profile) {
return selectedBirthPlace(profile)?.label || "地点未完整";
}
function profileBirthTimeLabel(profile: Profile) {
if (profile.time) return profile.time;
if (profile.birthTimePeriod) return `${profile.birthTimePeriod}(时分待确认)`;
return "出生时间待补全";
}
function profileBirthTimeStatusLabel(profile: Profile) {
if (profile.birthTimeStatus === "confirmed") return "时间已确认";
if (profile.birthTimeStatus === "candidate" || profile.birthTimeStatus === "accepted") return "时间为候选";
if (profile.birthTimeStatus === "rectifying" || profile.birthTimeStatus === "assessing") return "正在评估时间";
return "时间待确认";
}
function formatChartUpdatedAt(updatedAt: number) {
if (!Number.isFinite(updatedAt) || updatedAt <= 0) return "刚刚更新";
return `更新于 ${new Intl.DateTimeFormat("zh-CN", { month: "numeric", day: "numeric" }).format(new Date(updatedAt))}`;
}
function buildSynastryQuestion(selfProfile: Profile, partnerProfile: Profile, relationshipType: SynastryRelationshipType) {
const relationshipLabel = relationshipType === "business" ? "商业合作" : relationshipType === "family" ? "亲友/家庭" : relationshipType === "general" ? "其他关系" : "婚恋";
const evidenceRequest = relationshipType === "business"
@@ -3866,43 +3884,51 @@ export default function Home() {
renderChartLibrary() {
return (
<>
<p className="settings-dialog-copy"></p>
<div className="chart-library-panel" aria-label="星盘">
<div className="chart-library-group">
<b></b>
{chartLibrary.filter((record) => record.role === "self").map((record) => (
<article className="chart-library-item" key={record.id}>
<div>
<strong>{record.profile.name || "未命名"}</strong>
<small>{record.profile.date} {record.profile.time} · {profilePlaceLabel(record.profile)}</small>
</div>
<span></span>
</article>
))}
</div>
<div className="chart-library-group">
<b></b>
{chartLibrary.filter((record) => record.role === "other").length === 0 && <p className="empty-library-copy"></p>}
{chartLibrary.filter((record) => record.role === "other").map((record) => (
<article className="chart-library-item" key={record.id}>
<div>
<strong>{record.profile.name || "未命名"}</strong>
<small>{record.profile.date} {record.profile.time} · {profilePlaceLabel(record.profile)}</small>
</div>
<div className="chart-library-actions">
<select aria-label="关系类型" value={synastryRelationshipType} onChange={(event) => setSynastryRelationshipType(event.target.value as SynastryRelationshipType)} disabled={synastryPendingId !== null}>
<option value="romance"></option>
<option value="business"></option>
<option value="family">/</option>
<option value="general"></option>
</select>
<button className="button-secondary" type="button" onClick={() => void draftSynastryQuestionFromChart(record, synastryRelationshipType)} disabled={synastryPendingId !== null}>{synastryPendingId === record.id ? "正在计算合盘..." : "用于合盘"}</button>
<button className="button-secondary" type="button" onClick={() => void makeDefaultChart(record)} disabled={profileSaving}></button>
<button className="button-secondary" type="button" onClick={() => deleteOtherChart(record.id)}></button>
</div>
</article>
))}
</div>
<p className="settings-dialog-copy"></p>
<div className="chart-library-panel" aria-label="星盘资料管理">
<div className="chart-library-group">
<div className="chart-library-group-heading">
<div><b></b><small></small></div>
<span className="chart-library-count">{chartLibrary.filter((record) => record.role === "self").length}</span>
</div>
{chartLibrary.filter((record) => record.role === "self").map((record) => (
<article className="chart-library-item" key={record.id}>
<div className="chart-library-item-main">
<div className="chart-library-item-title"><strong>{record.profile.name || "未命名"}</strong><span className="chart-role-badge is-self"></span><span className="chart-default-badge"></span></div>
<small>{record.profile.date || "出生日期待补全"} · {profileBirthTimeLabel(record.profile)} · {profilePlaceLabel(record.profile)}</small>
<small>{profileBirthTimeStatusLabel(record.profile)} · {formatChartUpdatedAt(record.updatedAt)}</small>
</div>
</article>
))}
{chartLibrary.filter((record) => record.role === "self").length === 0 && <p className="empty-library-copy"></p>}
</div>
<div className="chart-library-group">
<div className="chart-library-group-heading">
<div><b></b><small></small></div>
<span className="chart-library-count">{chartLibrary.filter((record) => record.role === "other").length}</span>
</div>
{chartLibrary.filter((record) => record.role === "other").length === 0 && <p className="empty-library-copy"></p>}
{chartLibrary.filter((record) => record.role === "other").map((record) => (
<article className="chart-library-item" key={record.id}>
<div className="chart-library-item-main">
<div className="chart-library-item-title"><strong>{record.profile.name || "未命名"}</strong><span className="chart-role-badge"></span></div>
<small>{record.profile.date || "出生日期待补全"} · {profileBirthTimeLabel(record.profile)} · {profilePlaceLabel(record.profile)}</small>
<small>{profileBirthTimeStatusLabel(record.profile)} · {formatChartUpdatedAt(record.updatedAt)}</small>
</div>
<div className="chart-library-actions">
<select aria-label={`${record.profile.name || "其他人"}的关系类型`} value={synastryRelationshipType} onChange={(event) => setSynastryRelationshipType(event.target.value as SynastryRelationshipType)} disabled={synastryPendingId !== null}>
<option value="romance"></option>
<option value="business"></option>
<option value="family">/</option>
<option value="general"></option>
</select>
<button className="button-secondary" type="button" onClick={() => void draftSynastryQuestionFromChart(record, synastryRelationshipType)} disabled={synastryPendingId !== null}>{synastryPendingId === record.id ? "正在计算合盘..." : "用于合盘"}</button>
<button className="button-secondary" type="button" onClick={() => void makeDefaultChart(record)} disabled={profileSaving}></button>
<button className="button-secondary" type="button" onClick={() => deleteOtherChart(record.id)}></button>
</div>
</article>
))}
</div>
{synastryReportCard && (
<article className="synastry-report-card" aria-label="合盘结果摘要">
<div>