123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- import React, { forwardRef, useImperativeHandle, useState, useEffect } from "react";
- import { Button, Drawer, Form, Input, Select, Radio, Upload, message, TreeSelect } from "antd";
- import { CloseOutlined, UploadOutlined, CheckCircleOutlined, LoadingOutlined, CloseCircleOutlined } from "@ant-design/icons";
- import { uploadTypeList, AccessType } from "./prop";
- import styles from "../../css/Drawer.module.css";
- import { getOrganizations, uploadVoiceFile, getSkillList, uploadSensors } from "../../api/index";
- import { AxiosResponse } from "axios";
- import { validateFn, validateFileLength, validateFileSize, validateFileType } from "../../util/index"
- const { TextArea } = Input;
- const { Option } = Select;
- // 定义 ref 的类型
- export interface DrawerExampleRef {
- showDrawer: (title: string, type?: string | undefined, item?: any) => void;
- }
- interface UploadResponse {
- fileUUID: string;
- code: number;
- msg: string;
- }
- interface TreeNode {
- deptName: React.ReactNode; // 节点的显示文本
- deptID: string | number; // 节点的值
- childNodes?: TreeNode[]; // 子节点
- [key: string]: any; // 其他自定义属性
- }
- const DrawerExample: React.FC = forwardRef<DrawerExampleRef, {}>((props, ref) => {
- const { refreshFn } = props as any;
- const [open, setOpen] = useState(false);
- const [title, setTitle] = useState("");
- const [departmentData, setDepartmentData] = useState<any>([]);
- const [skillData, setSkillData] = useState([]);
- const [uploadState, setUploadState] = useState(false);
- const [loading, setLoading] = useState(false);
- const [initialValues, setInitialValues] = useState({});
- const [disable, setDisable] = useState(false);
- const [onlyDisable, setOnlyDisable] = useState(false);
- const [form] = Form.useForm();
- const [pageStatus, setPageStatus] = useState("");
- useImperativeHandle(ref, () => ({
- showDrawer: (title: string, type: string | undefined, item: any) => {
- setTitle(title);
- if (type === 'edit') {
- setPageStatus(type);
- setInitialValues({
- streamUrl: item.streamUrl,
- soundSensorName: item.name,
- soundSensorType: item.videoType,
- soundSensorUrl: item.srcUrl,
- soundFileUUid: item.fileUuid,
- deptUuid: item.deptUuid,
- department: '',
- description: item.description,
- skillUuid: item.skillUuid,
- responsiblePerson: item.responsiblePerson,
- ip: item.ip,
- soundSensorUuid: item.uuid
- })
- setDisable(true)
- setOnlyDisable(false)
- } else if (type === 'show') {
- setPageStatus(type);
- setInitialValues({
- streamUrl: item.streamUrl,
- soundSensorName: item.name,
- soundSensorType: item.videoType,
- soundSensorUrl: item.srcUrl,
- soundFileUUid: item.fileUuid,
- deptUuid: item.deptUuid,
- department: '',
- description: item.description,
- skillUuid: item.skillUuid,
- responsiblePerson: item.responsiblePerson,
- ip: item.ip,
- soundSensorUuid: item.uuid
- })
- setDisable(true)
- setOnlyDisable(true)
- } else {
- setInitialValues({
- streamUrl: '',
- soundSensorName: '',
- soundSensorType: AccessType.FileUpload,
- soundSensorUrl: '',
- soundFileUUid: '',
- deptUuid: departmentData.length ? departmentData[0].deptID : '',
- department: departmentData.length ? departmentData[0].deptName : '',
- description: '',
- skillUuid: '',
- responsiblePerson: departmentData.length ? departmentData[0].deptName : '',
- ip: '',
- soundSensorUuid: '',
- })
- setPageStatus('');
- setDisable(false)
- setOnlyDisable(false)
- }
- // 确保 initialValues 更新完成后再打开 Drawer
- setTimeout(() => {
- setOpen(true);
- }, 0);
- }
- }));
- const customRequest = (options: any) => {
- const { file, onSuccess, onError, onProgress } = options;
- setUploadState(true);
- if (!validateFn(file)) {
- message.error({ content: '文件格式不正确' })
- onError({ message: "文件格式不正确" });
- return;
- }
- if (file.size > Number(import.meta.env.VITE_APP_FILE_SIZE) * 1024 * 1024) {
- message.error({ content: '文件大小超过50MB限制' })
- onError({ message: "文件大小超过50MB限制" });
- return;
- }
- const formData = new FormData();
- // 添加文件到 FormData(字段名需与后端约定,通常为 "file")
- formData.append('file', file);
- const fn = (percent: number) => onProgress({ percent: percent });
- uploadVoiceFile(formData, fn).then((res: AxiosResponse<UploadResponse>) => {
- const { code, msg, fileUUID } = res as any;
- if (code !== 200) {
- onError({ message: msg });
- return;
- }
- form.setFieldValue("soundFileUUid", fileUUID)
- onSuccess({ message: "文件上传成功" })
- message.success({
- content: '文件上传成功',
- duration: 2,
- })
- }).catch(error => {
- onError({ message: "文件上传失败" });
- });
- };
- const onClose = () => {
- form.resetFields();
- setUploadState(false);
- setOpen(false);
- };
- const Footer = () => {
- return (
- <div style={{ textAlign: "right" }}>
- <Button style={{ borderRadius: 4 }} onClick={onClose}>取消</Button>
- <Button style={{ marginLeft: 8, borderRadius: 4 }} type={"primary"} loading={loading} onClick={() => { form.submit() }}>确定</Button>
- </div>
- );
- };
- const onFinish = (values: any) => {
- if (!values) {
- return;
- }
- if (pageStatus === 'show') {
- onClose();
- return;
- }
- if (!values.soundFileUUid) {
- message.error({ content: '请确认文件是否上传完成' })
- return;
- }
- setLoading(true);
- uploadSensors(values).then((res: any) => {
- if (res.code === 200) {
- message.success("操作成功");
- refreshFn();
- }
- }).finally(() => {
- onClose();
- setLoading(false);
- });
- };
- const onSelect = (_: String, node: TreeNode) => {
- form.setFieldValue("responsiblePerson", node.deptName);
- form.setFieldValue("department", node.deptName);
- };
- const iconRender = (file: any) => {
- switch (file.status) {
- case 'uploading':
- return <LoadingOutlined />;
- case 'done':
- return <CheckCircleOutlined style={{ color: 'green' }} />;
- case 'error':
- return <CloseCircleOutlined style={{ color: 'red' }} />;
- default:
- return <UploadOutlined />;
- }
- };
- const onRemove = () => {
- form.setFieldValue("files", []);
- setUploadState(false);
- }
- useEffect(() => {
- // 模拟数据获取
- getOrganizations().then((res) => {
- setDepartmentData(res.data ?? []);
- if (res.data.length > 0) {
- setInitialValues({
- soundSensorName: '',
- soundSensorUuid: '',
- skillUuid: '',
- soundSensorType: AccessType.FileUpload,
- soundFileUUid: '',
- deptUuid: res.data[0].deptID,
- department: res.data[0].deptName,
- responsiblePerson: res.data[0].deptName
- })
- }
- });
- getSkillList().then((res) => {
- setSkillData(res.data ?? []);
- });
- }, []);
- useEffect(() => {
- if (open) {
- form.setFieldsValue(initialValues); // 动态更新表单值
- form.setFields([{ name: 'skillUuid', disabled: onlyDisable }] as any); // 动态更新字段禁用状态
- }
- }, [initialValues, open, form, onlyDisable]);
- return (
- <>
- <Drawer
- title={title}
- onClose={onClose}
- open={open}
- width={600}
- maskClosable={false}
- keyboard={false}
- loading={false}
- closeIcon={null}
- footer={Footer()}
- extra={<CloseOutlined onClick={onClose} />}
- >
- <Form
- disabled={disable}
- initialValues={initialValues}
- labelCol={{ span: 6 }}
- wrapperCol={{ span: 16 }}
- form={form}
- name="control-hooks"
- onFinish={onFinish}
- style={{ maxWidth: 600, fontSize: 12 }}
- labelAlign="left"
- >
- <Form.Item name="soundSensorUuid" hidden>
- <Input />
- </Form.Item>
- <Form.Item name="soundFileUUid" hidden>
- <Input />
- </Form.Item>
- <Form.Item name="department" hidden>
- <Input />
- </Form.Item>
- <Form.Item name="soundSensorName" label=" 音频传感器名称"
- rules={[
- { required: true },
- { pattern: /^[0-9\u4e00-\u9fa5a-zA-Z-\/#\.]+$/, message: "仅支持数字、中文、大小写英文字母、特殊字符-/#." },
- ]}
- extra={<span style={{ fontSize: 12 }}>仅支持数字、中文、大小写英文字母、特殊字符-\/#.</span>}
- >
- <Input placeholder="请输入音频传感器名称" />
- </Form.Item>
- <Form.Item name="soundSensorType" label="音频接入" rules={[{ required: true }]}>
- <Radio.Group block options={uploadTypeList} />
- </Form.Item>
- <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.soundSensorType !== currentValues.soundSensorType}>
- {({ getFieldValue }) => {
- return (
- getFieldValue("soundSensorType") === AccessType.Audio ? (
- <Form.Item name="soundSensorUrl" label="传感器地址" rules={[{ required: true }]}>
- <Input placeholder="请输入传感器地址,如 http://127.0.0.1:8080/audio" />
- </Form.Item>
- ) : null)
- }}
- </Form.Item>
- <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.soundSensorType !== currentValues.soundSensorType}>
- {({ getFieldValue }) => {
- return getFieldValue("soundSensorType") === AccessType.FileUpload && getFieldValue("soundSensorUuid") === "" && (
- <Form.Item name="files" valuePropName="fileList"
- getValueFromEvent={(e) => {
- if (Array.isArray(e)) {
- return e;
- }
- return e && e.fileList;
- }}
- rules={[{ required: true },
- { validator: (_, value) => validateFileLength(value) },
- { validator: (_, value) => validateFileSize(value) },
- { validator: (_, value) => validateFileType(value) }]}
- label="音频上传"
- >
- <Upload customRequest={customRequest} maxCount={1} iconRender={iconRender} onRemove={onRemove}
- showUploadList={{
- extra: ({ size = 0 }) => (
- <span style={{ color: '#cccccc' }}>({(size / 1024 / 1024).toFixed(2)}MB)</span>
- ),
- showRemoveIcon: true,
- removeIcon: <CloseOutlined style={{ color: '#cccccc' }} />,
- }}>
- <Button disabled={uploadState} icon={<UploadOutlined />}>上传文件</Button>
- <p className={styles.tip}>文件大小仅支持 50M 以内,支持mp4格式,仅允许上传 1 个文件</p>
- </Upload>
- </Form.Item>
- )
- }}
- </Form.Item>
- <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.soundSensorUuid !== currentValues.soundSensorUuid}>
- {({ getFieldValue }) =>
- getFieldValue("soundSensorUuid") !== "" ? (
- <Form.Item name="streamUrl" label="上传音频">
- <Input />
- </Form.Item>) : null
- }
- </Form.Item>
- <Form.Item name="skillUuid" label="配置技能" rules={[{ required: true }]}>
- <Select placeholder="请选择技能" allowClear disabled={onlyDisable}>
- {skillData.map((e: any) => {
- return (
- <Option key={e.uuid} value={e.uuid}>{e.name}</Option>
- );
- })}
- </Select>
- </Form.Item>
- <Form.Item name="deptUuid" label="责任部门" rules={[{ required: true }]}>
- <TreeSelect
- showSearch
- style={{ width: '100%' }}
- dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
- placeholder="请选择责任部门"
- treeDefaultExpandAll
- treeData={departmentData}
- fieldNames={{ label: 'deptName', value: 'deptID', children: 'childNodes' }}
- onSelect={onSelect}
- />
- </Form.Item>
- <Form.Item name="responsiblePerson" label="责任人" rules={[{ required: false }]}>
- <Input placeholder="请填写责任人" allowClear />
- </Form.Item>
- <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.soundSensorType !== currentValues.soundSensorType}>
- {({ getFieldValue }) =>
- getFieldValue("soundSensorType") === AccessType.Audio ? (
- <Form.Item name="ip" label="音频传感器IP" rules={[{ required: false }]} >
- <Input placeholder="请输入音频传感器IP,如 127.0.0.1" />
- </Form.Item>
- ) : null
- }
- </Form.Item>
- <Form.Item name="description" label="备注信息" rules={[{ required: false }]}>
- <TextArea placeholder="请输入备注,建议长度200" maxLength={200} rows={4} showCount />
- </Form.Item>
- </Form>
- </Drawer>
- </>
- );
- });
- export default DrawerExample;
|