add 0126-0947
This commit is contained in:
parent
cd25e48617
commit
ac18602e58
|
@ -1,25 +1,27 @@
|
|||
import { db, Prisma, PrismaClient } from "@nice/common";
|
||||
import { db, Prisma, PrismaClient } from '@nice/common';
|
||||
|
||||
export type Operations =
|
||||
| 'aggregate'
|
||||
| 'count'
|
||||
| 'create'
|
||||
| 'createMany'
|
||||
| 'delete'
|
||||
| 'deleteMany'
|
||||
| 'findFirst'
|
||||
| 'findMany'
|
||||
| 'findUnique'
|
||||
| 'update'
|
||||
| 'updateMany'
|
||||
| 'upsert';
|
||||
export type DelegateFuncs = { [K in Operations]: (args: any) => Promise<unknown> }
|
||||
| 'aggregate'
|
||||
| 'count'
|
||||
| 'create'
|
||||
| 'createMany'
|
||||
| 'delete'
|
||||
| 'deleteMany'
|
||||
| 'findFirst'
|
||||
| 'findMany'
|
||||
| 'findUnique'
|
||||
| 'update'
|
||||
| 'updateMany'
|
||||
| 'upsert';
|
||||
export type DelegateFuncs = {
|
||||
[K in Operations]: (args: any) => Promise<unknown>;
|
||||
};
|
||||
export type DelegateArgs<T> = {
|
||||
[K in keyof T]: T[K] extends (args: infer A) => Promise<any> ? A : never;
|
||||
[K in keyof T]: T[K] extends (args: infer A) => Promise<any> ? A : never;
|
||||
};
|
||||
|
||||
export type DelegateReturnTypes<T> = {
|
||||
[K in keyof T]: T[K] extends (args: any) => Promise<infer R> ? R : never;
|
||||
[K in keyof T]: T[K] extends (args: any) => Promise<infer R> ? R : never;
|
||||
};
|
||||
|
||||
export type WhereArgs<T> = T extends { where?: infer W } ? W : never;
|
||||
|
@ -28,17 +30,17 @@ export type DataArgs<T> = T extends { data: infer D } ? D : never;
|
|||
export type IncludeArgs<T> = T extends { include: infer I } ? I : never;
|
||||
export type OrderByArgs<T> = T extends { orderBy: infer O } ? O : never;
|
||||
export type UpdateOrderArgs = {
|
||||
id: string
|
||||
overId: string
|
||||
}
|
||||
id: string;
|
||||
overId: string;
|
||||
};
|
||||
export interface FindManyWithCursorType<T extends DelegateFuncs> {
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
where?: WhereArgs<DelegateArgs<T>['findUnique']>;
|
||||
select?: SelectArgs<DelegateArgs<T>['findUnique']>;
|
||||
orderBy?: OrderByArgs<DelegateArgs<T>['findMany']>
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
where?: WhereArgs<DelegateArgs<T>['findUnique']>;
|
||||
select?: SelectArgs<DelegateArgs<T>['findUnique']>;
|
||||
orderBy?: OrderByArgs<DelegateArgs<T>['findMany']>;
|
||||
}
|
||||
export type TransactionType = Omit<
|
||||
PrismaClient,
|
||||
'$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'
|
||||
>;
|
||||
PrismaClient,
|
||||
'$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends'
|
||||
>;
|
||||
|
|
|
@ -12,13 +12,13 @@ const PostDeleteManyArgsSchema: ZodType<Prisma.PostDeleteManyArgs> = z.any();
|
|||
const PostWhereInputSchema: ZodType<Prisma.PostWhereInput> = z.any();
|
||||
const PostSelectSchema: ZodType<Prisma.PostSelect> = z.any();
|
||||
const PostUpdateInputSchema: ZodType<Prisma.PostUpdateInput> = z.any();
|
||||
const PostOrderBySchema: ZodType<Prisma.PostOrderByWithRelationInput> = z.any()
|
||||
const PostOrderBySchema: ZodType<Prisma.PostOrderByWithRelationInput> = z.any();
|
||||
@Injectable()
|
||||
export class PostRouter {
|
||||
constructor(
|
||||
private readonly trpc: TrpcService,
|
||||
private readonly postService: PostService,
|
||||
) { }
|
||||
) {}
|
||||
router = this.trpc.router({
|
||||
create: this.trpc.protectProcedure
|
||||
.input(PostCreateArgsSchema)
|
||||
|
@ -104,7 +104,7 @@ export class PostRouter {
|
|||
pageSize: z.number().optional(),
|
||||
where: PostWhereInputSchema.optional(),
|
||||
select: PostSelectSchema.optional(),
|
||||
orderBy: PostOrderBySchema.optional()
|
||||
orderBy: PostOrderBySchema.optional(),
|
||||
}),
|
||||
) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword
|
||||
.query(async ({ input, ctx }) => {
|
||||
|
|
|
@ -101,22 +101,31 @@ export class PostService extends BaseService<Prisma.PostDelegate> {
|
|||
});
|
||||
}
|
||||
async findManyWithPagination(
|
||||
args: { page?: number; pageSize?: number; where?: Prisma.PostWhereInput; select?: Prisma.PostSelect<DefaultArgs>; orderBy?: Prisma.PostOrderByWithRelationInput },
|
||||
args: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
where?: Prisma.PostWhereInput;
|
||||
select?: Prisma.PostSelect<DefaultArgs>;
|
||||
orderBy?: Prisma.PostOrderByWithRelationInput;
|
||||
},
|
||||
staff?: UserProfile,
|
||||
clientIp?: string,
|
||||
) {
|
||||
if (!args.where) args.where = {};
|
||||
args.where.OR = await this.preFilter(args.where.OR, staff);
|
||||
return this.wrapResult(super.findManyWithPagination(args as any), async (result) => {
|
||||
const { items } = result;
|
||||
await Promise.all(
|
||||
items.map(async (item) => {
|
||||
await setPostRelation({ data: item, staff, clientIp });
|
||||
await this.setPerms(item, staff);
|
||||
}),
|
||||
);
|
||||
return { ...result, items };
|
||||
});
|
||||
return this.wrapResult(
|
||||
super.findManyWithPagination(args as any),
|
||||
async (result) => {
|
||||
const { items } = result;
|
||||
await Promise.all(
|
||||
items.map(async (item) => {
|
||||
await setPostRelation({ data: item, staff, clientIp });
|
||||
await this.setPerms(item, staff);
|
||||
}),
|
||||
);
|
||||
return { ...result, items };
|
||||
},
|
||||
);
|
||||
}
|
||||
protected async setPerms(data: Post, staff?: UserProfile) {
|
||||
if (!staff) return;
|
||||
|
|
|
@ -12,13 +12,15 @@ const StaffWhereInputSchema: ZodType<Prisma.StaffWhereInput> = z.any();
|
|||
const StaffSelectSchema: ZodType<Prisma.StaffSelect> = z.any();
|
||||
const StaffUpdateInputSchema: ZodType<Prisma.PostUpdateInput> = z.any();
|
||||
const StaffFindManyArgsSchema: ZodType<Prisma.StaffFindManyArgs> = z.any();
|
||||
const StaffOrderBySchema: ZodType<Prisma.StaffOrderByWithRelationInput> =
|
||||
z.any();
|
||||
@Injectable()
|
||||
export class StaffRouter {
|
||||
constructor(
|
||||
private readonly trpc: TrpcService,
|
||||
private readonly staffService: StaffService,
|
||||
private readonly staffRowService: StaffRowService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
router = this.trpc.router({
|
||||
create: this.trpc.procedure
|
||||
|
@ -78,12 +80,15 @@ export class StaffRouter {
|
|||
return this.staffService.updateOrder(input);
|
||||
}),
|
||||
findManyWithPagination: this.trpc.procedure
|
||||
.input(z.object({
|
||||
page: z.number(),
|
||||
pageSize: z.number().optional(),
|
||||
where: StaffWhereInputSchema.optional(),
|
||||
select: StaffSelectSchema.optional()
|
||||
})) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword
|
||||
.input(
|
||||
z.object({
|
||||
page: z.number(),
|
||||
pageSize: z.number().optional(),
|
||||
where: StaffWhereInputSchema.optional(),
|
||||
select: StaffSelectSchema.optional(),
|
||||
orderBy: StaffOrderBySchema.optional(),
|
||||
}),
|
||||
) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword
|
||||
.query(async ({ input }) => {
|
||||
return await this.staffService.findManyWithPagination(input);
|
||||
}),
|
||||
|
|
|
@ -105,7 +105,7 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
|||
* @returns 更新后的员工记录
|
||||
*/
|
||||
async updateUserDomain(data: { domainId?: string }, staff?: UserProfile) {
|
||||
let { domainId } = data;
|
||||
const { domainId } = data;
|
||||
if (staff.domainId !== domainId) {
|
||||
const result = await this.update({
|
||||
where: { id: staff.id },
|
||||
|
@ -120,14 +120,23 @@ export class StaffService extends BaseService<Prisma.StaffDelegate> {
|
|||
}
|
||||
}
|
||||
|
||||
async findManyWithPagination(args: { page?: number; pageSize?: number; where?: Prisma.StaffWhereInput; select?: Prisma.StaffSelect<DefaultArgs>; }) {
|
||||
async findManyWithPagination(args: {
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
where?: Prisma.StaffWhereInput;
|
||||
select?: Prisma.StaffSelect<DefaultArgs>;
|
||||
orderBy?: Prisma.StaffOrderByWithRelationInput;
|
||||
}) {
|
||||
if (args.where.deptId && typeof args.where.deptId === 'string') {
|
||||
const childDepts = await this.departmentService.getDescendantIds(args.where.deptId, true);
|
||||
const childDepts = await this.departmentService.getDescendantIds(
|
||||
args.where.deptId,
|
||||
true,
|
||||
);
|
||||
args.where.deptId = {
|
||||
in: childDepts
|
||||
}
|
||||
in: childDepts,
|
||||
};
|
||||
}
|
||||
|
||||
return super.findManyWithPagination(args)
|
||||
|
||||
return super.findManyWithPagination(args as any);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,139 +1,149 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
import { SendCard } from './SendCard';
|
||||
import { Spin, Empty, Input, Alert, Pagination } from 'antd';
|
||||
import { api, useTerm } from '@nice/client';
|
||||
import DepartmentSelect from '@web/src/components/models/department/department-select';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import WriteHeader from './WriteHeader';
|
||||
import { SendCard } from "./SendCard";
|
||||
import { Spin, Empty, Input, Alert, Pagination } from "antd";
|
||||
import { api, useTerm } from "@nice/client";
|
||||
import DepartmentSelect from "@web/src/components/models/department/department-select";
|
||||
import debounce from "lodash/debounce";
|
||||
import { SearchOutlined } from "@ant-design/icons";
|
||||
import WriteHeader from "./WriteHeader";
|
||||
|
||||
export default function WriteLetterPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const termId = searchParams.get('termId');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedDept, setSelectedDept] = useState<string>();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const pageSize = 10;
|
||||
const { getTerm } = useTerm()
|
||||
const [searchParams] = useSearchParams();
|
||||
const termId = searchParams.get("termId");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [selectedDept, setSelectedDept] = useState<string>();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const pageSize = 10;
|
||||
const { getTerm } = useTerm();
|
||||
|
||||
const { data, isLoading, error } = api.staff.findManyWithPagination.useQuery({
|
||||
page: currentPage,
|
||||
pageSize,
|
||||
|
||||
where: {
|
||||
deptId: selectedDept,
|
||||
OR: [{
|
||||
showname: {
|
||||
contains: searchQuery
|
||||
}
|
||||
}, {
|
||||
username: {
|
||||
contains: searchQuery
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
const { data, isLoading, error } =
|
||||
api.staff.findManyWithPagination.useQuery({
|
||||
page: currentPage,
|
||||
pageSize,
|
||||
where: {
|
||||
deptId: selectedDept,
|
||||
OR: [
|
||||
{
|
||||
showname: {
|
||||
contains: searchQuery,
|
||||
},
|
||||
},
|
||||
{
|
||||
username: {
|
||||
contains: searchQuery,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
orderBy: {
|
||||
order: "desc",
|
||||
},
|
||||
// orderBy:{
|
||||
|
||||
const resetPage = useCallback(() => {
|
||||
setCurrentPage(1);
|
||||
}, []);
|
||||
// }
|
||||
});
|
||||
|
||||
// Reset page when search or department changes
|
||||
useEffect(() => {
|
||||
resetPage();
|
||||
}, [searchQuery, selectedDept, resetPage]);
|
||||
const resetPage = useCallback(() => {
|
||||
setCurrentPage(1);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen shadow-elegant border-2 border-white rounded-xl bg-slate-200">
|
||||
<WriteHeader term={getTerm(termId)} />
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="mb-8 space-y-4">
|
||||
{/* Search and Filter Section */}
|
||||
<div className="flex flex-col md:flex-row gap-4 items-center">
|
||||
<DepartmentSelect
|
||||
variant='filled'
|
||||
size="large"
|
||||
value={selectedDept}
|
||||
onChange={setSelectedDept as any}
|
||||
className="w-1/2"
|
||||
/>
|
||||
<Input
|
||||
variant='filled'
|
||||
className={'w-1/2'}
|
||||
prefix={<SearchOutlined className="text-gray-400" />}
|
||||
placeholder="搜索领导姓名或职级..."
|
||||
onChange={debounce((e) => setSearchQuery(e.target.value), 300)}
|
||||
// Reset page when search or department changes
|
||||
useEffect(() => {
|
||||
resetPage();
|
||||
}, [searchQuery, selectedDept, resetPage]);
|
||||
|
||||
size="large"
|
||||
/>
|
||||
return (
|
||||
<div className="min-h-screen shadow-elegant border-2 border-white rounded-xl bg-slate-200">
|
||||
<WriteHeader term={getTerm(termId)} />
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="mb-8 space-y-4">
|
||||
{/* Search and Filter Section */}
|
||||
<div className="flex flex-col md:flex-row gap-4 items-center">
|
||||
<DepartmentSelect
|
||||
variant="filled"
|
||||
size="large"
|
||||
value={selectedDept}
|
||||
onChange={setSelectedDept as any}
|
||||
className="w-1/2"
|
||||
/>
|
||||
<Input
|
||||
variant="filled"
|
||||
className={"w-1/2"}
|
||||
prefix={
|
||||
<SearchOutlined className="text-gray-400" />
|
||||
}
|
||||
placeholder="搜索领导姓名或职级..."
|
||||
onChange={debounce(
|
||||
(e) => setSearchQuery(e.target.value),
|
||||
300
|
||||
)}
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{error && (
|
||||
<Alert
|
||||
message="加载失败"
|
||||
description="获取数据时出现错误,请刷新页面重试。"
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert
|
||||
message="加载失败"
|
||||
description="获取数据时出现错误,请刷新页面重试。"
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<Spin size="large" tip="加载中..." />
|
||||
</div>
|
||||
) : data?.items.length > 0 ? (
|
||||
<motion.div
|
||||
className="grid grid-cols-1 gap-6"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}>
|
||||
{data?.items.map((item: any) => (
|
||||
<SendCard
|
||||
key={item.id}
|
||||
staff={item}
|
||||
termId={termId || undefined}
|
||||
/>
|
||||
))}
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
className="text-center py-12"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}>
|
||||
<Empty
|
||||
description="没有找到匹配的收信人"
|
||||
className="py-12"
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<AnimatePresence>
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<Spin size="large" tip="加载中..." />
|
||||
</div>
|
||||
) : data?.items.length > 0 ? (
|
||||
<motion.div
|
||||
className="grid grid-cols-1 gap-6"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
{data?.items.map((item: any) => (
|
||||
<SendCard
|
||||
key={item.id}
|
||||
staff={item}
|
||||
termId={termId || undefined}
|
||||
/>
|
||||
))}
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
className="text-center py-12"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<Empty
|
||||
description="没有找到匹配的收信人"
|
||||
className="py-12"
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Pagination */}
|
||||
{data?.items.length > 0 && (
|
||||
<div className="flex justify-center mt-8">
|
||||
<Pagination
|
||||
current={currentPage}
|
||||
total={data?.totalPages || 0}
|
||||
pageSize={pageSize}
|
||||
onChange={(page) => {
|
||||
setCurrentPage(page);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
showSizeChanger={false}
|
||||
showTotal={(total) => `共 ${total} 条记录`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
{/* Pagination */}
|
||||
{data?.items.length > 0 && (
|
||||
<div className="flex justify-center mt-8">
|
||||
<Pagination
|
||||
current={currentPage}
|
||||
total={data?.totalPages || 0}
|
||||
pageSize={pageSize}
|
||||
onChange={(page) => {
|
||||
setCurrentPage(page);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
showSizeChanger={false}
|
||||
showTotal={(total) => `共 ${total} 条记录`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,113 +1,114 @@
|
|||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { Input, Pagination, Empty, Spin } from 'antd';
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { Input, Pagination, Empty, Spin } from "antd";
|
||||
import { api, RouterInputs } from "@nice/client";
|
||||
import { LetterCard } from "../LetterCard";
|
||||
import { NonVoid } from "@nice/utils";
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { postDetailSelect } from '@nice/common';
|
||||
export default function LetterList({ params }: { params: NonVoid<RouterInputs["post"]["findManyWithPagination"]> }) {
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
import { SearchOutlined } from "@ant-design/icons";
|
||||
import debounce from "lodash/debounce";
|
||||
import { postDetailSelect } from "@nice/common";
|
||||
export default function LetterList({
|
||||
params,
|
||||
}: {
|
||||
params: NonVoid<RouterInputs["post"]["findManyWithPagination"]>;
|
||||
}) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const { data, isLoading } = api.post.findManyWithPagination.useQuery({
|
||||
page: currentPage,
|
||||
pageSize: params.pageSize,
|
||||
where: {
|
||||
OR: [{
|
||||
title: {
|
||||
contains: searchText
|
||||
}
|
||||
}],
|
||||
...params?.where
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: "desc"
|
||||
},
|
||||
select: {
|
||||
...postDetailSelect,
|
||||
...params.select
|
||||
}
|
||||
});
|
||||
const { data, isLoading } = api.post.findManyWithPagination.useQuery({
|
||||
page: currentPage,
|
||||
pageSize: params.pageSize,
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
title: {
|
||||
contains: searchText,
|
||||
},
|
||||
},
|
||||
],
|
||||
...params?.where,
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: "desc",
|
||||
},
|
||||
select: {
|
||||
...postDetailSelect,
|
||||
...params.select,
|
||||
},
|
||||
});
|
||||
|
||||
const debouncedSearch = useMemo(
|
||||
() =>
|
||||
debounce((value: string) => {
|
||||
setSearchText(value);
|
||||
setCurrentPage(1);
|
||||
}, 300),
|
||||
[]
|
||||
);
|
||||
// Cleanup debounce on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
debouncedSearch.cancel();
|
||||
};
|
||||
}, [debouncedSearch]);
|
||||
const handleSearch = (value: string) => {
|
||||
debouncedSearch(value);
|
||||
};
|
||||
const debouncedSearch = useMemo(
|
||||
() =>
|
||||
debounce((value: string) => {
|
||||
setSearchText(value);
|
||||
setCurrentPage(1);
|
||||
}, 300),
|
||||
[]
|
||||
);
|
||||
// Cleanup debounce on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
debouncedSearch.cancel();
|
||||
};
|
||||
}, [debouncedSearch]);
|
||||
const handleSearch = (value: string) => {
|
||||
debouncedSearch(value);
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
setCurrentPage(page);
|
||||
// Scroll to top when page changes
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
const handlePageChange = (page: number) => {
|
||||
setCurrentPage(page);
|
||||
// Scroll to top when page changes
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Search Bar */}
|
||||
<div className="p-6 transition-all ">
|
||||
<Input
|
||||
variant="filled"
|
||||
className='w-full'
|
||||
placeholder="搜索信件标题..."
|
||||
allowClear
|
||||
size="large"
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
prefix={<SearchOutlined className="text-gray-400" />}
|
||||
/>
|
||||
</div>
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Search Bar */}
|
||||
<div className="p-6 transition-all ">
|
||||
<Input
|
||||
variant="filled"
|
||||
className="w-full"
|
||||
placeholder="搜索信件标题..."
|
||||
allowClear
|
||||
size="large"
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
prefix={<SearchOutlined className="text-gray-400" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="flex-grow px-6">
|
||||
{isLoading ? (
|
||||
<div className='flex justify-center items-center pt-6'>
|
||||
<Spin size='large'></Spin>
|
||||
</div>
|
||||
) : data?.items.length ? (
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-1 gap-4 mb-6">
|
||||
{data.items.map((letter: any) => (
|
||||
<LetterCard key={letter.id} letter={letter} />
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-center pb-6">
|
||||
<Pagination
|
||||
current={currentPage}
|
||||
total={data.totalCount}
|
||||
pageSize={params.pageSize}
|
||||
onChange={handlePageChange}
|
||||
showSizeChanger={false}
|
||||
showQuickJumper
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col justify-center items-center pt-6">
|
||||
<Empty
|
||||
|
||||
description={
|
||||
searchText ? "未找到相关信件" : "暂无信件"
|
||||
}
|
||||
|
||||
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
{/* Content Area */}
|
||||
<div className="flex-grow px-6">
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center items-center pt-6">
|
||||
<Spin size="large"></Spin>
|
||||
</div>
|
||||
) : data?.items.length ? (
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-1 gap-4 mb-6">
|
||||
{data.items.map((letter: any) => (
|
||||
<LetterCard key={letter.id} letter={letter} />
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-center pb-6">
|
||||
<Pagination
|
||||
current={currentPage}
|
||||
total={data.totalCount}
|
||||
pageSize={params.pageSize}
|
||||
onChange={handlePageChange}
|
||||
showSizeChanger={false}
|
||||
showQuickJumper
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col justify-center items-center pt-6">
|
||||
<Empty
|
||||
description={
|
||||
searchText ? "未找到相关信件" : "暂无信件"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"dev-static": "tsup --no-watch",
|
||||
"clean": "rimraf dist",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue