| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- import React, { useState, useEffect } from 'react';
- import {
- Button, Table, Form, Input, Select, message, Card, Badge,
- Space, Modal, DatePicker, InputNumber, Switch, Tag, Checkbox, Divider, Progress
- } from 'antd';
- import { DeviceTypeAPI } from './api/device_type.ts';
- import { UserAPI } from './api/users.ts';
- const { RangePicker } = DatePicker;
- const { TextArea } = Input;
- import dayjs from 'dayjs';
- import 'dayjs/locale/zh-cn';
- import { InspectionsAPI } from './api/inspections.ts';
- import type {
- InspectionTask,
- InspectionTemplate,
- ListResponse
- } from './api/inspections.ts';
- import { getEnumOptions } from './utils.ts';
- const PriorityOptions = [
- { label: '低', value: 'low' },
- { label: '中', value: 'medium' },
- { label: '高', value: 'high' },
- { label: '紧急', value: 'urgent' }
- ];
- const StatusOptions = [
- { label: '待处理', value: 'pending' },
- { label: '进行中', value: 'in_progress' },
- { label: '已完成', value: 'completed' },
- { label: '已失败', value: 'failed' }
- ];
- const getStatusBadge = (status: InspectionTask['status']) => {
- switch (status) {
- case 'pending':
- return <Badge status="default" text="待处理" />;
- case 'in_progress':
- return <Badge status="processing" text="进行中" />;
- case 'completed':
- return <Badge status="success" text="已完成" />;
- case 'failed':
- return <Badge status="error" text="已失败" />;
- default:
- return <Badge status="default" text="未知" />;
- }
- };
- const getPriorityTag = (priority: string) => {
- switch (priority) {
- case 'low':
- return <Tag color="blue">低</Tag>;
- case 'medium':
- return <Tag color="orange">中</Tag>;
- case 'high':
- return <Tag color="red">高</Tag>;
- case 'urgent':
- return <Tag color="magenta">紧急</Tag>;
- default:
- return <Tag>未知</Tag>;
- }
- };
- export const InspectionsPage = () => {
- const [loading, setLoading] = useState(false);
- const [data, setData] = useState<InspectionTask[]>([]);
- const [deviceTypes, setDeviceTypes] = useState<{label: string, value: string}[]>([]);
- const [autoInspectionLoading, setAutoInspectionLoading] = useState(false);
- const [manualInspectionVisible, setManualInspectionVisible] = useState(false);
- const [progress, setProgress] = useState(0);
- const [autoForm] = Form.useForm();
- const [manualForm] = Form.useForm();
- const [pagination, setPagination] = useState({
- current: 1,
- pageSize: 10,
- total: 0,
- });
- const [formRef] = Form.useForm();
- const [modalVisible, setModalVisible] = useState(false);
- const [editingId, setEditingId] = useState<number | null>(null);
- useEffect(() => {
- fetchData();
- fetchDeviceTypes();
- }, [pagination.current, pagination.pageSize]);
- const fetchDeviceTypes = async () => {
- try {
- const response = await DeviceTypeAPI.getDeviceTypes({pageSize: 100});
- setDeviceTypes(response.data.map((item: any) => ({
- label: item.name,
- value: item.code
- })));
- } catch (error) {
- console.error('获取设备类型失败:', error);
- message.error('获取设备类型失败');
- }
- };
- const fetchData = async () => {
- setLoading(true);
- try {
- const values = formRef.getFieldsValue();
- const { title, priority, status, timeRange } = formRef.getFieldsValue();
- const params: Record<string, any> = {
- page: pagination.current,
- limit: pagination.pageSize,
- status,
- templateId: priority ? parseInt(priority) : undefined
- };
- if (timeRange) {
- params.startTime = timeRange[0].format('YYYY-MM-DD HH:mm:ss');
- params.endTime = timeRange[1].format('YYYY-MM-DD HH:mm:ss');
- }
- const response = await InspectionsAPI.getTasks(params);
-
- setData(response.data);
- setPagination({
- ...pagination,
- total: response.total,
- });
- } catch (error) {
- console.error('获取巡检任务失败:', error);
- message.error('获取巡检任务失败');
- } finally {
- setLoading(false);
- }
- };
- const handleSearch = (values: any) => {
- setPagination({
- ...pagination,
- current: 1,
- });
- fetchData();
- };
- const handleTableChange = (newPagination: any) => {
- setPagination({
- ...pagination,
- current: newPagination.current,
- pageSize: newPagination.pageSize,
- });
- };
- const handleEditAutoTask = (record: InspectionTask) => {
- setEditingId(record.id);
- formRef.setFieldsValue({
- ...record,
- cronExpression: record.cronExpression || `0 0 */${record.intervalDays} * *`
- });
- setModalVisible(true);
- };
- const handleDeleteAutoTask = async (id: number) => {
- Modal.confirm({
- title: '确认删除自动巡检任务?',
- content: '此操作不可撤销',
- onOk: async () => {
- try {
- await InspectionsAPI.deleteTemplate(id);
- message.success('删除成功');
- fetchData();
- } catch (error) {
- console.error('删除失败:', error);
- message.error('删除失败');
- }
- },
- });
- };
- const handleStatusChange = async (id: number, status: InspectionTask['status']) => {
- try {
- await InspectionsAPI.updateTemplate(id, { status } as Partial<InspectionTemplate>);
- message.success('状态更新成功');
- fetchData();
- } catch (error) {
- console.error('状态更新失败:', error);
- message.error('状态更新失败');
- }
- };
- const handleAutoInspection = async () => {
- try {
- const values = await formRef.validateFields();
- const { intervalDays, deviceTypes } = values;
-
- // 自动生成任务编号
- const taskNo = `INSP-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
- const data = {
- taskNo,
- intervalDays: intervalDays || 7, // 默认7天
- deviceTypes,
- reportReceivers: ['admin'] // 默认通知admin
- };
- if (editingId) {
- // 更新模板使用模板相关参数
- await InspectionsAPI.updateTemplate(editingId, {
- name: values.name,
- description: values.description
- });
- message.success('巡检模板更新成功');
- } else {
- // 创建任务使用任务相关参数
- await InspectionsAPI.createAutoInspectionTask(data);
- message.success('自动巡检任务创建成功');
- }
-
- setModalVisible(false);
- fetchData();
- } catch (error) {
- console.error('操作失败:', error);
- message.error('操作失败: ' + (error as Error).message);
- }
- };
- const columns = [
- {
- title: '任务标题',
- dataIndex: 'title',
- key: 'title',
- },
- {
- title: '优先级',
- dataIndex: 'priority',
- key: 'priority',
- render: (priority: string) => getPriorityTag(priority),
- },
- {
- title: '状态',
- dataIndex: 'status',
- key: 'status',
- render: (status: InspectionTask['status']) => getStatusBadge(status),
- },
- {
- title: '计划开始时间',
- dataIndex: 'startTime',
- key: 'startTime',
- render: (text?: string) => text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-',
- },
- {
- title: '计划结束时间',
- dataIndex: 'endTime',
- key: 'endTime',
- render: (text?: string) => text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-',
- },
- {
- title: '操作',
- key: 'action',
- render: (_: any, record: InspectionTask) => (
- <Space size="middle">
- {record.schedule_type === 'scheduled' && (
- <>
- <Button type="link" onClick={() => handleEditAutoTask(record)}>编辑</Button>
- <Button type="link" danger onClick={() => handleDeleteAutoTask(record.id)}>删除</Button>
- </>
- )}
- <Select
- defaultValue={record.status}
- style={{ width: 120 }}
- onChange={(value) => handleStatusChange(record.id, value)}
- options={StatusOptions}
- />
- </Space>
- ),
- },
- ];
- const handleManualInspection = async () => {
- try {
- const values = await manualForm.validateFields();
- setProgress(0);
-
- // 模拟巡检进度
- const interval = setInterval(() => {
- setProgress(prev => {
- const newProgress = prev + 10;
- if (newProgress >= 100) {
- clearInterval(interval);
- message.success('手动巡检完成');
- setManualInspectionVisible(false);
- // 实际调用API执行巡检
- InspectionsAPI.runManualTask({
- deviceTypes: values.deviceTypes
- });
- }
- return newProgress;
- });
- }, 500);
- } catch (error) {
- console.error('手动巡检失败:', error);
- message.error('手动巡检失败');
- }
- };
- return (
- <div className="inspections-page">
- <Card title="巡检任务配置" style={{ marginBottom: 16 }}>
- <Space>
- <Button
- type="primary"
- onClick={() => setManualInspectionVisible(true)}
- >
- 手动巡检
- </Button>
- <Button
- type="primary"
- onClick={() => {
- setEditingId(null);
- setModalVisible(true);
- formRef.setFieldsValue({
- name: `自动巡检-${new Date().toLocaleDateString()}`,
- intervalDays: 7,
- notifyEmails: ['admin'],
- cronExpression: '0 0 * * *'
- });
- }}
- >
- 自动巡检配置
- </Button>
- </Space>
- </Card>
- <Card title="巡检任务管理" style={{ marginBottom: 16 }}>
- <Form
- form={formRef}
- layout="inline"
- onFinish={handleSearch}
- style={{ marginBottom: 16 }}
- >
- <Form.Item name="title" label="任务标题">
- <Input placeholder="输入任务标题" style={{ width: 200 }} />
- </Form.Item>
- <Form.Item name="priority" label="优先级">
- <Select
- placeholder="选择优先级"
- style={{ width: 120 }}
- allowClear
- options={PriorityOptions}
- />
- </Form.Item>
- <Form.Item name="status" label="状态">
- <Select
- placeholder="选择状态"
- style={{ width: 120 }}
- allowClear
- options={StatusOptions}
- />
- </Form.Item>
- <Form.Item name="timeRange" label="计划时间">
- <RangePicker showTime format="YYYY-MM-DD HH:mm" />
- </Form.Item>
- <Form.Item>
- <Button type="primary" htmlType="submit">
- 查询
- </Button>
- </Form.Item>
- </Form>
-
- <Table
- columns={columns}
- dataSource={data}
- rowKey="id"
- pagination={pagination}
- loading={loading}
- onChange={handleTableChange}
- />
- </Card>
- <Modal
- title={editingId ? '编辑自动巡检任务' : '新建自动巡检任务'}
- visible={modalVisible}
- onOk={handleAutoInspection}
- onCancel={() => setModalVisible(false)}
- width={800}
- style={{ textAlign: 'center' }}
- >
- <Form form={formRef} layout="vertical">
- <Form.Item
- name="name"
- label="任务名称"
- rules={[{ required: true, message: '请输入任务名称' }]}
- >
- <Input placeholder="例如: 每周自动巡检" />
- </Form.Item>
- <Form.Item
- name="taskNo"
- label="任务编号"
- initialValue={`INSP-${Date.now()}`}
- >
- <Input disabled placeholder="自动生成" />
- </Form.Item>
- <Form.Item
- name="intervalDays"
- label="间隔天数"
- tooltip="如果未设置cron表达式,则使用间隔天数"
- >
- <InputNumber
- min={1}
- placeholder="例如: 7 (每周执行)"
- style={{ textAlign: 'left' }}
- />
- </Form.Item>
- <Form.Item
- name="deviceTypes"
- label="设备类型"
- tooltip="不选择则巡检所有设备"
- >
- <Select
- mode="multiple"
- style={{ width: '100%' }}
- placeholder="选择设备类型"
- options={deviceTypes}
- />
- </Form.Item>
- <Form.Item
- name="notifyEmails"
- label="通知"
- tooltip="巡检完成后发送通知"
- >
- <Select
- mode="tags"
- style={{ width: '100%' }}
- placeholder="输入邮箱后按回车添加"
- tokenSeparators={[',', ' ']}
- />
- </Form.Item>
- </Form>
- </Modal>
- {/* 手动巡检弹窗 */}
- <Modal
- title={<div style={{ textAlign: 'center', fontSize: '18px', fontWeight: 'bold' }}>手动巡检</div>}
- visible={manualInspectionVisible}
- onOk={handleManualInspection}
- onCancel={() => setManualInspectionVisible(false)}
- width={600}
- centered
- >
- <Form form={manualForm} layout="vertical">
- <Form.Item label="巡检时间">
- {dayjs().format('YYYY-MM-DD HH:mm:ss')}
- </Form.Item>
- <Form.Item
- name="taskNo"
- label="巡检任务编号"
- initialValue={`INSP-${Date.now()}`}
- >
- <Input disabled />
- </Form.Item>
- <Form.Item
- name="deviceTypes"
- label="巡检设备类型"
- >
- <Select
- mode="multiple"
- style={{ width: '100%' }}
- placeholder="选择设备类型(不选则为全部设备)"
- options={deviceTypes}
- />
- </Form.Item>
- <Form.Item label="巡检进度">
- <Progress percent={progress} status={progress < 100 ? 'active' : 'success'} />
- </Form.Item>
- </Form>
- </Modal>
- </div>
- );
- };
|