优化文案
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
Bar,
|
||||
XAxis,
|
||||
YAxis,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
CartesianGrid,
|
||||
} from "recharts";
|
||||
|
||||
type DailyCount = {
|
||||
date: string;
|
||||
count: number;
|
||||
};
|
||||
|
||||
type ActivityChartProps = {
|
||||
data: DailyCount[];
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export function ActivityChart({ data, color = "var(--copper)" }: ActivityChartProps) {
|
||||
if (data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-48 w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={data} margin={{ top: 4, right: 4, bottom: 0, left: -20 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="rgba(85,60,42,0.08)" />
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
tick={{ fontSize: 11, fill: "#726153" }}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<YAxis
|
||||
tick={{ fontSize: 11, fill: "#726153" }}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
allowDecimals={false}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
borderRadius: 16,
|
||||
border: "1px solid rgba(85,60,42,0.12)",
|
||||
background: "rgba(255,253,248,0.96)",
|
||||
fontSize: 13,
|
||||
}}
|
||||
labelStyle={{ color: "#241b14", fontWeight: 600 }}
|
||||
formatter={(value: number) => [`${value} 次`, "次数"]}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="count"
|
||||
fill={color}
|
||||
radius={[6, 6, 0, 0]}
|
||||
maxBarSize={32}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export function BindDeviceForm() {
|
||||
event.preventDefault();
|
||||
|
||||
if (!rawCode.trim()) {
|
||||
setNotice("先把老人二维码里的设备码贴进来。\n");
|
||||
setNotice("请先粘贴长辈手机上的设备码。\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function BindDeviceForm() {
|
||||
throw new Error(payload?.error || "连接失败,请稍后再试。");
|
||||
}
|
||||
|
||||
setNotice("已经连上了,这位老人的留言和陪伴记录会出现在下方。\n");
|
||||
setNotice("连接成功!现在可以查看长辈的留言和动态了。\n");
|
||||
setRawCode("");
|
||||
startTransition(() => {
|
||||
router.refresh();
|
||||
@@ -57,20 +57,20 @@ export function BindDeviceForm() {
|
||||
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div className="max-w-xl">
|
||||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||||
家人连接
|
||||
添加设备
|
||||
</p>
|
||||
<h3 className="mt-2 font-[family-name:var(--font-display)] text-2xl text-[var(--ink)]">
|
||||
扫二维码,或者直接贴入设备码
|
||||
扫码或粘贴设备码连接长辈
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-7 text-[var(--muted)]">
|
||||
如果老人已经把二维码发给您,直接扫码最快;如果您拿到的是一段链接或设备码,也可以贴到下面。
|
||||
让长辈打开手机上的「家人连接」页面,扫描二维码最快;也可以将设备码粘贴到下方。
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/scan"
|
||||
className="inline-flex h-12 items-center justify-center rounded-full bg-[var(--copper)] px-5 text-sm font-semibold text-white transition hover:bg-[var(--copper-deep)]"
|
||||
>
|
||||
打开扫码页
|
||||
扫码连接
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -78,7 +78,7 @@ export function BindDeviceForm() {
|
||||
<input
|
||||
value={rawCode}
|
||||
onChange={(event) => setRawCode(event.target.value)}
|
||||
placeholder="粘贴二维码链接,或者直接贴设备 UUID"
|
||||
placeholder="粘贴设备码或二维码链接"
|
||||
className="h-13 flex-1 rounded-full border border-[var(--line)] bg-[var(--paper-soft)] px-5 text-sm text-[var(--ink)] outline-none transition focus:border-[var(--copper)] focus:bg-white"
|
||||
/>
|
||||
<button
|
||||
@@ -115,7 +115,7 @@ export function SendMessageForm({ deviceUuid }: SendMessageFormProps) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!content.trim()) {
|
||||
setNotice("先写下想对老人说的话。\n");
|
||||
setNotice("请先写下您想说的话。\n");
|
||||
setCreatedMessagePublicId(null);
|
||||
return;
|
||||
}
|
||||
@@ -144,7 +144,7 @@ export function SendMessageForm({ deviceUuid }: SendMessageFormProps) {
|
||||
throw new Error(payload?.error || "留言发送失败,请稍后再试。");
|
||||
}
|
||||
|
||||
setNotice("留言已经写进家人的收件盒,老人下次打开或询问时就能看到。\n");
|
||||
setNotice("留言已发出,长辈下次使用时就能看到。\n");
|
||||
setCreatedMessagePublicId(
|
||||
typeof payload?.publicId === "number" ? payload.publicId : null,
|
||||
);
|
||||
@@ -168,10 +168,10 @@ export function SendMessageForm({ deviceUuid }: SendMessageFormProps) {
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||||
给老人留句话
|
||||
留言
|
||||
</p>
|
||||
<h4 className="mt-2 font-[family-name:var(--font-display)] text-xl text-[var(--ink)]">
|
||||
写一句今天就能被看到的问候
|
||||
给长辈写一句问候
|
||||
</h4>
|
||||
</div>
|
||||
<select
|
||||
@@ -196,7 +196,7 @@ export function SendMessageForm({ deviceUuid }: SendMessageFormProps) {
|
||||
|
||||
<div className="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm leading-7 text-[var(--muted)]">
|
||||
这条话会进入老人的留言列表,也会被 function calling 工具读取到。
|
||||
长辈和智能助理聊天时,就能看到这条留言。
|
||||
</p>
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
+13
-13
@@ -1,12 +1,13 @@
|
||||
import Link from "next/link";
|
||||
import { Home, MessageCircle, Smartphone, Activity, ScanLine } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const navigationItems = [
|
||||
{ href: "/", label: "总览" },
|
||||
{ href: "/messages", label: "留言" },
|
||||
{ href: "/devices", label: "设备" },
|
||||
{ href: "/activity", label: "活动" },
|
||||
{ href: "/scan", label: "扫码" },
|
||||
{ href: "/", label: "首页", icon: Home },
|
||||
{ href: "/messages", label: "留言", icon: MessageCircle },
|
||||
{ href: "/devices", label: "设备", icon: Smartphone },
|
||||
{ href: "/activity", label: "动态", icon: Activity },
|
||||
{ href: "/scan", label: "扫码", icon: ScanLine },
|
||||
];
|
||||
|
||||
function isActivePath(currentPath: string, href: string) {
|
||||
@@ -34,7 +35,7 @@ export function PanelShell({
|
||||
caregiverToken,
|
||||
children,
|
||||
actions,
|
||||
eyebrow = "Digital Human Bridge",
|
||||
eyebrow = "家人连线",
|
||||
}: PanelShellProps) {
|
||||
return (
|
||||
<main className="relative overflow-hidden px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
||||
@@ -42,24 +43,26 @@ export function PanelShell({
|
||||
<div className="mx-auto max-w-7xl space-y-6">
|
||||
<header className="paper-panel relative overflow-hidden rounded-[36px] border border-white/70 px-6 py-7 shadow-[0_35px_90px_rgba(105,68,41,0.13)] sm:px-8 lg:px-10 lg:py-8">
|
||||
<div className="absolute -right-12 top-[-50px] h-44 w-44 rounded-full bg-[rgba(53,84,141,0.14)] blur-2xl" />
|
||||
<div className="absolute left-10 top-10 h-24 w-24 rounded-full border border-[rgba(118,132,94,0.18)]" />
|
||||
<div className="absolute left-10 top-10 h-24 w-24 rounded-full border border-[rgba(118,132,94,0.18)] z-0 pointer-events-none" />
|
||||
|
||||
<div className="flex flex-col gap-6 xl:flex-row xl:items-start xl:justify-between">
|
||||
<div className="max-w-4xl">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{navigationItems.map((item) => {
|
||||
const active = isActivePath(currentPath, item.href);
|
||||
const Icon = item.icon;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`inline-flex h-11 items-center justify-center rounded-full px-4 text-sm font-semibold transition ${
|
||||
className={`inline-flex h-11 items-center justify-center gap-2 rounded-full px-4 text-sm font-semibold transition ${
|
||||
active
|
||||
? "bg-[var(--ink)] text-white shadow-[0_12px_30px_rgba(36,27,20,0.18)]"
|
||||
: "border border-[var(--line)] bg-white/82 text-[var(--ink)] hover:bg-[var(--paper-soft)]"
|
||||
}`}
|
||||
>
|
||||
<Icon size={16} />
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
@@ -78,10 +81,7 @@ export function PanelShell({
|
||||
|
||||
<div className="mt-6 flex flex-wrap gap-3 text-sm text-[var(--muted)]">
|
||||
<span className="rounded-full border border-[var(--line)] bg-white/76 px-4 py-2">
|
||||
当前家属账号:{caregiverToken.slice(0, 8).toUpperCase()}
|
||||
</span>
|
||||
<span className="rounded-full border border-[var(--line)] bg-white/76 px-4 py-2">
|
||||
不同功能已拆到独立路径,消息支持单条直达
|
||||
账号:{caregiverToken.slice(0, 8).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +119,7 @@ export function PwaControls() {
|
||||
|
||||
async function enableNotifications() {
|
||||
if (!vapidPublicKey) {
|
||||
setNotice("当前环境还没配置推送密钥,先把面板安装到桌面即可。\n");
|
||||
setNotice("当前还没有配置推送服务,先将应用安装到桌面即可。\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ export function PwaControls() {
|
||||
});
|
||||
|
||||
setNotificationState("enabled");
|
||||
setNotice("新留言会直接推到这台设备上,点开即可直达对应留言。\n");
|
||||
setNotice("已开启消息提醒,长辈的新留言会直接推送到您的设备。\n");
|
||||
} catch {
|
||||
setNotificationState("idle");
|
||||
setNotice("打开消息提醒失败,请稍后再试。\n");
|
||||
@@ -219,18 +219,18 @@ export function PwaControls() {
|
||||
return (
|
||||
<section className="rounded-[28px] border border-white/80 bg-white/88 p-5 shadow-[0_22px_55px_rgba(53,84,141,0.12)]">
|
||||
<p className="text-sm font-medium tracking-[0.18em] text-[var(--copper)] uppercase">
|
||||
安装与提醒
|
||||
快捷设置
|
||||
</p>
|
||||
<h2 className="mt-2 font-[family-name:var(--font-display)] text-2xl text-[var(--ink)]">
|
||||
把家属面板装到桌面,并接收新留言提醒
|
||||
安装到桌面,随时收到长辈的消息
|
||||
</h2>
|
||||
<p className="mt-3 text-sm leading-7 text-[var(--muted)]">
|
||||
安装后打开速度会更像原生应用;开启提醒后,老人一旦通过桥接工具留话,点击通知就能直达那条留言。
|
||||
安装后打开更快,开启提醒后,长辈每次留言都能立刻收到通知。
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex flex-wrap gap-3 text-sm text-[var(--muted)]">
|
||||
<span className="rounded-full border border-[var(--line)] bg-[var(--paper-soft)] px-4 py-2">
|
||||
{isStandalone ? "当前已可从桌面直接打开" : "浏览器内访问中"}
|
||||
{isStandalone ? "已安装到桌面" : "当前在浏览器中打开"}
|
||||
</span>
|
||||
<span className="rounded-full border border-[var(--line)] bg-[var(--paper-soft)] px-4 py-2">
|
||||
{notificationState === "enabled"
|
||||
|
||||
@@ -45,7 +45,7 @@ export function ScanClient() {
|
||||
|
||||
scannerRef.current.stop();
|
||||
setErrorText(null);
|
||||
setStatusText("识别到了设备码,正在建立连接……");
|
||||
setStatusText("识别到了设备,正在连接……");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/family/bind", {
|
||||
@@ -62,7 +62,7 @@ export function ScanClient() {
|
||||
const deviceUuid = payload?.deviceUuid;
|
||||
|
||||
if (!response.ok || !deviceUuid) {
|
||||
throw new Error(payload?.error || "绑定失败,请重新扫描一次。");
|
||||
throw new Error(payload?.error || "连接失败,请重新扫描。");
|
||||
}
|
||||
|
||||
startTransition(() => {
|
||||
@@ -72,9 +72,9 @@ export function ScanClient() {
|
||||
});
|
||||
} catch (error) {
|
||||
setErrorText(
|
||||
error instanceof Error ? error.message : "绑定失败,请重新扫描一次。",
|
||||
error instanceof Error ? error.message : "连接失败,请重新扫描。",
|
||||
);
|
||||
setStatusText("请再把二维码对准镜头中央。");
|
||||
setStatusText("请再次将二维码对准镜头。");
|
||||
await scannerRef.current.start().catch(() => null);
|
||||
}
|
||||
});
|
||||
@@ -91,7 +91,7 @@ export function ScanClient() {
|
||||
const hasCamera = await QrScanner.hasCamera();
|
||||
|
||||
if (!hasCamera) {
|
||||
setErrorText("当前设备没有可用摄像头,请改用系统扫码或手动粘贴设备码。");
|
||||
setErrorText("当前设备没有可用摄像头,请改用手动粘贴设备码。");
|
||||
setStatusText("无法打开镜头");
|
||||
return;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export function ScanClient() {
|
||||
await scanner.start();
|
||||
|
||||
if (!cancelled) {
|
||||
setStatusText("请把老人的二维码放到取景框中央。");
|
||||
setStatusText("请将长辈手机上的二维码放到取景框内。");
|
||||
}
|
||||
} catch (error) {
|
||||
setErrorText(
|
||||
@@ -151,7 +151,7 @@ export function ScanClient() {
|
||||
<div className="mt-4 space-y-2">
|
||||
<p className="text-base font-semibold text-[var(--ink)]">{statusText}</p>
|
||||
<p className="text-sm leading-7 text-[var(--muted)]">
|
||||
扫到后会直接把这位老人绑定到当前家属账号,不需要额外输密码。
|
||||
扫描成功后会自动连接长辈的设备,无需额外操作。
|
||||
</p>
|
||||
{errorText ? (
|
||||
<p className="rounded-[20px] bg-[var(--paper-soft)] px-4 py-3 text-sm leading-7 text-[var(--copper-deep)]">
|
||||
|
||||
Reference in New Issue
Block a user