|
@@ -1,63 +1,51 @@
|
|
|
-import React, { useState, useEffect } from 'react';
|
|
|
|
|
-import { Card, Table, Button, Modal, Form, Input, Select, message, Space, Switch, Popconfirm, TreeSelect } from 'antd';
|
|
|
|
|
|
|
+import React, { useState } from 'react';
|
|
|
|
|
+import { Card, Table, Button, Modal, Form, Input, message, Space, Switch, Popconfirm } from 'antd';
|
|
|
import { PlusOutlined, EditOutlined, DeleteOutlined, FolderOutlined } from '@ant-design/icons';
|
|
import { PlusOutlined, EditOutlined, DeleteOutlined, FolderOutlined } from '@ant-design/icons';
|
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
|
-import type { InferResponseType, InferRequestType } from 'hono/client';
|
|
|
|
|
|
|
+import type { InferResponseType } from 'hono/client';
|
|
|
import { silverUsersClient } from '@/client/api';
|
|
import { silverUsersClient } from '@/client/api';
|
|
|
|
|
+import KnowledgeCategoryTreeSelect from '@/client/admin/components/KnowledgeCategoryTreeSelect';
|
|
|
|
|
+import {
|
|
|
|
|
+ useKnowledgeCategories,
|
|
|
|
|
+ useCreateKnowledgeCategory,
|
|
|
|
|
+ useUpdateKnowledgeCategory,
|
|
|
|
|
+ useDeleteKnowledgeCategory,
|
|
|
|
|
+ useToggleKnowledgeCategoryStatus,
|
|
|
|
|
+} from '@/client/admin/hooks/useKnowledgeCategoriesQuery';
|
|
|
|
|
|
|
|
const { TextArea } = Input;
|
|
const { TextArea } = Input;
|
|
|
|
|
|
|
|
type KnowledgeCategory = InferResponseType<typeof silverUsersClient['knowledge-categories']['$get'], 200>['data'][0];
|
|
type KnowledgeCategory = InferResponseType<typeof silverUsersClient['knowledge-categories']['$get'], 200>['data'][0];
|
|
|
-type CreateCategoryRequest = InferRequestType<typeof silverUsersClient['knowledge-categories']['$post']>['json'];
|
|
|
|
|
-type UpdateCategoryRequest = InferRequestType<typeof silverUsersClient['knowledge-categories'][':id']['$put']>['json'];
|
|
|
|
|
|
|
|
|
|
const KnowledgeCategories: React.FC = () => {
|
|
const KnowledgeCategories: React.FC = () => {
|
|
|
- const [data, setData] = useState<KnowledgeCategory[]>([]);
|
|
|
|
|
- const [loading, setLoading] = useState(false);
|
|
|
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
|
const [modalType, setModalType] = useState<'create' | 'edit'>('create');
|
|
const [modalType, setModalType] = useState<'create' | 'edit'>('create');
|
|
|
const [currentRecord, setCurrentRecord] = useState<KnowledgeCategory | null>(null);
|
|
const [currentRecord, setCurrentRecord] = useState<KnowledgeCategory | null>(null);
|
|
|
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
|
|
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
|
|
|
const [form] = Form.useForm();
|
|
const [form] = Form.useForm();
|
|
|
const [searchText, setSearchText] = useState('');
|
|
const [searchText, setSearchText] = useState('');
|
|
|
- const [categories, setCategories] = useState<KnowledgeCategory[]>([]);
|
|
|
|
|
|
|
|
|
|
- const fetchData = async (page = 1, pageSize = 10) => {
|
|
|
|
|
- setLoading(true);
|
|
|
|
|
- try {
|
|
|
|
|
- const response = await silverUsersClient['knowledge-categories'].$get({
|
|
|
|
|
- query: { page, pageSize, keyword: searchText || undefined }
|
|
|
|
|
- });
|
|
|
|
|
- const result = await response.json();
|
|
|
|
|
- setData(result.data);
|
|
|
|
|
- setPagination({
|
|
|
|
|
- current: page,
|
|
|
|
|
- pageSize,
|
|
|
|
|
- total: result.pagination.total
|
|
|
|
|
- });
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- message.error('获取数据失败');
|
|
|
|
|
- } finally {
|
|
|
|
|
- setLoading(false);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ // React Query hooks
|
|
|
|
|
+ const { data: categoriesData, isLoading, refetch } = useKnowledgeCategories({
|
|
|
|
|
+ page: pagination.current,
|
|
|
|
|
+ pageSize: pagination.pageSize,
|
|
|
|
|
+ keyword: searchText,
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- fetchData();
|
|
|
|
|
- loadCategories();
|
|
|
|
|
- }, []);
|
|
|
|
|
|
|
+ const createMutation = useCreateKnowledgeCategory();
|
|
|
|
|
+ const updateMutation = useUpdateKnowledgeCategory();
|
|
|
|
|
+ const deleteMutation = useDeleteKnowledgeCategory();
|
|
|
|
|
+ const toggleStatusMutation = useToggleKnowledgeCategoryStatus();
|
|
|
|
|
|
|
|
- const loadCategories = async () => {
|
|
|
|
|
- try {
|
|
|
|
|
- const response = await silverUsersClient['knowledge-categories'].$get({
|
|
|
|
|
- query: { page: 1, pageSize: 100 }
|
|
|
|
|
- });
|
|
|
|
|
- const result = await response.json();
|
|
|
|
|
- setCategories(result.data);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- message.error('加载分类失败');
|
|
|
|
|
|
|
+ // 更新分页信息
|
|
|
|
|
+ React.useEffect(() => {
|
|
|
|
|
+ if (categoriesData?.pagination) {
|
|
|
|
|
+ setPagination(prev => ({
|
|
|
|
|
+ ...prev,
|
|
|
|
|
+ total: categoriesData.pagination.total,
|
|
|
|
|
+ }));
|
|
|
}
|
|
}
|
|
|
- };
|
|
|
|
|
|
|
+ }, [categoriesData]);
|
|
|
|
|
|
|
|
const handleCreate = () => {
|
|
const handleCreate = () => {
|
|
|
setModalType('create');
|
|
setModalType('create');
|
|
@@ -77,15 +65,7 @@ const KnowledgeCategories: React.FC = () => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleDelete = async (id: number) => {
|
|
const handleDelete = async (id: number) => {
|
|
|
- try {
|
|
|
|
|
- await silverUsersClient['knowledge-categories'][':id']['$delete']({
|
|
|
|
|
- param: { id: id.toString() }
|
|
|
|
|
- });
|
|
|
|
|
- message.success('删除成功');
|
|
|
|
|
- fetchData(pagination.current, pagination.pageSize);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- message.error('删除失败');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ deleteMutation.mutate(id);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleSubmit = async () => {
|
|
const handleSubmit = async () => {
|
|
@@ -93,57 +73,51 @@ const KnowledgeCategories: React.FC = () => {
|
|
|
const values = await form.validateFields();
|
|
const values = await form.validateFields();
|
|
|
|
|
|
|
|
if (modalType === 'create') {
|
|
if (modalType === 'create') {
|
|
|
- await silverUsersClient['knowledge-categories'].$post({
|
|
|
|
|
- json: values as CreateCategoryRequest
|
|
|
|
|
- });
|
|
|
|
|
- message.success('创建成功');
|
|
|
|
|
- } else {
|
|
|
|
|
- await silverUsersClient['knowledge-categories'][':id']['$put']({
|
|
|
|
|
- param: { id: currentRecord!.id.toString() },
|
|
|
|
|
- json: values as UpdateCategoryRequest
|
|
|
|
|
- });
|
|
|
|
|
- message.success('更新成功');
|
|
|
|
|
|
|
+ createMutation.mutate(values);
|
|
|
|
|
+ } else if (currentRecord) {
|
|
|
|
|
+ updateMutation.mutate({ id: currentRecord.id, data: values });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
setModalVisible(false);
|
|
setModalVisible(false);
|
|
|
- fetchData(pagination.current, pagination.pageSize);
|
|
|
|
|
- loadCategories(); // 重新加载分类树
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- message.error(modalType === 'create' ? '创建失败' : '更新失败');
|
|
|
|
|
|
|
+ console.error('表单验证失败:', error);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const handleTableChange = (pagination: any) => {
|
|
|
|
|
- fetchData(pagination.current, pagination.pageSize);
|
|
|
|
|
|
|
+ const handleTableChange = (newPagination: any) => {
|
|
|
|
|
+ setPagination(prev => ({
|
|
|
|
|
+ ...prev,
|
|
|
|
|
+ current: newPagination.current,
|
|
|
|
|
+ pageSize: newPagination.pageSize,
|
|
|
|
|
+ }));
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleSearch = () => {
|
|
const handleSearch = () => {
|
|
|
- fetchData(1, pagination.pageSize);
|
|
|
|
|
|
|
+ setPagination(prev => ({ ...prev, current: 1 }));
|
|
|
|
|
+ refetch();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const handleStatusChange = async (id: number, isActive: boolean) => {
|
|
|
|
|
- try {
|
|
|
|
|
- await silverUsersClient['knowledge-categories'][':id']['$put']({
|
|
|
|
|
- param: { id: id.toString() },
|
|
|
|
|
- json: { isActive: isActive ? 1 : 0 } as UpdateCategoryRequest
|
|
|
|
|
- });
|
|
|
|
|
- message.success('状态更新成功');
|
|
|
|
|
- fetchData(pagination.current, pagination.pageSize);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- message.error('状态更新失败');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const handleStatusChange = (id: number, isActive: boolean) => {
|
|
|
|
|
+ toggleStatusMutation.mutate({ id, isActive });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // 构建分类树结构
|
|
|
|
|
- const buildCategoryTree = (categories: KnowledgeCategory[], parentId: number | null = null): any[] => {
|
|
|
|
|
- return categories
|
|
|
|
|
- .filter(cat => cat.parentId === parentId)
|
|
|
|
|
- .map(cat => ({
|
|
|
|
|
- title: cat.name,
|
|
|
|
|
- value: cat.id,
|
|
|
|
|
- children: buildCategoryTree(categories, cat.id)
|
|
|
|
|
- }));
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ // 监听mutation成功,刷新数据
|
|
|
|
|
+ React.useEffect(() => {
|
|
|
|
|
+ if (
|
|
|
|
|
+ createMutation.isSuccess ||
|
|
|
|
|
+ updateMutation.isSuccess ||
|
|
|
|
|
+ deleteMutation.isSuccess ||
|
|
|
|
|
+ toggleStatusMutation.isSuccess
|
|
|
|
|
+ ) {
|
|
|
|
|
+ refetch();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [
|
|
|
|
|
+ createMutation.isSuccess,
|
|
|
|
|
+ updateMutation.isSuccess,
|
|
|
|
|
+ deleteMutation.isSuccess,
|
|
|
|
|
+ toggleStatusMutation.isSuccess,
|
|
|
|
|
+ refetch,
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
const columns: ColumnsType<KnowledgeCategory> = [
|
|
const columns: ColumnsType<KnowledgeCategory> = [
|
|
|
{
|
|
{
|
|
@@ -267,9 +241,9 @@ const KnowledgeCategories: React.FC = () => {
|
|
|
|
|
|
|
|
<Table
|
|
<Table
|
|
|
columns={columns}
|
|
columns={columns}
|
|
|
- dataSource={data}
|
|
|
|
|
|
|
+ dataSource={categoriesData?.data || []}
|
|
|
rowKey="id"
|
|
rowKey="id"
|
|
|
- loading={loading}
|
|
|
|
|
|
|
+ loading={isLoading}
|
|
|
pagination={{
|
|
pagination={{
|
|
|
...pagination,
|
|
...pagination,
|
|
|
showSizeChanger: true,
|
|
showSizeChanger: true,
|
|
@@ -306,11 +280,10 @@ const KnowledgeCategories: React.FC = () => {
|
|
|
label="父分类"
|
|
label="父分类"
|
|
|
name="parentId"
|
|
name="parentId"
|
|
|
>
|
|
>
|
|
|
- <TreeSelect
|
|
|
|
|
|
|
+ <KnowledgeCategoryTreeSelect
|
|
|
placeholder="请选择父分类(不选则为顶级分类)"
|
|
placeholder="请选择父分类(不选则为顶级分类)"
|
|
|
- treeData={buildCategoryTree(categories)}
|
|
|
|
|
allowClear
|
|
allowClear
|
|
|
- treeDefaultExpandAll
|
|
|
|
|
|
|
+ showRoot={false}
|
|
|
/>
|
|
/>
|
|
|
</Form.Item>
|
|
</Form.Item>
|
|
|
|
|
|