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