From a0447eab2f97452404e56b948048ac1b24d8d2d4 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Sun, 26 Jan 2025 09:28:38 +0800 Subject: [PATCH] add --- apps/server/src/models/base/base.service.ts | 10 +- apps/web/src/app/main/letter/inbox/Header.tsx | 74 ++++ apps/web/src/app/main/letter/inbox/page.tsx | 24 ++ apps/web/src/app/main/letter/index/page.tsx | 11 + .../web/src/app/main/letter/outbox/Header.tsx | 74 ++++ apps/web/src/app/main/letter/outbox/page.tsx | 20 ++ apps/web/src/app/main/letter/write/page.tsx | 1 + .../components/layout/element/usermenu.tsx | 324 +++++++++--------- .../src/components/layout/main/navigation.tsx | 170 ++++----- .../src/components/layout/main/useNavItem.tsx | 123 ++++--- apps/web/src/routes/index.tsx | 15 + 11 files changed, 552 insertions(+), 294 deletions(-) create mode 100644 apps/web/src/app/main/letter/inbox/Header.tsx create mode 100644 apps/web/src/app/main/letter/inbox/page.tsx create mode 100644 apps/web/src/app/main/letter/index/page.tsx create mode 100644 apps/web/src/app/main/letter/outbox/Header.tsx create mode 100644 apps/web/src/app/main/letter/outbox/page.tsx diff --git a/apps/server/src/models/base/base.service.ts b/apps/server/src/models/base/base.service.ts index 19fa918..9367d37 100644 --- a/apps/server/src/models/base/base.service.ts +++ b/apps/server/src/models/base/base.service.ts @@ -38,7 +38,7 @@ export class BaseService< protected prisma: PrismaClient, protected objectType: string, protected enableOrder: boolean = false, - ) { } + ) {} /** * Retrieves the name of the model dynamically. @@ -451,7 +451,11 @@ export class BaseService< pageSize?: number; where?: WhereArgs; select?: SelectArgs; - }): Promise<{ items: R['findMany']; totalPages: number, totalCount: number }> { + }): Promise<{ + items: R['findMany']; + totalPages: number; + totalCount: number; + }> { const { page = 1, pageSize = 10, where, select } = args; try { @@ -470,7 +474,7 @@ export class BaseService< return { items, totalPages, - totalCount: total + totalCount: total, }; } catch (error) { this.handleError(error, 'read'); diff --git a/apps/web/src/app/main/letter/inbox/Header.tsx b/apps/web/src/app/main/letter/inbox/Header.tsx new file mode 100644 index 0000000..3189c55 --- /dev/null +++ b/apps/web/src/app/main/letter/inbox/Header.tsx @@ -0,0 +1,74 @@ +export function Header() { + return ( +
+
+ {/* 主标题区域 */} +
+

+ 我收到的信件 +

+

+ 及时查看 • 快速处理 • 高效反馈 +

+
+ + {/* 服务特点说明 */} +
+
+ + + + 随时查看收到的信件 +
+
+ + + + 快速处理信件内容 +
+
+ + + + 高效反馈处理结果 +
+
+ + {/* 服务宗旨说明 */} +
+

+ 我们确保您能够及时查看、快速处理收到的信件, + 并通过高效反馈机制,提升沟通效率,助力工作顺利开展。 +

+
+
+
+ ); +} diff --git a/apps/web/src/app/main/letter/inbox/page.tsx b/apps/web/src/app/main/letter/inbox/page.tsx new file mode 100644 index 0000000..78c227e --- /dev/null +++ b/apps/web/src/app/main/letter/inbox/page.tsx @@ -0,0 +1,24 @@ +import LetterList from "@web/src/components/models/post/list/LetterList"; +import { Header } from "./Header"; +import { useAuth } from "@web/src/providers/auth-provider"; +export default function InboxPage() { + const { user } = useAuth(); + return ( + // 添加 flex flex-col 使其成为弹性布局容器 +
+
+ {/* 添加 flex-grow 使内容区域自动填充剩余空间 */} + + +
+ ); +} diff --git a/apps/web/src/app/main/letter/index/page.tsx b/apps/web/src/app/main/letter/index/page.tsx new file mode 100644 index 0000000..3dcea41 --- /dev/null +++ b/apps/web/src/app/main/letter/index/page.tsx @@ -0,0 +1,11 @@ +import { useAuth } from "@web/src/providers/auth-provider"; +import InboxPage from "../inbox/page"; +import LetterListPage from "../list/page"; + +export default function IndexPage() { + const { user } = useAuth(); + if (user) { + return ; + } + return ; +} diff --git a/apps/web/src/app/main/letter/outbox/Header.tsx b/apps/web/src/app/main/letter/outbox/Header.tsx new file mode 100644 index 0000000..31630bb --- /dev/null +++ b/apps/web/src/app/main/letter/outbox/Header.tsx @@ -0,0 +1,74 @@ +export function Header() { + return ( +
+
+ {/* 主标题区域 */} +
+

+ 我发出的信件 +

+

+ 清晰记录 • 实时跟踪 • 高效沟通 +

+
+ + {/* 服务特点说明 */} +
+
+ + + + 清晰记录发出的信件 +
+
+ + + + 实时跟踪信件状态 +
+
+ + + + 高效沟通信件进展 +
+
+ + {/* 服务宗旨说明 */} +
+

+ 我们确保您能够清晰记录发出的信件, + 实时跟踪信件状态,并通过高效沟通机制,确保信件处理顺利进行。 +

+
+
+
+ ); +} diff --git a/apps/web/src/app/main/letter/outbox/page.tsx b/apps/web/src/app/main/letter/outbox/page.tsx new file mode 100644 index 0000000..c39748a --- /dev/null +++ b/apps/web/src/app/main/letter/outbox/page.tsx @@ -0,0 +1,20 @@ +import LetterList from "@web/src/components/models/post/list/LetterList"; +import { Header } from "./Header"; +import { useAuth } from "@web/src/providers/auth-provider"; +export default function OutboxPage() { + const { user } = useAuth(); + return ( + // 添加 flex flex-col 使其成为弹性布局容器 +
+
+ {/* 添加 flex-grow 使内容区域自动填充剩余空间 */} + + +
+ ); +} diff --git a/apps/web/src/app/main/letter/write/page.tsx b/apps/web/src/app/main/letter/write/page.tsx index cb8ffba..3a0cf33 100644 --- a/apps/web/src/app/main/letter/write/page.tsx +++ b/apps/web/src/app/main/letter/write/page.tsx @@ -22,6 +22,7 @@ export default function WriteLetterPage() { const { data, isLoading, error } = api.staff.findManyWithPagination.useQuery({ page: currentPage, pageSize, + where: { deptId: selectedDept, OR: [{ diff --git a/apps/web/src/components/layout/element/usermenu.tsx b/apps/web/src/components/layout/element/usermenu.tsx index e261666..208dc34 100644 --- a/apps/web/src/components/layout/element/usermenu.tsx +++ b/apps/web/src/components/layout/element/usermenu.tsx @@ -4,194 +4,196 @@ import { motion, AnimatePresence } from "framer-motion"; import { useState, useRef, useCallback, useMemo } from "react"; import { Avatar } from "../../common/element/Avatar"; import { - UserOutlined, - SettingOutlined, - QuestionCircleOutlined, - LogoutOutlined + UserOutlined, + SettingOutlined, + QuestionCircleOutlined, + LogoutOutlined, } from "@ant-design/icons"; import { Spin } from "antd"; import { useNavigate } from "react-router-dom"; import { MenuItemType } from "./types"; const menuVariants = { - hidden: { opacity: 0, scale: 0.95, y: -10 }, - visible: { - opacity: 1, - scale: 1, - y: 0, - transition: { - type: "spring", - stiffness: 300, - damping: 30 - } - }, - exit: { - opacity: 0, - scale: 0.95, - y: -10, - transition: { - duration: 0.2 - } - } + hidden: { opacity: 0, scale: 0.95, y: -10 }, + visible: { + opacity: 1, + scale: 1, + y: 0, + transition: { + type: "spring", + stiffness: 300, + damping: 30, + }, + }, + exit: { + opacity: 0, + scale: 0.95, + y: -10, + transition: { + duration: 0.2, + }, + }, }; export function UserMenu() { - const [showMenu, setShowMenu] = useState(false); - const menuRef = useRef(null); - const { user, logout, isLoading } = useAuth(); - const navigate = useNavigate() - useClickOutside(menuRef, () => setShowMenu(false)); + const [showMenu, setShowMenu] = useState(false); + const menuRef = useRef(null); + const { user, logout, isLoading } = useAuth(); + const navigate = useNavigate(); + useClickOutside(menuRef, () => setShowMenu(false)); - const toggleMenu = useCallback(() => { - setShowMenu(prev => !prev); - }, []); + const toggleMenu = useCallback(() => { + setShowMenu((prev) => !prev); + }, []); - const menuItems: MenuItemType[] = useMemo(() => [ - { - icon: , - label: '个人信息', - action: () => { }, - }, - { - icon: , - label: '设置', - action: () => { - navigate('/admin/staff') - }, - }, - { - icon: , - label: '帮助', - action: () => { }, - }, - { - icon: , - label: '注销', - action: () => logout(), - }, - ], [logout]); + const menuItems: MenuItemType[] = useMemo( + () => [ + { + icon: , + label: "个人信息", + action: () => {}, + }, + { + icon: , + label: "设置", + action: () => { + navigate("/admin/staff"); + }, + }, + // { + // icon: , + // label: '帮助', + // action: () => { }, + // }, + { + icon: , + label: "注销", + action: () => logout(), + }, + ], + [logout] + ); - const handleMenuItemClick = useCallback((action: () => void) => { - action(); - setShowMenu(false); - }, []); + const handleMenuItemClick = useCallback((action: () => void) => { + action(); + setShowMenu(false); + }, []); - if (isLoading) { - return ( -
- -
- ); - } + if (isLoading) { + return ( +
+ +
+ ); + } - return ( -
- + - + - + aria-hidden="true" + /> + - - {showMenu && ( - - {/* User Profile Section */} -
+ {/* User Profile Section */} +
+
+ +
+ + {user?.showname || user?.username} + + + + 在线 + +
+
+
- > -
- -
- - {user?.showname || user?.username} - - - - 在线 - -
-
-
- - {/* Menu Items */} -
- {menuItems.map((item, index) => ( - - ))} -
-
- )} -
-
- ); + group-hover:translate-x-0.5 ${ + item.label === "注销" + ? "group-hover:text-red-600" + : "group-hover:text-[#003F6A]" + }`}> + {item.icon} + + {item.label} + + ))} + + + )} + + + ); } diff --git a/apps/web/src/components/layout/main/navigation.tsx b/apps/web/src/components/layout/main/navigation.tsx index 1a1cccd..27a1302 100644 --- a/apps/web/src/components/layout/main/navigation.tsx +++ b/apps/web/src/components/layout/main/navigation.tsx @@ -1,96 +1,108 @@ import { NavLink, useLocation } from "react-router-dom"; import { useNavItem } from "./useNavItem"; import { twMerge } from "tailwind-merge"; +import React from "react"; interface NavItem { - to: string; - label: string; - icon?: React.ReactNode; + to: string; + label: string; + icon?: React.ReactNode; } interface NavigationProps { - className?: string; + className?: string; } export default function Navigation({ className }: NavigationProps) { - const { navItems } = useNavItem(); - const location = useLocation(); + const { navItems } = useNavItem(); + const location = useLocation(); - const isActive = (to: string) => { - const [pathname, search] = to.split('?'); - return location.pathname === pathname && - (!search ? !location.search : location.search === `?${search}`); - }; + const isActive = (to: string) => { + const [pathname, search] = to.split("?"); + return ( + location.pathname === pathname && + (!search ? !location.search : location.search === `?${search}`) + ); + }; - return ( - - ); + {/* Mobile Navigation */} +
+
+ {navItems.map((item) => ( + + twMerge( + "px-3 py-1.5 text-sm font-medium rounded-full", + "transition-colors duration-200", + "text-gray-300 hover:text-white", + active && "bg-blue-500/20 text-white" + ) + }> + + {item.icon} + {item.label} + + + ))} +
+
+ + + ); } diff --git a/apps/web/src/components/layout/main/useNavItem.tsx b/apps/web/src/components/layout/main/useNavItem.tsx index c04a818..48f12f7 100644 --- a/apps/web/src/components/layout/main/useNavItem.tsx +++ b/apps/web/src/components/layout/main/useNavItem.tsx @@ -1,66 +1,87 @@ import { api } from "@nice/client"; import { TaxonomySlug } from "@nice/common"; -import { useMemo } from "react"; +import React, { useMemo } from "react"; +import { MailOutlined, SendOutlined } from "@ant-design/icons"; import { - FileTextOutlined, - ScheduleOutlined, - QuestionCircleOutlined, - FolderOutlined, - TagsOutlined + FileTextOutlined, + ScheduleOutlined, + QuestionCircleOutlined, + FolderOutlined, + TagsOutlined, } from "@ant-design/icons"; +import { useAuth } from "@web/src/providers/auth-provider"; export interface NavItem { - to: string; - label: string; - icon?: React.ReactNode; + to: string; + label: string; + icon?: React.ReactNode; } export function useNavItem() { - const { data } = api.term.findMany.useQuery({ - where: { - taxonomy: { slug: TaxonomySlug.CATEGORY } - } - }); + const { user } = useAuth(); + const { data } = api.term.findMany.useQuery({ + where: { + taxonomy: { slug: TaxonomySlug.CATEGORY }, + }, + }); - const navItems = useMemo(() => { - // 定义固定的导航项 - const staticItems = { - letterList: { - to: "/", - label: "公开信件", - icon: - }, - letterProgress: { - to: "/letter-progress", - label: "进度查询", - icon: - }, - help: { - to: "/help", - label: "使用帮助", - icon: - } - }; + const navItems = useMemo(() => { + // 定义固定的导航项 + const staticItems = { + inbox: { + to: user ? "/" : "/inbox", + label: "我收到的", + icon: , + }, + outbox: { + to: "/outbox", + label: "我发出的", + icon: , + }, + letterList: { + to: !user ? "/" : "/letter-list", + label: "公开信件", + icon: , + }, + letterProgress: { + to: "/letter-progress", + label: "进度查询", + icon: , + }, + // help: { + // to: "/help", + // label: "使用帮助", + // icon: + // } + }; - if (!data) { - return [staticItems.letterList, staticItems.letterProgress, staticItems.help]; - } + if (!data) { + return [ + user && staticItems.inbox, + user && staticItems.outbox, + staticItems.letterList, + staticItems.letterProgress, + // staticItems.help, + ].filter(Boolean); + } - // 构建分类导航项 - const categoryItems = data.map(term => ({ - to: `/write-letter?termId=${term.id}`, - label: term.name, - icon: - })); + // 构建分类导航项 + const categoryItems = data.map((term) => ({ + to: `/write-letter?termId=${term.id}`, + label: term.name, + icon: , + })); - // 按照指定顺序返回导航项 - return [ - staticItems.letterList, - staticItems.letterProgress, - ...categoryItems, - staticItems.help - ]; - }, [data]); + // 按照指定顺序返回导航项 + return [ + user && staticItems.inbox, + user && staticItems.outbox, + staticItems.letterList, + staticItems.letterProgress, + ...categoryItems, + // staticItems.help, + ].filter(Boolean); + }, [data]); - return { navItems }; + return { navItems }; } diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index a876714..dc0dca8 100755 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -23,6 +23,9 @@ import LetterDetailPage from "../app/main/letter/detail/page"; import AdminLayout from "../components/layout/admin/AdminLayout"; import { CustomRouteObject } from "./types"; import { adminRoute } from "./admin-route"; +import InboxPage from "../app/main/letter/inbox/page"; +import OutboxPage from "../app/main/letter/outbox/page"; +import IndexPage from "../app/main/letter/index/page"; export const routes: CustomRouteObject[] = [ { path: "/", @@ -36,8 +39,20 @@ export const routes: CustomRouteObject[] = [ { element: , children: [ + { + path: "inbox", + element: , + }, + { + path: "outbox", + element: , + }, { index: true, + element: , + }, + { + path: "letter-list", element: , }, {