|
|
@@ -0,0 +1,306 @@
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import { useNavigate, useLocation } from 'react-router-dom';
|
|
|
+import { Search, Plus, RefreshCw, Edit2, Trash2, Copy, Eye, X } from 'lucide-react';
|
|
|
+import { Button, Input, Space, Table as AntdTable, Modal, message } from 'antd';
|
|
|
+import type { ColumnsType } from 'antd/es/table';
|
|
|
+import { toast } from 'sonner';
|
|
|
+import * as FormApi from '../api/bpm/form';
|
|
|
+import { DICT_TYPE, getDictLabel } from '../utils/dict';
|
|
|
+import { dateFormatter } from '../utils/formatTime';
|
|
|
+import { setConfAndFields2, FormCreateData } from '../utils/formCreate';
|
|
|
+
|
|
|
+export default function FormManagement() {
|
|
|
+ const navigate = useNavigate();
|
|
|
+ const location = useLocation();
|
|
|
+
|
|
|
+ // 调试信息
|
|
|
+ useEffect(() => {
|
|
|
+ console.log('FormManagement 组件已加载');
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const [loading, setLoading] = useState(true);
|
|
|
+ const [list, setList] = useState<FormApi.FormVO[]>([]);
|
|
|
+ const [total, setTotal] = useState(0);
|
|
|
+ const [queryParams, setQueryParams] = useState<FormApi.FormPageParams>({
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ name: undefined
|
|
|
+ });
|
|
|
+
|
|
|
+ // 详情弹窗
|
|
|
+ const [detailVisible, setDetailVisible] = useState(false);
|
|
|
+ const [detailData, setDetailData] = useState<FormCreateData>({
|
|
|
+ rule: [],
|
|
|
+ option: {}
|
|
|
+ });
|
|
|
+
|
|
|
+ /** 查询列表 */
|
|
|
+ const getList = async () => {
|
|
|
+ setLoading(true);
|
|
|
+ try {
|
|
|
+ console.log('FormManagement: 开始获取表单列表', queryParams);
|
|
|
+ const data = await FormApi.getFormPage(queryParams);
|
|
|
+ console.log('FormManagement: 获取到数据', data);
|
|
|
+ setList(data.list || []);
|
|
|
+ setTotal(data.total || 0);
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error('FormManagement: 获取表单列表失败', error);
|
|
|
+ toast.error(error.message || '获取表单列表失败');
|
|
|
+ // 确保即使失败也显示空列表
|
|
|
+ setList([]);
|
|
|
+ setTotal(0);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ getList();
|
|
|
+ // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
+ }, [queryParams.pageNo, queryParams.pageSize, queryParams.name]);
|
|
|
+
|
|
|
+ /** 搜索按钮操作 */
|
|
|
+ const handleQuery = () => {
|
|
|
+ setQueryParams({ ...queryParams, pageNo: 1 });
|
|
|
+ getList();
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 重置按钮操作 */
|
|
|
+ const resetQuery = () => {
|
|
|
+ setQueryParams({
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ name: undefined
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 添加/修改操作 */
|
|
|
+ const openForm = (type: string, id?: number) => {
|
|
|
+ const query: Record<string, string> = { type };
|
|
|
+ if (id !== undefined && (typeof id === 'number' || typeof id === 'string')) {
|
|
|
+ query.id = String(id);
|
|
|
+ }
|
|
|
+ // 跳转到表单编辑器(这里需要根据实际路由配置)
|
|
|
+ navigate(`/bpm/form/editor?${new URLSearchParams(query).toString()}`);
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 删除按钮操作 */
|
|
|
+ const handleDelete = async (id: number) => {
|
|
|
+ Modal.confirm({
|
|
|
+ title: '确认删除',
|
|
|
+ content: '确定要删除这条表单数据吗?',
|
|
|
+ okText: '确定',
|
|
|
+ cancelText: '取消',
|
|
|
+ onOk: async () => {
|
|
|
+ try {
|
|
|
+ await FormApi.deleteForm(id);
|
|
|
+ toast.success('删除成功');
|
|
|
+ await getList();
|
|
|
+ } catch (error: any) {
|
|
|
+ toast.error(error.message || '删除失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 详情操作 */
|
|
|
+ const openDetail = async (rowId: number) => {
|
|
|
+ try {
|
|
|
+ const data = await FormApi.getForm(rowId);
|
|
|
+ setConfAndFields2(setDetailData, data.conf, data.fields);
|
|
|
+ setDetailVisible(true);
|
|
|
+ } catch (error: any) {
|
|
|
+ toast.error(error.message || '获取表单详情失败');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 表格列配置
|
|
|
+ const columns: ColumnsType<FormApi.FormVO> = [
|
|
|
+ {
|
|
|
+ title: '编号',
|
|
|
+ dataIndex: 'id',
|
|
|
+ width: 80,
|
|
|
+ align: 'center',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '表单名',
|
|
|
+ dataIndex: 'name',
|
|
|
+ width: 200,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '状态',
|
|
|
+ dataIndex: 'status',
|
|
|
+ width: 100,
|
|
|
+ align: 'center',
|
|
|
+ render: (status: number) => {
|
|
|
+ const label = getDictLabel(DICT_TYPE.COMMON_STATUS, status);
|
|
|
+ const colorType = status === 0 ? 'success' : 'danger';
|
|
|
+ return (
|
|
|
+ <span
|
|
|
+ className={`inline-flex px-3 py-1 rounded-lg text-xs ${
|
|
|
+ colorType === 'success'
|
|
|
+ ? 'bg-green-100 text-green-700'
|
|
|
+ : 'bg-red-100 text-red-700'
|
|
|
+ }`}
|
|
|
+ >
|
|
|
+ {label}
|
|
|
+ </span>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '备注',
|
|
|
+ dataIndex: 'remark',
|
|
|
+ ellipsis: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '创建时间',
|
|
|
+ dataIndex: 'createTime',
|
|
|
+ width: 180,
|
|
|
+ align: 'center',
|
|
|
+ render: (text: string) => dateFormatter(text),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '操作',
|
|
|
+ width: 280,
|
|
|
+ align: 'center',
|
|
|
+ fixed: 'right',
|
|
|
+ render: (_: any, record: FormApi.FormVO) => (
|
|
|
+ <Space size="small">
|
|
|
+ <Button
|
|
|
+ type="link"
|
|
|
+ size="small"
|
|
|
+ icon={<Copy className="w-4 h-4" />}
|
|
|
+ onClick={() => openForm('copy', record.id)}
|
|
|
+ >
|
|
|
+ 复制
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="link"
|
|
|
+ size="small"
|
|
|
+ icon={<Edit2 className="w-4 h-4" />}
|
|
|
+ onClick={() => openForm('update', record.id)}
|
|
|
+ >
|
|
|
+ 编辑
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="link"
|
|
|
+ size="small"
|
|
|
+ icon={<Eye className="w-4 h-4" />}
|
|
|
+ onClick={() => openDetail(record.id!)}
|
|
|
+ >
|
|
|
+ 详情
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="link"
|
|
|
+ size="small"
|
|
|
+ danger
|
|
|
+ icon={<Trash2 className="w-4 h-4" />}
|
|
|
+ onClick={() => handleDelete(record.id!)}
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className="space-y-6">
|
|
|
+ {/* 搜索工作栏 */}
|
|
|
+ <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
|
|
|
+ <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap">
|
|
|
+ <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
|
|
|
+ <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
|
|
|
+ <label className="text-sm font-medium text-gray-700 whitespace-nowrap">表单名:</label>
|
|
|
+ <Input
|
|
|
+ value={queryParams.name || ''}
|
|
|
+ onChange={(e) => setQueryParams({ ...queryParams, name: e.target.value || undefined })}
|
|
|
+ onPressEnter={handleQuery}
|
|
|
+ placeholder="请输入表单名"
|
|
|
+ className="min-w-[200px] max-w-[300px]"
|
|
|
+ allowClear
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Space className="flex-shrink-0">
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ icon={<Search className="w-4 h-4" />}
|
|
|
+ onClick={handleQuery}
|
|
|
+ >
|
|
|
+ 搜索
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ icon={<RefreshCw className="w-4 h-4" />}
|
|
|
+ onClick={resetQuery}
|
|
|
+ >
|
|
|
+ 重置
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ icon={<Plus className="w-4 h-4" />}
|
|
|
+ onClick={() => openForm('create')}
|
|
|
+ >
|
|
|
+ 新增
|
|
|
+ </Button>
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 列表 */}
|
|
|
+ <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
|
|
|
+ <AntdTable
|
|
|
+ loading={loading}
|
|
|
+ columns={columns}
|
|
|
+ dataSource={list}
|
|
|
+ rowKey="id"
|
|
|
+ pagination={{
|
|
|
+ current: queryParams.pageNo,
|
|
|
+ pageSize: queryParams.pageSize,
|
|
|
+ total: total,
|
|
|
+ showSizeChanger: true,
|
|
|
+ showTotal: (total) => `共 ${total} 条记录`,
|
|
|
+ onChange: (page, pageSize) => {
|
|
|
+ setQueryParams({ ...queryParams, pageNo: page, pageSize: pageSize || 10 });
|
|
|
+ },
|
|
|
+ }}
|
|
|
+ scroll={{ x: 'max-content' }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 表单详情的弹窗 */}
|
|
|
+ <Modal
|
|
|
+ title="表单详情"
|
|
|
+ open={detailVisible}
|
|
|
+ onCancel={() => setDetailVisible(false)}
|
|
|
+ footer={[
|
|
|
+ <Button key="close" onClick={() => setDetailVisible(false)}>
|
|
|
+ 关闭
|
|
|
+ </Button>
|
|
|
+ ]}
|
|
|
+ width={800}
|
|
|
+ >
|
|
|
+ <div className="p-4">
|
|
|
+ {/* 这里可以集成 form-create 组件来展示表单 */}
|
|
|
+ <div className="space-y-4">
|
|
|
+ <div>
|
|
|
+ <label className="text-sm font-medium text-gray-700">表单配置:</label>
|
|
|
+ <pre className="mt-2 p-4 bg-gray-50 rounded-lg text-xs overflow-auto max-h-96">
|
|
|
+ {JSON.stringify(detailData.option, null, 2)}
|
|
|
+ </pre>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <label className="text-sm font-medium text-gray-700">表单字段:</label>
|
|
|
+ <pre className="mt-2 p-4 bg-gray-50 rounded-lg text-xs overflow-auto max-h-96">
|
|
|
+ {JSON.stringify(detailData.rule, null, 2)}
|
|
|
+ </pre>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|