feat: 完成代码编写

This commit is contained in:
2025-08-08 03:03:59 -07:00
parent 19fe24660e
commit aead2ff68f
26 changed files with 793 additions and 328 deletions
@@ -0,0 +1,158 @@
'use client';
import { queryAnnouncement } from '@/services/user/announcement';
import { useQuery } from '@tanstack/react-query';
import {
ColumnFiltersState,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
PaginationState,
useReactTable,
} from '@tanstack/react-table';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@workspace/airo-ui/components/dialog';
import { Pagination } from '@workspace/airo-ui/custom-components/pro-table/pagination';
import { useImperativeHandle, useRef, useState } from 'react';
import { Popup, PopupData, PopupRef } from './Popup';
export interface AnnouncementItem {
id: number;
title: string;
content: string;
pinned?: boolean;
}
export interface DialogRef {
open: () => void;
close: () => void;
}
interface DialogProps {
ref?: React.Ref<DialogRef>;
}
export const AnnouncementDialog = ({ ref }: DialogProps) => {
const [open, setOpen] = useState(false);
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const popupRef = useRef<PopupRef>(null);
const { data: announcementData, isLoading } = useQuery({
queryKey: ['queryAnnouncement', pagination.pageIndex + 1, pagination.pageSize],
queryFn: async () => {
const { data } = await queryAnnouncement({
page: pagination.pageIndex + 1,
size: pagination.pageSize,
pinned: true,
popup: true,
});
return {
list: data.data?.announcements || [],
total: data.data?.total || 0,
};
},
enabled: open, // 只在弹窗打开时查询数据
});
const table = useReactTable({
data: announcementData?.list || [],
columns: [],
onPaginationChange: setPagination,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getFilteredRowModel: getFilteredRowModel(),
state: {
columnFilters,
pagination,
},
manualPagination: true,
manualFiltering: true,
rowCount: announcementData?.total || 0,
});
useImperativeHandle(ref, () => ({
open: () => {
setOpen(true);
},
close: () => {
setOpen(false);
},
}));
const handleOpenPopup = (item: AnnouncementItem) => {
const popupData: PopupData = {
title: item.title,
content: item.content,
};
popupRef.current?.open(popupData);
};
return (
<>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className='flex flex-col sm:grid sm:max-w-[600px]'>
<DialogHeader>
<DialogTitle className={'text-left text-2xl sm:text-4xl'}></DialogTitle>
</DialogHeader>
<div className='space-y-4'>
{isLoading ? (
<div className='py-8 text-center'>...</div>
) : (
<>
{announcementData?.list?.map((item: AnnouncementItem) => {
return (
<div
key={item.id}
className='flex items-center rounded-[20px] bg-[#B5C9E2] px-4 py-2 sm:p-4'
>
<p className='line-clamp-2 flex-1 text-[10px] text-[#225BA9] sm:text-sm'>
{item.pinned && '【置顶公告】'}{' '}
<span className={`${item.pinned ? 'text-white' : 'text-[#4D4D4D]'}`}>
{item.content}
</span>
</p>
<div className='ml-2 w-[65px] text-right'>
<span
className='cursor-pointer text-xs text-[#225BA9] sm:text-sm'
onClick={() => handleOpenPopup(item)}
>
</span>
</div>
</div>
);
})}
{/* 使用 table 的分页器组件 */}
{announcementData && announcementData.list && announcementData.list.length > 0 && (
<div className='mt-6'>
<Pagination
table={table}
text={{
textRowsPerPage: '每页显示',
textPageOf: (pageIndex, pageCount) =>
`${pageIndex} 页,共 ${pageCount}`,
}}
/>
</div>
)}
</>
)}
</div>
</DialogContent>
</Dialog>
<Popup ref={popupRef} />
</>
);
};
@@ -0,0 +1,53 @@
'use client';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@workspace/airo-ui/components/dialog';
import { Markdown } from '@workspace/ui/custom-components/markdown';
import { useImperativeHandle, useState } from 'react';
export interface PopupData {
title: string;
content: string;
}
export interface PopupRef {
open: (data: PopupData) => void;
close: () => void;
}
interface PopupProps {
ref?: React.Ref<PopupRef>;
}
export const Popup = ({ ref }: PopupProps) => {
const [open, setOpen] = useState(false);
const [data, setData] = useState<PopupData>({ title: '', content: '' });
useImperativeHandle(ref, () => ({
open: (newData: PopupData) => {
setData(newData);
setOpen(true);
},
close: () => {
setOpen(false);
},
}));
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className='max-h-[350px] min-h-[220px] max-w-[80%] rounded-[25px] sm:max-w-[425px]'>
<DialogHeader>
<DialogTitle className={'sr-only'}>{data.title}</DialogTitle>
</DialogHeader>
<div className={'pt-[30%]'}>
<div className={'mb-4 text-xl'}>{data.title}</div>
<Markdown>{data.content}</Markdown>
</div>
</DialogContent>
</Dialog>
);
};