diff --git a/package.json b/package.json index e989a58..ddb674e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "@tailwindcss/vite": "^4.2.2", "axios": "^1.13.6", "solid-js": "^1.9.11", - "tailwindcss": "^4.2.2" + "tailwindcss": "^4.2.2", + "zustand": "^5.0.12" }, "devDependencies": { "@types/node": "^24.12.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 159be8a..68f8fb8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: tailwindcss: specifier: ^4.2.2 version: 4.2.2 + zustand: + specifier: ^5.0.12 + version: 5.0.12 devDependencies: '@types/node': specifier: ^24.12.0 @@ -863,6 +866,24 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + zustand@5.0.12: + resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: '@babel/code-frame@7.29.0': @@ -1522,3 +1543,5 @@ snapshots: vite: 8.0.2(@types/node@24.12.0)(jiti@2.6.1) yallist@3.1.1: {} + + zustand@5.0.12: {} diff --git a/src/App.tsx b/src/App.tsx index c2113f0..f7daf80 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,69 +1,103 @@ import type { ParentComponent } from "solid-js"; import { A, useLocation } from "@solidjs/router"; -import { createSignal } from "solid-js"; const asideList = [ { label: "账号", url: "/account" }, { label: "设置", url: "/setting" }, ]; -const userInfo = { - name: "张三", - id: "123456", - dept: "大数据学院", - class: "3班", - gender: "男", -}; -const infoList = [ - ["名字", userInfo.name], - ["学号", userInfo.id], - ["学院", userInfo.dept], - ["班级", userInfo.class], - ["性别", userInfo.gender], -]; const App: ParentComponent = (props) => { const location = useLocation(); + const isActive = (url: string) => + location.pathname === url || + (location.pathname === "/" && url === "/account"); + return ( -
-
-
- 标题 -
-
- 个人信息 -
- {infoList.map(([label, value]) => ( -

- {label}: {value} -

- ))} -
-
-
-
- -
- {props.children} -
+

+ 管理账号、课程记录与任务日志 +

+
+
+ +
+

学习控制中枢

+

+ 多账号管理 / 课程记录 / 日志面板 +

+
+ + +
+ + +
+ {props.children} +
+
); diff --git a/src/components/account/AccountSidebar.tsx b/src/components/account/AccountSidebar.tsx new file mode 100644 index 0000000..f285fd4 --- /dev/null +++ b/src/components/account/AccountSidebar.tsx @@ -0,0 +1,170 @@ +import { For, Show } from "solid-js"; +import type { CourseKind } from "~/service/wk"; +import type { AccountItem } from "~/store/account"; + +type StatusOption = { + label: string; + value: CourseKind; +}; + +interface AccountSidebarProps { + accounts: AccountItem[]; + selectedAccountId: string; + expandedAccountId: string; + statusOptions: StatusOption[]; + hostLabels: Record; + isRefreshingAccount: boolean; + loggingOutId: string; + densityMode: "comfortable" | "compact"; + sidebarWidth: number; + onRefreshAccount: () => void; + onSelectAccount: (accountId: string) => void; + onToggleExpand: (accountId: string) => void; + onLogout: (accountId: string) => void; +} + +const AccountSidebar = (props: AccountSidebarProps) => { + const compact = () => props.densityMode === "compact"; + + return ( +
+
+
+

账号信息

+

选择账号后查看课程与记录

+
+ + +
+ +
+ + {(account) => { + const selected = account.id === props.selectedAccountId; + const expanded = account.id === props.expandedAccountId; + const platformLabel = + props.hostLabels[account.host] ?? account.host; + const statusLabel = + props.statusOptions.find((item) => item.value === account.status) + ?.label ?? account.status; + + return ( +
+
+ + +
+ + +
+

学院:{account.user.dept}

+

班级:{account.user.class}

+

性别:{account.user.gender}

+

站点:{account.host}

+

账号:{account.username || "-"}

+

课程数:{account.courses.length}

+ +
+ + +
+
+
+
+ ); + }} +
+ + +
+ 还没有账号,点击右上角“添加账号”开始登录。 +
+
+
+
+ ); +}; + +export default AccountSidebar; diff --git a/src/components/account/AddAccountDialog.tsx b/src/components/account/AddAccountDialog.tsx new file mode 100644 index 0000000..c66a19b --- /dev/null +++ b/src/components/account/AddAccountDialog.tsx @@ -0,0 +1,147 @@ +import { For, Show } from "solid-js"; +import Dialog from "~/components/dialog/Dialog"; +import type { CourseKind } from "~/service/wk"; + +type HostOption = { + label: string; + host: string; +}; + +type StatusOption = { + label: string; + value: CourseKind; +}; + +export type LoginForm = { + username: string; + password: string; + token: string; + status: CourseKind; + host: string; +}; + +interface AddAccountDialogProps { + open: () => boolean; + onClose: () => void; + onSubmit: () => void; + isSubmitting: boolean; + errorMessage: string; + form: LoginForm; + statusOptions: StatusOption[]; + hostOptions: HostOption[]; + updateForm: (key: K, value: LoginForm[K]) => void; +} + +const AddAccountDialog = (props: AddAccountDialogProps) => { + return ( + + + + + } + > +
+ + + + +