From b38b069119242775a833e2111f51200523460e88 Mon Sep 17 00:00:00 2001 From: longdayi <13477510+longdayilongdayi@user.noreply.gitee.com> Date: Thu, 23 Jan 2025 23:59:49 +0800 Subject: [PATCH 1/2] 01232359 --- apps/server/src/tasks/init/gendev.service.ts | 4 +- apps/web/package.json | 1 + apps/web/src/App.css | 2 +- apps/web/src/App.tsx | 3 +- apps/web/src/app/auth/login.tsx | 117 +++--- apps/web/src/app/auth/page.tsx | 228 +++++------ apps/web/src/app/auth/register.tsx | 310 ++++++-------- apps/web/src/app/auth/types.ts | 10 - apps/web/src/app/auth/useAuthForm.ts | 46 +++ apps/web/src/app/denied.tsx | 2 +- apps/web/src/app/error.tsx | 2 +- apps/web/src/app/login.tsx | 387 ------------------ apps/web/src/app/main/letter/list/Header.tsx | 2 +- .../src/app/main/letter/list/Pagination.tsx | 12 +- .../src/app/main/letter/list/SearchFilter.tsx | 2 +- .../web/src/app/main/letter/progress/page.tsx | 8 +- apps/web/src/app/main/letter/write/filter.tsx | 4 +- .../common/editor/quill/QuillCharCounter.tsx | 2 +- .../user => common/element}/Avatar.tsx | 0 .../src/components/common/element/Button.tsx | 14 +- .../src/components/common/element/Input.tsx | 82 ++++ .../components/common/element/LeaderCard.tsx | 4 +- .../components/common/element/TreeSelect.tsx | 215 ++++++++++ .../components/common/form/FormArrayField.tsx | 16 +- .../common/form/FormDynamicInputs.tsx | 28 +- .../src/components/common/form/FormField.tsx | 97 +++++ .../src/components/common/form/FormInput.tsx | 161 -------- .../src/components/common/form/FormSelect.tsx | 8 +- .../components/common/form/FormSignature.tsx | 28 +- .../src/components/common/form/FormTags.tsx | 15 +- .../common/uploader/FileUploader.tsx | 17 +- apps/web/src/components/layout/fix-header.tsx | 2 +- .../web/src/components/layout/main/Footer.tsx | 8 +- .../web/src/components/layout/main/Header.tsx | 50 ++- .../layout/main/notifications-panel.tsx | 6 +- .../src/components/layout/main/search-bar.tsx | 4 +- .../layout/main/search-dropdown.tsx | 4 +- .../src/components/layout/main/side-bar.tsx | 44 -- .../components/layout/main/top-nav-bar.tsx | 47 --- .../src/components/layout/main/useNavItem.ts | 37 +- .../layout/main/usermenu-dropdown.tsx | 68 --- .../src/components/layout/main/usermenu.tsx | 111 +++++ .../web/src/components/layout/user-header.tsx | 2 +- .../models/course/card/CourseHeader.tsx | 2 +- .../models/course/card/CourseStats.tsx | 6 +- .../detail/CourseSyllabus/LectureItem.tsx | 4 +- .../detail/CourseSyllabus/SectionItem.tsx | 4 +- .../course/editor/form/CourseBasicForm.tsx | 6 +- .../course/editor/form/CourseContentForm.tsx | 6 +- .../course/editor/form/LectureFormItem.tsx | 4 +- .../course/editor/form/LectureFormList.tsx | 2 +- .../course/editor/form/SectionFormItem.tsx | 6 +- .../editor/layout/CourseEditorHeader.tsx | 4 +- .../editor/layout/CourseEditorSidebar.tsx | 2 +- .../models/department/department-select.tsx | 10 - .../LetterEditor/form/LetterBasicForm.tsx | 5 +- .../models/role/role-editor/assign-list.tsx | 2 +- .../models/role/role-editor/role-list.tsx | 2 +- .../components/models/staff/staff-list.tsx | 6 +- .../models/staff/staff-transfer.tsx | 2 +- .../src/components/models/term/term-list.tsx | 2 +- .../src/components/presentation/NavBar.tsx | 2 +- .../components/presentation/dropdown-menu.tsx | 24 +- .../src/components/presentation/id-card.tsx | 4 +- .../components/presentation/phone-book.tsx | 4 +- .../video-player/ControlButtons/TimeLine.tsx | 2 +- .../src/components/utils/image-uploader.tsx | 2 +- apps/web/src/env.ts | 2 + apps/web/src/index.css | 7 +- apps/web/src/providers/theme-provider.tsx | 4 +- apps/web/tailwind.config.js | 53 --- apps/web/tailwind.config.ts | 13 + packages/theme/package.json | 35 ++ packages/theme/src/colors.ts | 24 ++ packages/theme/src/constants.ts | 25 ++ packages/theme/src/context.tsx | 50 +++ packages/theme/src/generator.ts | 212 ++++++++++ packages/theme/src/index.ts | 5 + packages/theme/src/styles.ts | 100 +++++ packages/theme/src/types.ts | 207 ++++++++++ packages/theme/src/utils.ts | 28 ++ packages/theme/tsconfig.json | 43 ++ packages/theme/tsup.config.ts | 13 + packages/ui/package.json | 3 + packages/ui/src/index.ts | 2 +- pnpm-lock.yaml | 86 ++++ 86 files changed, 1900 insertions(+), 1335 deletions(-) delete mode 100644 apps/web/src/app/auth/types.ts create mode 100644 apps/web/src/app/auth/useAuthForm.ts delete mode 100644 apps/web/src/app/login.tsx rename apps/web/src/components/{presentation/user => common/element}/Avatar.tsx (100%) create mode 100644 apps/web/src/components/common/element/Input.tsx create mode 100644 apps/web/src/components/common/element/TreeSelect.tsx create mode 100644 apps/web/src/components/common/form/FormField.tsx delete mode 100644 apps/web/src/components/common/form/FormInput.tsx delete mode 100644 apps/web/src/components/layout/main/side-bar.tsx delete mode 100644 apps/web/src/components/layout/main/top-nav-bar.tsx delete mode 100644 apps/web/src/components/layout/main/usermenu-dropdown.tsx create mode 100644 apps/web/src/components/layout/main/usermenu.tsx delete mode 100755 apps/web/tailwind.config.js create mode 100755 apps/web/tailwind.config.ts create mode 100644 packages/theme/package.json create mode 100644 packages/theme/src/colors.ts create mode 100644 packages/theme/src/constants.ts create mode 100644 packages/theme/src/context.tsx create mode 100644 packages/theme/src/generator.ts create mode 100644 packages/theme/src/index.ts create mode 100644 packages/theme/src/styles.ts create mode 100644 packages/theme/src/types.ts create mode 100644 packages/theme/src/utils.ts create mode 100644 packages/theme/tsconfig.json create mode 100644 packages/theme/tsup.config.ts diff --git a/apps/server/src/tasks/init/gendev.service.ts b/apps/server/src/tasks/init/gendev.service.ts index d955a18..91ba312 100644 --- a/apps/server/src/tasks/init/gendev.service.ts +++ b/apps/server/src/tasks/init/gendev.service.ts @@ -46,7 +46,7 @@ export class GenDevService { try { await this.calculateCounts(); await this.generateDepartments(3, 6); - await this.generateTerms(2, 6); + await this.generateTerms(1, 3); await this.generateStaffs(4); } catch (err) { @@ -60,7 +60,7 @@ export class GenDevService { this.logger.log(`${capitalizeFirstLetter(key)} count: ${value}`); }); } - private async generateTerms(depth: number = 2, count: number = 10) { + private async generateTerms(depth: number = 1, count: number = 5) { if (this.counts.termCount === 0) { this.logger.log('Generate terms'); await this.createTerms(null, TaxonomySlug.CATEGORY, depth, count); diff --git a/apps/web/package.json b/apps/web/package.json index 93e9ac4..77eb2f2 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -34,6 +34,7 @@ "@nice/common": "workspace:^", "@nice/iconer": "workspace:^", "@nice/ui": "workspace:^", + "@nice/theme": "workspace:^", "@tanstack/query-async-storage-persister": "^5.51.9", "@tanstack/react-query": "^5.51.21", "@tanstack/react-query-persist-client": "^5.51.9", diff --git a/apps/web/src/App.css b/apps/web/src/App.css index 8406459..4b64e11 100755 --- a/apps/web/src/App.css +++ b/apps/web/src/App.css @@ -9,7 +9,7 @@ --ag-borders-input: solid 1px; --ag-border-color: var(--color-border-secondary); --ag-secondary-border-color: var(--color-border-secondary); - --ag-secondary-foreground-color: var(--color-text-tertiary); + --ag-secondary-foreground-color: var(--color-text-tertiary-300); /* --ag-border-radius: 2px; */ --ag-header-column-separator-display: block; --ag-header-column-separator-height: 30%; diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 54a93a3..a8652ba 100755 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -4,14 +4,13 @@ import "./App.css"; import { RouterProvider } from "react-router-dom"; import QueryProvider from "./providers/query-provider"; import { router } from "./routes"; -import ThemeProvider from "./providers/theme-provider"; import { App as AntdApp, ConfigProvider, theme } from "antd"; import locale from "antd/locale/zh_CN"; import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; import { AuthProvider } from './providers/auth-provider'; import { Toaster } from 'react-hot-toast'; - +import {ThemeProvider} from "@nice/theme" dayjs.locale("zh-cn"); function App() { diff --git a/apps/web/src/app/auth/login.tsx b/apps/web/src/app/auth/login.tsx index fb481d3..9606d49 100644 --- a/apps/web/src/app/auth/login.tsx +++ b/apps/web/src/app/auth/login.tsx @@ -1,68 +1,69 @@ +import { Form, Input, Button } from "antd"; import { motion } from "framer-motion"; -export const LoginForm = ({ form, onSubmit, isLoading }) => { - const { register, handleSubmit, formState: { errors } } = form; +export interface LoginFormData { + username: string; + password: string; +} + +interface LoginFormProps { + onSubmit: (data: LoginFormData) => void; + isLoading: boolean; +} + +export const LoginForm = ({ onSubmit, isLoading }: LoginFormProps) => { + const [form] = Form.useForm(); + return ( - -

- Sign In -

-
-
- - {errors.username && ( - - {errors.username.message} - - )} -
-
- - {errors.password && ( - - {errors.password.message} - - )} -
-
- - -
+ + + + + + + + + + + + + ); -}; \ No newline at end of file +}; diff --git a/apps/web/src/app/auth/page.tsx b/apps/web/src/app/auth/page.tsx index 2cc7f86..39c0185 100644 --- a/apps/web/src/app/auth/page.tsx +++ b/apps/web/src/app/auth/page.tsx @@ -1,52 +1,30 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import { toast } from "react-hot-toast"; -import { AnimatePresence, motion } from "framer-motion"; -import { useForm } from "react-hook-form"; import { useAuth } from "@web/src/providers/auth-provider"; import { RegisterForm } from "./register"; import { LoginForm } from "./login"; -import { LoginFormInputs, RegisterFormInputs } from "./types"; -import { Button } from "@web/src/components/common/element/Button"; +import { Card, Typography, Button, Spin, Divider } from "antd"; +import { AnimatePresence, motion } from "framer-motion"; +import { useAuthForm } from "./useAuthForm"; +import { + GithubOutlined, + GoogleOutlined, + LoadingOutlined +} from '@ant-design/icons'; + +const { Title, Text, Paragraph } = Typography; const AuthPage: React.FC = () => { - const [showLogin, setShowLogin] = useState(true); - const [isLoading, setIsLoading] = useState(false); - const { login, isAuthenticated, signup } = useAuth(); + const { + showLogin, + isLoading, + toggleForm, + handleLogin, + handleRegister + } = useAuthForm(); + const { isAuthenticated } = useAuth(); const location = useLocation(); const navigate = useNavigate(); - const loginForm = useForm({ - mode: "onChange" - }); - const registerForm = useForm({ - mode: "onChange" - }); - - const onSubmitLogin = async (data: LoginFormInputs) => { - try { - setIsLoading(true); - console.log(data) - await login(data.username, data.password); - toast.success("Welcome back!"); - } catch (err: any) { - toast.error(err?.response?.data?.message || "Invalid credentials"); - } finally { - setIsLoading(false); - } - }; - - const onSubmitRegister = async (data: RegisterFormInputs) => { - try { - setIsLoading(true); - await signup(data); - toast.success("Registration successful!"); - setShowLogin(true); - } catch (err: any) { - toast.error(err?.response?.data?.message); - } finally { - setIsLoading(false); - } - }; useEffect(() => { if (isAuthenticated) { @@ -57,88 +35,110 @@ const AuthPage: React.FC = () => { }, [isAuthenticated, location]); return ( -
+
-
- {/* Left Panel */} - - - {/* Logo Section */} -
-
- United States Air Force Logo -
-
-
- - {/* Title Section */} -
-

- - USAF Leadership Portal - -

-

- {showLogin - ? "Access your secure USAF portal" - : "Create your authorized account"} -

-
- - {/* Switch Form Button */} - - - +
+ 欢迎来到 Leader Mail +
+ + 与领导者建立联系,分享见解,在专为职业发展设计的协作环境中共同成长。 + + {showLogin && ( + + )} + +
- {/* Right Panel - Forms */} -
- - {showLogin ? ( - - ) : ( - - )} - + {/* Right Panel - Form Section */} +
+ + + {showLogin ? ( + + + 登录 + + 还没有账户?{' '} + + + + + + + ) : ( + + + 创建账户 + + 已有账户?{' '} + + + + + + + )} + +
-
+
+ ); }; diff --git a/apps/web/src/app/auth/register.tsx b/apps/web/src/app/auth/register.tsx index 75c2343..613956e 100644 --- a/apps/web/src/app/auth/register.tsx +++ b/apps/web/src/app/auth/register.tsx @@ -1,203 +1,131 @@ +import DepartmentSelect from "@web/src/components/models/department/department-select"; +import { Form, Input, Button, Select } from "antd"; import { motion } from "framer-motion"; -// RegisterForm.tsx -export const RegisterForm = ({ form, onSubmit, isLoading }) => { - const { register, handleSubmit, formState: { errors }, watch } = form; - const password = watch("password"); +export interface RegisterFormData { + deptId: string; + username: string; + showname: string; + officerId: string; + password: string; + repeatPass: string; +} + +interface RegisterFormProps { + onSubmit: (data: RegisterFormData) => void; + isLoading: boolean; +} + +export const RegisterForm = ({ onSubmit, isLoading }: RegisterFormProps) => { + const [form] = Form.useForm(); return ( - -

- Create Account -

-
- {/* Department Selection */} -
- - - {errors.deptId && ( - - {errors.deptId.message} - - )} -
- - {/* User Information Row */} -
-
- - - {errors.username && ( - - {errors.username.message} - - )} -
- -
- - - {errors.showname && ( - - {errors.showname.message} - - )} -
-
- - {/* Service ID */} -
- - - {errors.officerId && ( - - {errors.officerId.message} - - )} -
- - {/* Password Fields */} -
-
- - - {errors.password && ( - - {errors.password.message} - - )} -
- -
- - value === password || "Passwords do not match" - })} - className="w-full px-4 py-2 bg-white/10 border border-gray-600 - rounded-lg focus:ring-2 focus:ring-blue-500 - text-white placeholder-gray-400" - placeholder="Confirm Password" - /> - {errors.repeatPass && ( - - {errors.repeatPass.message} - - )} -
-
-
- {/* Submit Button */} - + + -
+ + +
+ + + + + + + +
+ + + + + + + + + + ({ + validator(_, value) { + if (!value || getFieldValue('password') === value) { + return Promise.resolve(); + } + return Promise.reject(new Error('两次输入的密码不一致')); + }, + }), + ]} + > + + + + + + + + ); -}; \ No newline at end of file +}; diff --git a/apps/web/src/app/auth/types.ts b/apps/web/src/app/auth/types.ts deleted file mode 100644 index 6d6b894..0000000 --- a/apps/web/src/app/auth/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface LoginFormInputs { - username: string; - password: string; -} -export interface RegisterFormInputs extends LoginFormInputs { - deptId: string; - officerId: string; - showname: string; - repeatPass: string; -} \ No newline at end of file diff --git a/apps/web/src/app/auth/useAuthForm.ts b/apps/web/src/app/auth/useAuthForm.ts new file mode 100644 index 0000000..1a619a4 --- /dev/null +++ b/apps/web/src/app/auth/useAuthForm.ts @@ -0,0 +1,46 @@ +import { useState } from "react"; +import { toast } from "react-hot-toast"; +import { useAuth } from "@web/src/providers/auth-provider"; +import type { LoginFormData } from "./login"; +import type { RegisterFormData } from "./register"; + +export const useAuthForm = () => { + const [showLogin, setShowLogin] = useState(true); + const [isLoading, setIsLoading] = useState(false); + const { login, signup } = useAuth(); + + const toggleForm = () => setShowLogin(!showLogin); + + const handleLogin = async (data: LoginFormData) => { + try { + setIsLoading(true); + await login(data.username, data.password); + toast.success("登录成功!"); + } catch (err: any) { + toast.error(err?.response?.data?.message || "用户名或密码错误"); + } finally { + setIsLoading(false); + } + }; + + const handleRegister = async (data: RegisterFormData) => { + try { + setIsLoading(true); + await signup(data); + toast.success("注册成功!"); + setShowLogin(true); + } catch (err: any) { + toast.error(err?.response?.data?.message || "注册失败"); + } finally { + setIsLoading(false); + } + }; + + return { + showLogin, + isLoading, + toggleForm, + handleLogin, + handleRegister + }; +}; diff --git a/apps/web/src/app/denied.tsx b/apps/web/src/app/denied.tsx index c994375..cbe3a67 100644 --- a/apps/web/src/app/denied.tsx +++ b/apps/web/src/app/denied.tsx @@ -1,7 +1,7 @@ import { Empty } from "antd"; export default function DeniedPage() { - return
+ return
} \ No newline at end of file diff --git a/apps/web/src/app/error.tsx b/apps/web/src/app/error.tsx index c32c11b..f72001d 100755 --- a/apps/web/src/app/error.tsx +++ b/apps/web/src/app/error.tsx @@ -5,7 +5,7 @@ export default function ErrorPage() { return
哦?页面似乎出错了...
-
{error?.statusText || error?.message}
+
{error?.statusText || error?.message}
} \ No newline at end of file diff --git a/apps/web/src/app/login.tsx b/apps/web/src/app/login.tsx deleted file mode 100644 index a48bfc8..0000000 --- a/apps/web/src/app/login.tsx +++ /dev/null @@ -1,387 +0,0 @@ -import React, { useState, useRef, useEffect } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; -import { useAuth } from "../providers/auth-provider"; -import { toast } from "react-hot-toast"; -import { AnimatePresence, motion } from "framer-motion"; -import { useForm } from "react-hook-form"; -interface LoginFormInputs { - username: string; - password: string; -} -interface RegisterFormInputs extends LoginFormInputs { - deptId: string; - officerId: string; - showname: string; - repeatPass: string; -} -const LoginPage: React.FC = () => { - const [showLogin, setShowLogin] = useState(true); - const [isLoading, setIsLoading] = useState(false); - const { login, isAuthenticated, signup } = useAuth(); - const location = useLocation(); - const navigate = useNavigate(); - const loginForm = useForm({ - mode: "onChange" - }); - const registerForm = useForm({ - mode: "onChange" - }); - const onSubmitLogin = async (data: LoginFormInputs) => { - try { - setIsLoading(true); - await login(data.username, data.password); - toast.success("Welcome back!"); - } catch (err: any) { - toast.error(err?.response?.data?.message || "Invalid credentials"); - } finally { - setIsLoading(false); - } - }; - const onSubmitRegister = async (data: RegisterFormInputs) => { - try { - setIsLoading(true); - await signup(data); - toast.success("Registration successful!"); - setShowLogin(true); - } catch (err: any) { - toast.error(err?.response?.data?.message); - } finally { - setIsLoading(false); - } - }; - useEffect(() => { - if (isAuthenticated) { - const params = new URLSearchParams(location.search); - const redirectUrl = params.get("redirect_url") || "/"; - navigate(redirectUrl, { replace: true }); - } - }, [isAuthenticated, location]); - - return ( -
- -
- -
- USAF Logo -

- USAF Leadership Portal -

-

- {showLogin ? "Need an account?" : "Already registered?"} -

- -
-
- - {/* Right Panel */} -
- - {showLogin ? ( - - ) : ( - - )} - -
-
-
-
- ); -}; - -// RegisterForm.tsx -const RegisterForm = ({ form, onSubmit, isLoading }) => { - const { register, handleSubmit, formState: { errors }, watch } = form; - const password = watch("password"); - - return ( - -

- Create Account -

- -
- {/* Department Selection */} -
- - - {errors.deptId && ( - - {errors.deptId.message} - - )} -
- - {/* User Information Row */} -
-
- - - {errors.username && ( - - {errors.username.message} - - )} -
- -
- - - {errors.showname && ( - - {errors.showname.message} - - )} -
-
- - {/* Service ID */} -
- - - {errors.officerId && ( - - {errors.officerId.message} - - )} -
- - {/* Password Fields */} -
-
- - - {errors.password && ( - - {errors.password.message} - - )} -
- -
- - value === password || "Passwords do not match" - })} - className="w-full px-4 py-2 bg-white/10 border border-gray-600 - rounded-lg focus:ring-2 focus:ring-blue-500 - text-white placeholder-gray-400" - placeholder="Confirm Password" - /> - {errors.repeatPass && ( - - {errors.repeatPass.message} - - )} -
-
-
- {/* Submit Button */} - - -
- ); -}; -const LoginForm = ({ form, onSubmit, isLoading }) => { - const { register, handleSubmit, formState: { errors } } = form; - - return ( - -

- Sign In -

- -
-
- - {errors.username && ( - - {errors.username.message} - - )} -
- -
- - {errors.password && ( - - {errors.password.message} - - )} -
-
- - -
- ); -}; -export default LoginPage; diff --git a/apps/web/src/app/main/letter/list/Header.tsx b/apps/web/src/app/main/letter/list/Header.tsx index 0764ee9..1d7aa41 100644 --- a/apps/web/src/app/main/letter/list/Header.tsx +++ b/apps/web/src/app/main/letter/list/Header.tsx @@ -2,7 +2,7 @@ import { SearchFilters } from "./SearchFilter"; export function Header() { return ( -
+

公开信件列表

diff --git a/apps/web/src/app/main/letter/list/Pagination.tsx b/apps/web/src/app/main/letter/list/Pagination.tsx index f6df121..9f31cbf 100644 --- a/apps/web/src/app/main/letter/list/Pagination.tsx +++ b/apps/web/src/app/main/letter/list/Pagination.tsx @@ -15,13 +15,13 @@ export function Pagination({ }: PaginationProps) { const STYLE_CONFIG = { colors: { - primary: 'bg-[#003875]', // USAF Blue + primary: 'bg-primary', // USAF Blue hover: 'hover:bg-[#00264d]', // Darker USAF Blue - disabled: 'bg-[#e6e6e6]', + disabled: 'bg-disabled', text: { - primary: 'text-[#003875]', + primary: 'text-primary', light: 'text-white', - secondary: 'text-[#4a4a4a]' + secondary: 'text-tertiary-400' } }, components: { @@ -38,7 +38,7 @@ export function Pagination({ min-w-[2.5rem] h-10 text-sm font-medium transition-colors duration-200 - focus:outline-none focus:ring-2 focus:ring-[#003875] focus:ring-offset-2 + focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ` } }; @@ -82,7 +82,7 @@ export function Pagination({ className={` ${STYLE_CONFIG.components.button} ${STYLE_CONFIG.colors.text.secondary} - bg-white + bg-white `} > … diff --git a/apps/web/src/app/main/letter/list/SearchFilter.tsx b/apps/web/src/app/main/letter/list/SearchFilter.tsx index 5825439..dd697ee 100644 --- a/apps/web/src/app/main/letter/list/SearchFilter.tsx +++ b/apps/web/src/app/main/letter/list/SearchFilter.tsx @@ -28,7 +28,7 @@ export function SearchFilters({ placeholder="Search by keyword, sender, or unit..." className="w-full h-[46px] pl-12 pr-4 rounded-lg bg-white shadow-sm transition-all duration-200 - placeholder:text-gray-400 + placeholder:text-tertiary-400 focus:outline-none focus:border-[#00308F] focus:ring-2 focus:ring-[#00308F]/20" value={searchTerm} onChange={(e) => onSearchChange(e.target.value)} diff --git a/apps/web/src/app/main/letter/progress/page.tsx b/apps/web/src/app/main/letter/progress/page.tsx index 95722c5..df5b143 100644 --- a/apps/web/src/app/main/letter/progress/page.tsx +++ b/apps/web/src/app/main/letter/progress/page.tsx @@ -77,19 +77,19 @@ export default function LetterProgressPage() {

-

Ticket ID

+

Ticket ID

{status.ticketId}

-

Submitted Date

+

Submitted Date

{status.submittedDate}

-

Last Update

+

Last Update

{status.lastUpdate}

-

Title

+

Title

{status.title}

diff --git a/apps/web/src/app/main/letter/write/filter.tsx b/apps/web/src/app/main/letter/write/filter.tsx index ffede5b..6012465 100644 --- a/apps/web/src/app/main/letter/write/filter.tsx +++ b/apps/web/src/app/main/letter/write/filter.tsx @@ -14,7 +14,7 @@ export default function Filter() { return
- +
- + = ({ accept={allowedTypes.join(",")} className="hidden" /> - -

{placeholder}

+ +

{placeholder}

{isDragging && (

diff --git a/apps/web/src/components/layout/fix-header.tsx b/apps/web/src/components/layout/fix-header.tsx index d5aa1b1..f624ea7 100644 --- a/apps/web/src/components/layout/fix-header.tsx +++ b/apps/web/src/components/layout/fix-header.tsx @@ -113,7 +113,7 @@ const FixedHeader: React.FC = ({ + {value?.user.deptName && ( {value?.user?.deptName} )} diff --git a/apps/web/src/components/layout/main/Footer.tsx b/apps/web/src/components/layout/main/Footer.tsx index f14b05e..04a79a5 100644 --- a/apps/web/src/components/layout/main/Footer.tsx +++ b/apps/web/src/components/layout/main/Footer.tsx @@ -21,7 +21,7 @@ export function Footer() { drop-shadow-md"> 美国空军官方领导机关信箱 -

+

为美国空军提供安全可靠的通信服务

@@ -44,7 +44,7 @@ export function Footer() { 1-800-XXX-XXXX
-

+

由软件小组提供 24/7 专业技术支持

@@ -57,7 +57,7 @@ export function Footer() { {/* Bottom Section */}
+ text-tertiary-300"> 美国国防部授权网站 @@ -68,7 +68,7 @@ export function Footer() { 政府信息系统
-

+

© {new Date().getFullYear()} United States Air Force. All rights reserved.

diff --git a/apps/web/src/components/layout/main/Header.tsx b/apps/web/src/components/layout/main/Header.tsx index a8cb668..4718408 100644 --- a/apps/web/src/components/layout/main/Header.tsx +++ b/apps/web/src/components/layout/main/Header.tsx @@ -4,35 +4,49 @@ import { memo } from "react"; import { SearchBar } from "./SearchBar"; import { Logo } from "./Logo"; import Navigation from "./navigation"; +import { UserMenu} from "./usermenu"; +import { useAuth } from "@web/src/providers/auth-provider"; interface HeaderProps { onSearch?: (query: string) => void; } export const Header = memo(function Header({ onSearch }: HeaderProps) { + const { isAuthenticated } = useAuth() return (
-
-
+
+
- -
- - - Login - +
+ +
+
+ + { + !isAuthenticated ? + + Login + : + + + }
- +
); -}); +}); \ No newline at end of file diff --git a/apps/web/src/components/layout/main/notifications-panel.tsx b/apps/web/src/components/layout/main/notifications-panel.tsx index 43ef2b4..70251c9 100644 --- a/apps/web/src/components/layout/main/notifications-panel.tsx +++ b/apps/web/src/components/layout/main/notifications-panel.tsx @@ -23,7 +23,7 @@ export function NotificationsPanel({ notificationItems }: NotificationsPanelProp

Notifications

- + Mark all as read
@@ -44,7 +44,7 @@ export function NotificationsPanel({ notificationItems }: NotificationsPanelProp

{item.title}

{item.description}

-
+
{item.time}
@@ -55,7 +55,7 @@ export function NotificationsPanel({ notificationItems }: NotificationsPanelProp
-
diff --git a/apps/web/src/components/layout/main/search-bar.tsx b/apps/web/src/components/layout/main/search-bar.tsx index a965660..55cdafb 100644 --- a/apps/web/src/components/layout/main/search-bar.tsx +++ b/apps/web/src/components/layout/main/search-bar.tsx @@ -23,7 +23,7 @@ export function SearchBar({ recentSearches }: SearchBarProps) { : 'bg-gray-100 hover:bg-gray-200' } `}> - + setSearchQuery('')} > - + )}
diff --git a/apps/web/src/components/layout/main/search-dropdown.tsx b/apps/web/src/components/layout/main/search-dropdown.tsx index 60ed49f..39f27f4 100644 --- a/apps/web/src/components/layout/main/search-dropdown.tsx +++ b/apps/web/src/components/layout/main/search-dropdown.tsx @@ -25,7 +25,7 @@ export function SearchDropdown({ className="absolute top-12 left-4 right-4 bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden" >
-

Recent Searches

+

Recent Searches

{recentSearches.map((search, index) => ( setSearchQuery(search)} > - + {search} ))} diff --git a/apps/web/src/components/layout/main/side-bar.tsx b/apps/web/src/components/layout/main/side-bar.tsx deleted file mode 100644 index e85cfcf..0000000 --- a/apps/web/src/components/layout/main/side-bar.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { motion } from 'framer-motion'; -import { useNavigate, useLocation } from 'react-router-dom'; -import { NavItem } from '@nice/client'; - -interface SidebarProps { - navItems: Array; -} - -export function Sidebar({ navItems }: SidebarProps) { - const navigate = useNavigate(); - const location = useLocation(); - return ( - -
- {navItems.map((item, index) => { - const isActive = location.pathname === item.path; - return ( - { - navigate(item.path) - }} - className={`flex items-center gap-3 w-full p-3 rounded-lg transition-colors - ${isActive - ? 'bg-blue-50 text-blue-600' - : 'text-gray-700 hover:bg-blue-50 hover:text-blue-600' - }`} - > - {item.icon} - {item.label} - - ); - })} -
-
- ); -} \ No newline at end of file diff --git a/apps/web/src/components/layout/main/top-nav-bar.tsx b/apps/web/src/components/layout/main/top-nav-bar.tsx deleted file mode 100644 index 0d5aae6..0000000 --- a/apps/web/src/components/layout/main/top-nav-bar.tsx +++ /dev/null @@ -1,47 +0,0 @@ - -import { NotificationsDropdown } from './notifications-dropdown'; -import { SearchBar } from './search-bar'; -import { UserMenuDropdown } from './usermenu-dropdown'; -import { Bars3Icon } from '@heroicons/react/24/outline'; - -interface TopNavBarProps { - sidebarOpen: boolean; - setSidebarOpen: (open: boolean) => void; - notifications: number; - notificationItems: Array; - recentSearches: string[]; -} -export function TopNavBar({ - sidebarOpen, - setSidebarOpen, - notifications, - notificationItems, - recentSearches -}: TopNavBarProps) { - return ( - - ); -} \ No newline at end of file diff --git a/apps/web/src/components/layout/main/useNavItem.ts b/apps/web/src/components/layout/main/useNavItem.ts index 959c568..e14e709 100644 --- a/apps/web/src/components/layout/main/useNavItem.ts +++ b/apps/web/src/components/layout/main/useNavItem.ts @@ -11,7 +11,6 @@ import { useMemo } from "react"; // { to: "/write-letter", label: "举报投诉" }, // ] as const; - export function useNavItem() { const { data } = api.term.findMany.useQuery({ where: { @@ -20,19 +19,31 @@ export function useNavItem() { }); const navItems = useMemo(() => { - const defaultItems = [ - { to: "/letter-list", label: "公开信件" }, - { to: "/letter-progress", label: "进度查询" }, - { to: "/help", label: "使用帮助" }, + // 定义固定的导航项 + const staticItems = { + letterList: { to: "/letter-list", label: "公开信件" }, + letterProgress: { to: "/letter-progress", label: "进度查询" }, + help: { to: "/help", label: "使用帮助" } + }; + + if (!data) { + return [staticItems.letterList, staticItems.letterProgress, staticItems.help]; + } + + // 构建分类导航项 + const categoryItems = data.map(term => ({ + to: `/write-letter?category=${term.id}`, + label: term.name + })); + + // 按照指定顺序返回导航项 + return [ + staticItems.letterList, + ...categoryItems, + staticItems.letterProgress, + staticItems.help ]; - - if (!data) return defaultItems; - - return data.reduce((items, term) => { - items.push({ to: `/write-letter?category=${term.id}`, label: term.name }); - return items; - }, [{ to: "/letter-list", label: "公开信件" }].concat(defaultItems.slice(1))); }, [data]); return { navItems }; -} \ No newline at end of file +} diff --git a/apps/web/src/components/layout/main/usermenu-dropdown.tsx b/apps/web/src/components/layout/main/usermenu-dropdown.tsx deleted file mode 100644 index 846a043..0000000 --- a/apps/web/src/components/layout/main/usermenu-dropdown.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { useState, useRef, useEffect } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { ArrowLeftStartOnRectangleIcon, Cog6ToothIcon, QuestionMarkCircleIcon, UserCircleIcon } from '@heroicons/react/24/outline'; -import { useAuth } from '@web/src/providers/auth-provider'; -import { Avatar } from '../../presentation/user/Avatar'; -import { useClickOutside } from '@web/src/hooks/useClickOutside'; - -export function UserMenuDropdown() { - const [showMenu, setShowMenu] = useState(false); - const menuRef = useRef(null); - const { user, logout } = useAuth() - useClickOutside(menuRef, () => setShowMenu(false)); - const menuItems = [ - { icon: , label: '个人信息', action: () => { } }, - { icon: , label: '设置', action: () => { } }, - { icon: , label: '帮助', action: () => { } }, - { icon: , label: '注销', action: () => { logout() } }, - ]; - - return ( -
- setShowMenu(!showMenu)} - className="w-10 h-10" // 移除了边框相关的类 - > - - - - - {showMenu && ( - -
-

{user?.showname}

-

{user?.username}

-
- -
- {menuItems.map((item, index) => ( - - {item.icon} - {item.label} - - ))} -
-
- )} -
-
- ); -} \ No newline at end of file diff --git a/apps/web/src/components/layout/main/usermenu.tsx b/apps/web/src/components/layout/main/usermenu.tsx new file mode 100644 index 0000000..72a26fc --- /dev/null +++ b/apps/web/src/components/layout/main/usermenu.tsx @@ -0,0 +1,111 @@ +// ... existing imports ... + +import { UserCircleIcon, Cog6ToothIcon, QuestionMarkCircleIcon, ArrowLeftStartOnRectangleIcon } from "@heroicons/react/24/outline"; +import { useClickOutside } from "@web/src/hooks/useClickOutside"; +import { useAuth } from "@web/src/providers/auth-provider"; +import { motion, AnimatePresence } from "framer-motion"; +import { useState, useRef } from "react"; +import { Avatar } from "../../common/element/Avatar"; + +export function UserMenu() { + const [showMenu, setShowMenu] = useState(false); + const menuRef = useRef(null); + const { user, logout } = useAuth(); + useClickOutside(menuRef, () => setShowMenu(false)); + + const menuItems = [ + { + icon: , + label: '个人信息', + action: () => { }, + color: 'text-primary-600' + }, + { + icon: , + label: '设置', + action: () => { }, + color: 'text-gray-600' + }, + { + icon: , + label: '帮助', + action: () => { }, + color: 'text-gray-600' + }, + { + icon: , + label: '注销', + action: () => logout(), + color: 'text-red-600' + }, + ]; + + return ( +
+ setShowMenu(!showMenu)} + className="relative rounded-full focus:outline-none + focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 + focus:ring-offset-[#13294B]" + > + + + + + + {showMenu && ( + +
+

+ {user?.showname} +

+

+ {user?.username} +

+
+ +
+ {menuItems.map((item, index) => ( + + {item.icon} + {item.label} + + ))} +
+
+ )} +
+
+ ); +} diff --git a/apps/web/src/components/layout/user-header.tsx b/apps/web/src/components/layout/user-header.tsx index a62ff2a..788f6a9 100644 --- a/apps/web/src/components/layout/user-header.tsx +++ b/apps/web/src/components/layout/user-header.tsx @@ -15,7 +15,7 @@ export default function UserHeader() { style={{ color: token.colorTextLightSolid }} className="rounded flex items-center select-none justify-between">
{ // if (user?.pilot?.id) { // navigate(`/pilots/${user?.pilot.id}`); diff --git a/apps/web/src/components/models/course/card/CourseHeader.tsx b/apps/web/src/components/models/course/card/CourseHeader.tsx index 95780a1..5a22fd9 100644 --- a/apps/web/src/components/models/course/card/CourseHeader.tsx +++ b/apps/web/src/components/models/course/card/CourseHeader.tsx @@ -32,7 +32,7 @@ export const CourseHeader = ({

{title}

{subTitle &&

{subTitle}

} -
+
{level && (
diff --git a/apps/web/src/components/models/course/card/CourseStats.tsx b/apps/web/src/components/models/course/card/CourseStats.tsx index 0ff3906..fe8df81 100644 --- a/apps/web/src/components/models/course/card/CourseStats.tsx +++ b/apps/web/src/components/models/course/card/CourseStats.tsx @@ -22,7 +22,7 @@ export const CourseStats = ({
{averageRating.toFixed(1)}
-
+
{numberOfReviews} 观看量
@@ -35,7 +35,7 @@ export const CourseStats = ({
{completionRate}%
-
+
完成率
@@ -48,7 +48,7 @@ export const CourseStats = ({
{Math.floor(totalDuration / 60)}h {totalDuration % 60}m
-
+
总时长
diff --git a/apps/web/src/components/models/course/detail/CourseSyllabus/LectureItem.tsx b/apps/web/src/components/models/course/detail/CourseSyllabus/LectureItem.tsx index 9a87d75..95f6c74 100644 --- a/apps/web/src/components/models/course/detail/CourseSyllabus/LectureItem.tsx +++ b/apps/web/src/components/models/course/detail/CourseSyllabus/LectureItem.tsx @@ -23,12 +23,12 @@ export const LectureItem: React.FC = ({

{lecture.title}

{lecture.description && ( -

+

{lecture.description}

)}
-
+
{lecture.duration}分钟
diff --git a/apps/web/src/components/models/course/detail/CourseSyllabus/SectionItem.tsx b/apps/web/src/components/models/course/detail/CourseSyllabus/SectionItem.tsx index 100fb6e..930f9dd 100644 --- a/apps/web/src/components/models/course/detail/CourseSyllabus/SectionItem.tsx +++ b/apps/web/src/components/models/course/detail/CourseSyllabus/SectionItem.tsx @@ -32,7 +32,7 @@ export const SectionItem = React.forwardRef(

{section.title}

-

+

{section.totalLectures}节课 ·{" "} {Math.floor(section.totalDuration / 60)}分钟

@@ -41,7 +41,7 @@ export const SectionItem = React.forwardRef( - + diff --git a/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx b/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx index dd83771..ab6a430 100644 --- a/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseBasicForm.tsx @@ -1,6 +1,6 @@ import { SubmitHandler, useFormContext } from 'react-hook-form'; import { CourseLevel, CourseLevelLabel } from '@nice/common'; -import { FormInput } from '@web/src/components/common/form/FormInput'; + import { FormSelect } from '@web/src/components/common/form/FormSelect'; import { FormArrayField } from '@web/src/components/common/form/FormArrayField'; import { convertToOptions } from '@nice/client'; @@ -12,8 +12,8 @@ export function CourseBasicForm() { const { register, formState: { errors }, watch, handleSubmit } = useFormContext(); return (
- - + {/* */} + {/* */} diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm.tsx index 8e35690..c2c864e 100644 --- a/apps/web/src/components/models/course/editor/form/CourseContentForm.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseContentForm.tsx @@ -7,10 +7,10 @@ import SectionFormList from "./SectionFormList"; const CourseContentFormHeader = () =>

创建您的课程大纲

-

+

通过组织清晰的章节和课时,帮助学员更好地学习。建议:

-
    +
    • 将相关内容组织到章节中
    • 每个章节建议包含 3-7 个课时
    • 课时可以是视频、文章或测验
    • @@ -18,7 +18,7 @@ const CourseContentFormHeader = () =>
const CourseSectionEmpty = () => (
-
+

开始创建您的课程内容

点击下方按钮添加第一个章节

diff --git a/apps/web/src/components/models/course/editor/form/LectureFormItem.tsx b/apps/web/src/components/models/course/editor/form/LectureFormItem.tsx index a136a52..da27041 100644 --- a/apps/web/src/components/models/course/editor/form/LectureFormItem.tsx +++ b/apps/web/src/components/models/course/editor/form/LectureFormItem.tsx @@ -2,7 +2,7 @@ import { VideoCameraIcon, DocumentTextIcon, QuestionMarkCircleIcon, Bars3Icon, P import { Lecture } from "@nice/common"; import { Card } from "@web/src/components/common/container/Card"; import { Button } from "@web/src/components/common/element/Button"; -import { FormInput } from "@web/src/components/common/form/FormInput"; +// import { FormInput } from "@web/src/components/common/form/FormInput"; import { FormQuillInput } from "@web/src/components/common/form/FormQuillInput"; import FileUploader from "@web/src/components/common/uploader/FileUploader"; import { useState } from "react"; @@ -102,7 +102,7 @@ export function LectureEditor({ lecture, onUpdate }: LectureEditorProps) { onClick={() => onUpdate({ ...lecture, type: key })} className={`flex items-center space-x-2 px-6 py-2.5 rounded-lg transition-all ${lecture.type === key - ? 'bg-blue-50 text-blue-600 shadow-sm ring-1 ring-blue-100' + ? 'bg-blue-50 text-primary-600 shadow-sm ring-1 ring-blue-100' : 'text-gray-600 hover:bg-gray-50' }`} > diff --git a/apps/web/src/components/models/course/editor/form/LectureFormList.tsx b/apps/web/src/components/models/course/editor/form/LectureFormList.tsx index 0279af4..bce800c 100644 --- a/apps/web/src/components/models/course/editor/form/LectureFormList.tsx +++ b/apps/web/src/components/models/course/editor/form/LectureFormList.tsx @@ -112,7 +112,7 @@ export function LectureFormList({ lectures, sectionId, onUpdate }: LectureFormLi if (lectures.length === 0) { return ( -
+
暂无课程内容
); diff --git a/apps/web/src/components/models/course/editor/form/SectionFormItem.tsx b/apps/web/src/components/models/course/editor/form/SectionFormItem.tsx index c68a42b..4e87400 100644 --- a/apps/web/src/components/models/course/editor/form/SectionFormItem.tsx +++ b/apps/web/src/components/models/course/editor/form/SectionFormItem.tsx @@ -7,7 +7,7 @@ import { Section, Lecture } from "@nice/common"; import { useState } from "react"; import { LectureFormList } from "./LectureFormList"; import { cn } from "@web/src/utils/classname"; -import { FormInput } from "@web/src/components/common/form/FormInput"; +// import { FormInput } from "@web/src/components/common/form/FormInput"; import { Button } from "@web/src/components/common/element/Button"; import { Card } from "@web/src/components/common/container/Card"; interface SectionProps { @@ -50,10 +50,10 @@ export function SectionFormItem({ section, index, onUpdate, onDelete }: SectionP + bg-blue-50 text-primary-600 text-sm font-semibold rounded"> {index + 1} - + {/* */}
diff --git a/apps/web/src/components/models/course/editor/layout/CourseEditorHeader.tsx b/apps/web/src/components/models/course/editor/layout/CourseEditorHeader.tsx index 53a6c5d..07be5b4 100644 --- a/apps/web/src/components/models/course/editor/layout/CourseEditorHeader.tsx +++ b/apps/web/src/components/models/course/editor/layout/CourseEditorHeader.tsx @@ -13,7 +13,7 @@ const courseStatusVariant: Record = { }; export default function CourseEditorHeader() { const navigate = useNavigate(); - + const { handleSubmit, formState: { isValid, isDirty, errors } } = useFormContext() const { onSubmit, course } = useCourseEditor() @@ -33,7 +33,7 @@ export default function CourseEditorHeader() { {course?.status ? CourseStatusLabel[course.status] : CourseStatusLabel[CourseStatus.DRAFT]} {course?.totalDuration ? ( -
+
总时长 {course?.totalDuration}
diff --git a/apps/web/src/components/models/course/editor/layout/CourseEditorSidebar.tsx b/apps/web/src/components/models/course/editor/layout/CourseEditorSidebar.tsx index 143ab15..0aeb9fa 100644 --- a/apps/web/src/components/models/course/editor/layout/CourseEditorSidebar.tsx +++ b/apps/web/src/components/models/course/editor/layout/CourseEditorSidebar.tsx @@ -30,7 +30,7 @@ export default function CourseEditorSidebar({ key={index} onClick={() => onNavigate(item, index)} className={`w-full flex ${!isHovered ? 'justify-center' : 'items-center'} px-5 py-2.5 mb-1 rounded-lg transition-all duration-200 ${selectedSection === index - ? "bg-blue-50 text-blue-600 shadow-sm" + ? "bg-blue-50 text-primary-600 shadow-sm" : "text-gray-600 hover:bg-gray-50" }`} > diff --git a/apps/web/src/components/models/department/department-select.tsx b/apps/web/src/components/models/department/department-select.tsx index 79cb7f4..9a23358 100644 --- a/apps/web/src/components/models/department/department-select.tsx +++ b/apps/web/src/components/models/department/department-select.tsx @@ -120,16 +120,6 @@ export default function DepartmentSelect({ try { const allKeyIds = keys.map((key) => key.toString()).filter(Boolean) || []; - // const expandedNodes = await Promise.all( - // keys.map(async (key) => { - // return await utils.department.getChildSimpleTree.fetch({ - // deptId: key.toString(), - // domain, - // }); - // }) - // ); - // - //上面那样一个个拉会拉爆,必须直接拉deptIds const expandedNodes = await utils.department.getChildSimpleTree.fetch({ deptIds: allKeyIds, diff --git a/apps/web/src/components/models/post/LetterEditor/form/LetterBasicForm.tsx b/apps/web/src/components/models/post/LetterEditor/form/LetterBasicForm.tsx index 64a6b73..2b31cd4 100644 --- a/apps/web/src/components/models/post/LetterEditor/form/LetterBasicForm.tsx +++ b/apps/web/src/components/models/post/LetterEditor/form/LetterBasicForm.tsx @@ -4,7 +4,6 @@ import { LetterFormData, useLetterEditor, } from "../context/LetterEditorContext"; -import { FormInput } from "@web/src/components/common/form/FormInput"; import { FormQuillInput } from "@web/src/components/common/form/FormQuillInput"; import { api } from "@nice/client"; import { @@ -94,11 +93,11 @@ export function LetterBasicForm() { 主题 - + /> */} {/* 标签输入 */}
{role?.name} - 角色成员列表 + 角色成员列表
diff --git a/apps/web/src/components/models/role/role-editor/role-list.tsx b/apps/web/src/components/models/role/role-editor/role-list.tsx index ad3b8cb..6f06808 100644 --- a/apps/web/src/components/models/role/role-editor/role-list.tsx +++ b/apps/web/src/components/models/role/role-editor/role-list.tsx @@ -73,7 +73,7 @@ export default function RoleList() { style={{ background: item.id === role?.id ? token.colorPrimaryBg : "" }} - className={`p-2 hover:bg-textHover text-secondary ${item.id === role?.id ? " text-primary border-l-4 border-primaryHover" : ""} transition-all ease-in-out flex items-center justify-between `} + className={`p-2 hover:bg-textHover text-tertiary-300 ${item.id === role?.id ? " text-primary border-l-4 border-primaryHover" : ""} transition-all ease-in-out flex items-center justify-between `} key={item.id}>
diff --git a/apps/web/src/components/models/staff/staff-list.tsx b/apps/web/src/components/models/staff/staff-list.tsx index f10018f..6d3c918 100644 --- a/apps/web/src/components/models/staff/staff-list.tsx +++ b/apps/web/src/components/models/staff/staff-list.tsx @@ -90,7 +90,7 @@ const StaffList = ({ return ( params.value || ( - 未录入所属单位 + 未录入所属单位 ) ); }, @@ -112,7 +112,7 @@ const StaffList = ({ if (params?.data?.id) return ( params.value || ( - 未录入帐号 + 未录入帐号 ) ); }, @@ -128,7 +128,7 @@ const StaffList = ({ if (params?.data?.id) return ( params.value || ( - 未录入姓名 + 未录入姓名 ) ); }, diff --git a/apps/web/src/components/models/staff/staff-transfer.tsx b/apps/web/src/components/models/staff/staff-transfer.tsx index 3708839..a64070a 100644 --- a/apps/web/src/components/models/staff/staff-transfer.tsx +++ b/apps/web/src/components/models/staff/staff-transfer.tsx @@ -69,7 +69,7 @@ const StaffTransfer = forwardRef(({ staffs {item.title?.slice(0, 1).toUpperCase()} {item.title} - {item.description} + {item.description}
)} /> diff --git a/apps/web/src/components/models/term/term-list.tsx b/apps/web/src/components/models/term/term-list.tsx index 433e7f8..ab902cd 100644 --- a/apps/web/src/components/models/term/term-list.tsx +++ b/apps/web/src/components/models/term/term-list.tsx @@ -128,7 +128,7 @@ export default function TermList() { return (
- 分类项列表 + 分类项列表
{ -
-
- {/* Results Section */} - {status && ( -
-
-
-

- Feedback Details -

- - {status.status.toUpperCase()} - -
+ setLoading(true) + setTimeout(() => { + setStatus({ + status: 'in-progress', + ticketId: feedbackId, + submittedDate: '2025-01-15', + lastUpdate: '2025-01-21', + title: 'Aircraft Maintenance Schedule Inquiry' + }) + setLoading(false) + }, 1000) + } -
-
-

Ticket ID

-

{status.ticketId}

-
-
-

Submitted Date

-

{status.submittedDate}

-
-
-

Last Update

-

{status.lastUpdate}

-
-
-

Title

-

{status.title}

-
-
+ const getStatusColor = (status: string) => { + switch (status) { + case 'pending': + return 'orange' + case 'in-progress': + return 'blue' + case 'resolved': + return 'green' + default: + return 'gray' + } + } - {/* Progress Timeline */} -
-
-
-
- {['Submitted', 'In Review', 'Resolved'].map((step, index) => ( -
-
- {index + 1} -
-
{step}
-
- ))} -
-
-
-
-
- )} - + return ( +
+ {/* Header */} +
+
+ +
+

USAF Feedback Tracking System

+

Enter your ticket ID to track progress

+
- ) +
+ + {/* Main Content */} +
+ {/* Search Section */} + +
+ +
+ } + size="large" + value={feedbackId} + onChange={(e) => setFeedbackId(e.target.value)} + placeholder="e.g. USAF-2025-0123" + status={error ? 'error' : ''} + className="border border-gray-300" + /> + +
+ {error &&

{error}

} +
+
+ + {/* Results Section */} + {status && ( + +
+ {/* Header */} +
+

+ Ticket Details +

+ + {status.status} + +
+ + {/* Details Grid */} +
+
+

Ticket ID

+

{status.ticketId}

+
+
+

Submitted Date

+

{status.submittedDate}

+
+
+

Last Update

+

{status.lastUpdate}

+
+
+

Subject

+

{status.title}

+
+
+ + {/* Progress Timeline */} +
+ + } + /> + } + /> + } + /> + +
+
+
+ )} +
+
+ ) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 669a08e..4a8df5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -404,6 +404,9 @@ importers: tailwind-merge: specifier: ^2.6.0 version: 2.6.0 + usehooks-ts: + specifier: ^3.1.0 + version: 3.1.0(react@18.2.0) uuid: specifier: ^10.0.0 version: 10.0.0 @@ -6800,6 +6803,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + usehooks-ts@3.1.0: + resolution: {integrity: sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw==} + engines: {node: '>=16.15.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -14251,6 +14260,11 @@ snapshots: dependencies: react: 18.2.0 + usehooks-ts@3.1.0(react@18.2.0): + dependencies: + lodash.debounce: 4.0.8 + react: 18.2.0 + util-deprecate@1.0.2: {} util@0.12.5: