ai-course/app/backend/src/pages/textbook/resource.tsx

451 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Button, Input, message, Modal, Radio, Space, Table, TableProps } from 'antd';
import React, { useEffect, useState } from 'react';
import { GetResourceListApi, DelResourceItemApi, GetDetailApi } from '../../api/textbook';
import styles from './resource.module.less';
import { TableRowSelection } from 'antd/es/table/interface';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import LoadingPage from '../loading';
import { BackBartment } from '../../compenents';
import { useTranslation } from 'react-i18next';
import { DeleteOutlined, UploadOutlined } from '@ant-design/icons';
import CreateResourceModal from './compenents/Resource/CreateResourceModal';
interface ResourceBase {
id: number | null | string | undefined; // 资源id
typeId: number; // 分类Id
bookId: 0;
chapterId: 0;
name: string;
type: string;
ext: string;
size: 0;
duration: 0;
url: string;
cover: string;
status: 0;
creator: string;
updater: string;
createTime: string;
updateTime: string;
tenantId: string;
}
// 列表展示
interface ResourceDataType extends ResourceBase {
index?: number;
}
// 分页查询
interface ResourceResData {
data: {
records: ResourceDataType[];
total: number;
};
}
const ResourcePage = () => {
const navigate = useNavigate();
const { t } = useTranslation();
const [searchParams] = useSearchParams();
const title = searchParams.get('title');
const { bookId } = useParams();
const [isEdit, setIsEdit] = useState(false); // 是否编辑模式
const [editId, setEditId] = useState<number>(0);
const [isAddModalOpen, setIsAddModalOpen] = useState<boolean>(false);
const [confirmLoading, setConfirmLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false);
const [selectedId, setSelectedId] = useState<number | string | null>(null);
const [selectedIdList, setSelectedIdList] = useState<string[]>([]);
const [resource, setResource] = useState<ResourceBase[]>([]);
const [resourceTotal, setResourceTotal] = useState<number>(0);
const [type, setType] = useState<number>(0);
const [page, setPage] = useState<number>(1);
const [pageSize, setPageSize] = useState<number>(10);
const [sortOrder, setSortOrder] = useState<string>('');
const [sortField, setSortField] = useState<string>('');
const [searchData, setSearchData] = useState<string>('');
const [refresh, setRefresh] = useState(false);
const [pageLoading, setPageLoading] = useState<boolean>(false);
const TypeOptions = [
{ label: t('textbook.resource.typeList.all'), value: 0, color: '#f3f4f6' },
{ label: t('textbook.resource.typeList.video'), value: 1, color: '#dbeafe' },
{ label: t('textbook.resource.typeList.img'), value: 2, color: '#dcfce7' },
{ label: t('textbook.resource.typeList.doc'), value: 3, color: '#fef9c3' },
{ label: t('textbook.resource.typeList.audio'), value: 4, color: '#f3e8ff' },
{ label: t('textbook.resource.typeList.other'), value: 5, color: '#f3f4f6' },
];
// 排序字段/**/
/* name
size createTime这三个*/
// 列表数据
const columns: TableProps<ResourceBase>['columns'] = [
{
title: t('textbook.resource.title1'),
dataIndex: 'softNo',
align: 'left',
width: 300,
sorter: true,
},
{
title: t('textbook.resource.title2'),
dataIndex: 'softwareName',
align: 'left',
},
{
title: t('textbook.resource.title3'),
dataIndex: 'company',
ellipsis: true,
sorter: true,
},
{
title: t('textbook.resource.title4'),
dataIndex: 'version',
align: 'left',
ellipsis: true,
width: 140,
sorter: true,
},
{
title: t('textbook.resource.title5'),
// align: 'center',
render: (_, record) => (
<Space
size="middle"
style={{ display: 'flex', justifyContent: 'space-around', alignItems: 'center' }}
>
<a
key="pre"
onClick={() => {
console.log('预览');
}}
>
{/*<EyeOutlined />*/}
{t('commen.preview')}
</a>
<a
key={'edit'}
//@ts-ignore
onClick={() => {
getDetail(Number(record.id));
}}
>
{t('commen.edit')}
</a>
<a
key={'del'}
className="b-link c-red"
onClick={() => showDeleteConfirm(Number(record.id))}
>
{/*<DeleteOutlined />*/}
{t('commen.del')}
</a>
</Space>
),
},
];
// @ts-ignore
const getResourceList = async () => {
try {
const res = (await GetResourceListApi(
page,
pageSize,
type,
searchData,
sortOrder,
sortField
)) as ResourceResData;
if (res && 'data' in res && 'records' in res.data) {
setResource(res.data.records);
setResourceTotal(res.data.total);
} else {
console.warn('接口返回数据结构异常:', res);
setResource([]); // 设置为空数组防止崩溃
}
} catch (error) {
console.error('获取列表失败:', error);
}
};
const resetVirtualList = async () => {
try {
const res = (await GetResourceListApi(1, 10, null, null, null, null)) as ResourceResData;
if (res && 'data' in res && 'records' in res.data) {
setResource(res.data.records || []);
setResourceTotal(res.data.total);
} else {
console.warn('接口返回数据结构异常:', res);
setResource([]); // 设置为空数组防止崩溃
}
} catch (error) {
console.error('获取列表失败:', error);
}
};
const getDetail = async (id: number) => {
try {
const res = (await GetDetailApi(id)) as VirtualResDetail | undefined;
if (!res || !res.data) {
message.error('获取详情失败');
return;
}
setIsEdit(true);
//@ts-ignore
setItemDetailData(res.data);
setSelectedId(res.data.id);
// 打开弹窗
setIsAddModalOpen(true);
} catch (error) {
message.error('获取详情失败');
setPageLoading(false);
console.error('获取详情失败:', error);
}
};
const showDeleteConfirm = (id: number) => {
setSelectedId(id);
setModalVisible(true);
};
const handleDeleteItem = async () => {
if (selectedId === null) return;
setConfirmLoading(true);
try {
await DelResourceItemApi(selectedId.toString());
message.success('删除成功');
// @ts-ignore
await getVirtualList();
} catch (error) {
message.error('删除失败');
console.error('删除失败:', error);
} finally {
setConfirmLoading(false);
setModalVisible(false);
setSelectedId(null);
}
};
//弹窗取消
const showAddSoftModal = () => {
setIsEdit(false); // 设置为新增模式
setSelectedId(null); // 清除选中 ID
setIsAddModalOpen(true);
};
const handleCancelDeleteItem = () => {
setModalVisible(false);
};
const handleCancelDeleteItems = () => {
setIsConfirmModalOpen(false);
};
const handleReset = () => {
setPage(1);
setPageSize(10);
setType(0);
setSelectedIdList([]);
setSearchData('');
resetVirtualList();
};
// 批量删除
const handleDeleteItems = () => {
toDeleteSoftwareList();
};
const toDeleteSoftwareList = async () => {
try {
const selectedIdListString = selectedIdList.join(',');
const res = await DelResourceItemApi(selectedIdListString);
message.success('删除成功');
// @ts-ignore
await getVirtualList();
} catch (error) {
message.error('删除失败');
console.error('删除失败:', error);
} finally {
setConfirmLoading(false);
setIsConfirmModalOpen(false);
setSelectedId(null);
setSelectedIdList([]);
}
};
const handleAddCancel = () => {
setIsAddModalOpen(false);
setIsEdit(false);
setSelectedId(null);
setType(0);
};
useEffect(() => {
setResource([]);
getResourceList();
}, [refresh, page, pageSize, type, searchData]);
const onSelectChange = (newSelectedRowKeys: any[]) => {
setSelectedIdList(newSelectedRowKeys);
};
const canDelete = selectedIdList.length > 0;
const rowSelection: TableRowSelection<ResourceBase> = {
selectedRowKeys: selectedIdList,
onChange: onSelectChange,
};
useEffect(() => {
getResourceList();
}, [page, pageSize, type, sortOrder, sortField, searchData]);
const handleTableChange = (
pagination: { current: number; pageSize: number },
filters: any,
sorter: { field: string; order: string }
) => {
// 统一处理分页
// if (pagination.current !== page || pagination.pageSize !== pageSize) {
setPage(pagination.current);
setPageSize(pagination.pageSize);
// }
// 处理排序
if (sorter && sorter.field) {
const sortField = sorter.field as string;
const sortOrder =
sorter.order === 'ascend' ? 'asc' : sorter.order === 'descend' ? 'desc' : '';
console.log('排序字段:', sortField, '排序方向:', sortOrder);
}
};
return (
<>
<div className={styles.container}>
{pageLoading && <LoadingPage />}
<BackBartment title={`${t('textbook.resource.pageTitle')}`} />
<div className={styles.mainBox}>
<div className={styles.title}>: {title}</div>
<div className={styles.search}>
<div className={styles.types}>
<div className={styles.typeTitle}>{t('textbook.resource.type')}:</div>
<Radio.Group
optionType="button"
buttonStyle={'solid'}
defaultValue={0}
value={type}
onChange={(e) => {
console.log(e.target.value, 'data');
setType(e.target.value);
}}
style={{
display: 'flex',
height: 40,
borderRadius: 16,
gap: 8,
}}
>
{TypeOptions.map((option) => {
return (
<Radio.Button
key={option.value}
value={option.value}
style={{
borderRadius: '16px',
border: '1px solid #d9d9d9',
}}
>
{option.label}
</Radio.Button>
);
})}
</Radio.Group>
</div>
<div className={styles.btns}>
<Input
placeholder={t('textbook.resource.searchPlaceholder')}
style={{ marginRight: 15, width: 360 }}
value={searchData}
allowClear
onChange={(e) => setSearchData(e.target.value)}
/>
<Button type="primary" onClick={getResourceList}>
{t('commen.search')}
</Button>
<Button
variant="outlined"
style={{ marginRight: 15, marginLeft: 15 }}
onClick={handleReset}
>
{t('commen.reset')}
</Button>
<Button type="primary" style={{ marginRight: 15 }} onClick={showAddSoftModal}>
<UploadOutlined />
{t('textbook.resource.upload')}
</Button>
<Button
variant="outlined"
style={{ marginRight: 15 }}
disabled={!canDelete}
onClick={() => setIsConfirmModalOpen(true)}
>
<DeleteOutlined /> {t('commen.moreDel')}
</Button>
</div>
</div>
<Table<ResourceBase>
rowSelection={rowSelection}
columns={columns}
dataSource={resource}
// @ts-ignore
rowKey={(record) => record.id}
pagination={{
pageSize: pageSize,
current: page,
total: resourceTotal,
showSizeChanger: true,
align: 'start',
showTotal: (total) => `${resourceTotal} 条记录`,
}}
// @ts-ignore
onChange={handleTableChange}
/>
</div>
</div>
{isAddModalOpen && (
<CreateResourceModal
isEdit={isEdit}
isOpen={isAddModalOpen}
onCancel={handleAddCancel}
resourceId={editId}
bookId={bookId}
typeOptions={TypeOptions}
></CreateResourceModal>
)}
{/*删除确认*/}
<Modal
title="确认删除"
open={modalVisible}
onOk={handleDeleteItem}
onCancel={handleCancelDeleteItem}
confirmLoading={confirmLoading}
>
<p></p>
</Modal>
{/*多个删除确认*/}
<Modal
title="确认删除"
open={isConfirmModalOpen}
onOk={handleDeleteItems}
onCancel={handleCancelDeleteItems}
confirmLoading={confirmLoading}
>
<p></p>
</Modal>
</>
);
};
export default ResourcePage;