diff --git a/.gitignore b/.gitignore index 3123a28..1f93c9b 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,10 @@ client/release/ # local tooling data .codebuddy/ .playwright-cli/ + +# temp debug artifacts (screenshots / UI dumps) +/apps-calendar-lite.png +/apps-group-collapse.png +/schedule-lunar.png +/forms.yaml +/forms2.yaml diff --git a/client/electron/main.cjs b/client/electron/main.cjs index c61abd2..2a023e4 100644 --- a/client/electron/main.cjs +++ b/client/electron/main.cjs @@ -9,7 +9,7 @@ function createWindow() { height: 800, minWidth: 980, minHeight: 640, - title: 'ChatOA', + title: `ChatOA v${app.getVersion()}`, autoHideMenuBar: true, webPreferences: { preload: path.join(__dirname, 'preload.cjs'), diff --git a/client/package-lock.json b/client/package-lock.json index 40c3226..b83a789 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,6 +12,7 @@ "antd": "^5.22.0", "axios": "^1.7.9", "dayjs": "^1.11.13", + "lunar-typescript": "^1.8.6", "quill": "^1.3.7", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -4956,6 +4957,12 @@ "yallist": "^3.0.2" } }, + "node_modules/lunar-typescript": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/lunar-typescript/-/lunar-typescript-1.8.6.tgz", + "integrity": "sha512-5Eo4T/cnuXfrgO4k5LCpOGHIUOuz5hCF/IfNv0T29WY2shR36Hiz+ecN9WjnUuxUKhql9gbOkPaQoqLFKtPRNA==", + "license": "MIT" + }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", diff --git a/client/package.json b/client/package.json index 6850704..d089819 100644 --- a/client/package.json +++ b/client/package.json @@ -55,6 +55,7 @@ "antd": "^5.22.0", "axios": "^1.7.9", "dayjs": "^1.11.13", + "lunar-typescript": "^1.8.6", "quill": "^1.3.7", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/client/src/App.tsx b/client/src/App.tsx index 2f37be8..52aff88 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -58,7 +58,10 @@ export default function App() { } /> } /> } /> - } /> + {/* 注意:/ai-chat 必须与 /ai-chat/:assistantId 都渲染 KeepAliveOutlet(相同类型), + 若用 会把 Outlet 位置的 KeepAliveOutlet 卸载,导致切走再切回时 + 所有 keep-alive 页面状态(如应用页打开的运行页签)被重置 */} + } /> } /> } /> diff --git a/client/src/api/auth.ts b/client/src/api/auth.ts index 94071ec..ebd91a0 100644 --- a/client/src/api/auth.ts +++ b/client/src/api/auth.ts @@ -22,3 +22,8 @@ export async function fetchMe() { const { data } = await client.get('/auth/me'); return data; } + +export async function updateMe(patch: { signature?: string; status?: string }) { + const { data } = await client.patch('/auth/me', patch); + return data; +} diff --git a/client/src/components/ProfileModal.tsx b/client/src/components/ProfileModal.tsx new file mode 100644 index 0000000..040d41d --- /dev/null +++ b/client/src/components/ProfileModal.tsx @@ -0,0 +1,154 @@ +import { useEffect, useState } from 'react'; +import { + Avatar, + Button, + Descriptions, + Input, + Modal, + Select, + Tag, + message, +} from 'antd'; +import { useAuth } from '../store/auth'; +import { USER_STATUS, userStatusMeta } from '../constants/user'; + +interface Props { + open: boolean; + onClose: () => void; +} + +/** 个人信息弹窗:展示资料 + 编辑签名 + 设置状态 */ +export default function ProfileModal({ open, onClose }: Props) { + const user = useAuth((s) => s.user); + const updateProfile = useAuth((s) => s.updateProfile); + const [signature, setSignature] = useState(''); + const [status, setStatus] = useState(undefined); + const [saving, setSaving] = useState(false); + + // 每次打开时同步当前用户数据 + useEffect(() => { + if (open) { + setSignature(user?.signature ?? ''); + setStatus(user?.status); + } + }, [open, user]); + + const save = async () => { + setSaving(true); + try { + await updateProfile({ + signature: signature.trim(), + status: status && status !== user?.status ? status : undefined, + }); + message.success('已保存'); + onClose(); + } catch { + message.error('保存失败,请重试'); + } finally { + setSaving(false); + } + }; + + const statusMeta = userStatusMeta(user?.status); + + return ( + + 取消 + , + , + ]} + > +
+ + {user?.nickname?.[0] ?? 'U'} + +
+
+ {user?.nickname} + {statusMeta && ( + + {statusMeta.label} + + )} +
+
+ @{user?.username} +
+
+
+ + + +
+
+ 个性签名 +
+ setSignature(e.target.value)} + /> +
+ +
+
+ 我的状态 +
+