Browse Source

fix(core): fix null reference issue in user service

wjx 1 year ago
parent
commit
33f1cc632c

+ 3 - 0
.eslintrc.js

@@ -4,4 +4,7 @@ module.exports = {
     page: true,
     REACT_APP_ENV: true,
   },
+  rules: {
+    '@typescript-eslint/no-unused-expressions': 'off',
+  },
 };

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+* -text

+ 59 - 43
src/components/Material/index.tsx

@@ -1,43 +1,59 @@
-import React, { useState } from "react"
-import { Button, Flex, Image, Space, Typography } from 'antd'
-import { RatioEnum } from "@/const"
-
-interface Props {
-    claimJson: string,
-    items?: string[],
-    resourceUrl?: string
-}
-const Material: React.FC<Props> = ({ items = [], resourceUrl, claimJson }) => {
-
-    /******************************/
-    const [visible, setVisible] = useState<boolean>(false)
-    const { size, extent } = JSON.parse(claimJson)
-    /******************************/
-
-    return <>
-        <Flex vertical={true}>
-            <Typography.Title level={5}>素材制作要求</Typography.Title>
-            <Typography.Text>素材尺寸:{size?.[0]} * {size?.[1]}</Typography.Text>
-            <Typography.Text>素材大小:{extent?.[0]}MB ~ {extent?.[1]}MB</Typography.Text>
-        </Flex>
-
-        <Space>
-            {items.length > 0 && <>
-                <Image.PreviewGroup
-                    items={items}
-                    preview={{
-                        visible: visible,
-                        onVisibleChange: (value) => {
-                            setVisible(value);
-                        },
-                    }}
-                />
-                <Button type="link" style={{ padding: 0 }} onClick={() => setVisible(true)}>素材示例</Button>
-            </>}
-            {resourceUrl && <Button type="link" style={{ paddingLeft: 0, paddingRight: 0 }} href={resourceUrl} download="资源包下载">资源包下载</Button>}
-        </Space>
-
-    </>
-}
-
-export default React.memo(Material)
+import { Button, Flex, Image, Space, Typography } from 'antd';
+import React, { useState } from 'react';
+
+interface Props {
+  claimJson: string;
+  items?: string[];
+  resourceUrl?: string;
+}
+const Material: React.FC<Props> = ({ items = [], resourceUrl, claimJson }) => {
+  /******************************/
+  const [visible, setVisible] = useState<boolean>(false);
+  const { size, extent } = JSON.parse(claimJson);
+  /******************************/
+
+  return (
+    <>
+      <Flex vertical={true}>
+        <Typography.Title level={5}>素材制作要求</Typography.Title>
+        <Typography.Text>
+          素材尺寸:{size?.[0]} * {size?.[1]}
+        </Typography.Text>
+        <Typography.Text>
+          素材大小:{extent?.[0]}MB ~ {extent?.[1]}MB
+        </Typography.Text>
+      </Flex>
+
+      <Space>
+        {items.length > 0 && (
+          <>
+            <Image.PreviewGroup
+              items={items}
+              preview={{
+                visible: visible,
+                onVisibleChange: (value) => {
+                  setVisible(value);
+                },
+              }}
+            />
+            <Button type="link" style={{ padding: 0 }} onClick={() => setVisible(true)}>
+              素材示例
+            </Button>
+          </>
+        )}
+        {resourceUrl && (
+          <Button
+            type="link"
+            style={{ paddingLeft: 0, paddingRight: 0 }}
+            href={resourceUrl}
+            download="资源包下载"
+          >
+            资源包下载
+          </Button>
+        )}
+      </Space>
+    </>
+  );
+};
+
+export default React.memo(Material);

+ 0 - 81
src/const.tsx

@@ -1,81 +0,0 @@
-import { Badge, Tag } from "antd"
-import React from "react"
-
-/**
- * 任务分类枚举
- */
-export enum TaskTypeEnum {
-    TASK_TYPE_GAME = '游戏',
-    TASK_TYPE_NOVEL = '小说',
-    TASK_TYPE_SHORT_PLAY = '短剧'
-}
-
-export const TaskTypeEle: any = {
-    TASK_TYPE_GAME: <Tag color="success">游戏</Tag>,
-    TASK_TYPE_NOVEL: <Tag color="processing">小说</Tag>,
-    TASK_TYPE_SHORT_PLAY: <Tag color="error">短剧</Tag>
-}
-
-/**
- * 紧急度
- */
-export const UrgencyEnum = {
-    '0': '普通',
-    '1': '置顶'
-}
-
-
-/**
- * 任务状态
- */
-export enum StatusEnum {
-    STATUS_NORMAL = '正常',
-    STATUS_EXPIRE = '失效'
-}
-export const TaskStatusEle: any = {
-    STATUS_NORMAL: <Badge status="success" text="正常" />,
-    STATUS_EXPIRE: <Badge status="error" text="失效" />
-}
-
-/**
- * 素材类型枚举
- */
-export enum MaterialTypeEnum {
-    // MATERIAL_TYPE_SINGLE_PICTURE = '单图',
-    // MATERIAL_TYPE_GROUP_PICTURE = '组图',
-    // MATERIAL_TYPE_VOICE = '音频',
-    MATERIAL_TYPE_VIDEO = '视频',
-}
-
-export const MaterialTypeEle: any = {
-    MATERIAL_TYPE_SINGLE_PICTURE: <Tag color="#55acee">单图</Tag>,
-    MATERIAL_TYPE_GROUP_PICTURE: <Tag color="#3b5999">组图</Tag>,
-    MATERIAL_TYPE_VOICE: <Tag color="#cd201f">音频</Tag>,
-    MATERIAL_TYPE_VIDEO: <Tag color="#55acee">视频</Tag>,
-}
-
-
-/**
- * 素材来源要求枚举
- */
-export enum MaterialSourceEnum {
-    MATERIAL_SOURCE_ORIGINAL = '原创'
-}
-
-/**
- * 素材比例枚举
- */
-export enum RatioEnum {
-    ONE_ONE = '1:1',
-    FOUR_THREE = '4:3',
-    SIXTEEN_NINE = '16:9',
-    NINE_SIXTEEN = '9:16',
-}
-
-/**
- * 结算类型
- */
-export enum CheckoutTypeEnum {
-    CHECKOUT_TYPE_SCALE = '消耗比例',
-    CHECKOUT_TYPE_AMOUNT = '任务'
-}

+ 372 - 206
src/pages/MyTask/index.tsx

@@ -1,206 +1,372 @@
-import { SearchOutlined } from "@ant-design/icons"
-import { PageContainer } from "@ant-design/pro-components"
-import { useRequest, useSize } from "ahooks";
-import { Affix, Avatar, Image, Button, Card, Col, Flex, Form, Input, List, Row, Space, Tag, Typography, theme, DatePicker, Select, Tooltip } from "antd"
-import { useEffect, useRef, useState } from "react";
-import TaskModal from "./taskModal";
-import { getTaskListApi } from "@/services/task-api/myTask";
-import { MaterialSourceEnum, MaterialTypeEle, MaterialTypeEnum, StatusEnum, TaskStatusEle, TaskTypeEle, TaskTypeEnum, UrgencyEnum } from "@/const";
-import Material from "@/components/Material";
-import moment from "moment";
-
-/**
- * 我的任务
- * @returns 
- */
-const MyTask: React.FC = () => {
-
-
-    /***********************************/
-    const { useToken } = theme;
-    const ref = useRef(null);
-    const size = useSize(ref)
-    const { token } = useToken();
-    const [form] = Form.useForm()
-    const [initialValues, setInitialState] = useState<any>({})
-    const [visible, setVisible] = useState<boolean>(false)
-    const [queryForm, setQueryForm] = useState<TASKAPI.TaskList>({ pageNum: 1, pageSize: 10 })
-    const getTaskList = useRequest(getTaskListApi, { manual: true })//添加请求
-    /***********************************/
-
-    useEffect(() => {
-        getTaskList.run(queryForm)
-    }, [queryForm])
-
-    const onFinish = (data: any) => {
-        const { time, ...pre } = data
-        let newQueryForm = JSON.parse(JSON.stringify(queryForm))
-        newQueryForm.pageNum = 1
-        if (time) {
-            newQueryForm.startTime = moment(time[0]).format('YYYY-MM-DD')
-            newQueryForm.endTime = moment(time[1]).format('YYYY-MM-DD')
-        } else {
-            delete newQueryForm?.startTime
-            delete newQueryForm?.endTime
-        }
-        setQueryForm({ ...newQueryForm, ...pre })
-    }
-
-    const editHandle = (data: any) => {
-        console.log(data)
-        let newData: any ={}
-        const { startTime, endTime, materialClaimJson, materialExample, checkoutType, checkout, urgency, ...pre } = data
-        if (checkoutType === 'CHECKOUT_TYPE_SCALE') {
-            newData['checkout'] = checkout * 100
-        } else {
-            newData['checkout'] = checkout
-        }
-        newData['endTime'] = endTime ? moment(endTime) : null
-        newData['materialExamples'] = materialExample ? materialExample.split(',') : []
-        setInitialState({ ...newData, ...pre, ...JSON.parse(materialClaimJson), checkoutType, startTime: moment(startTime), urgency: urgency + '' })
-        setVisible(true)
-    }
-
-    return <PageContainer>
-        <Card
-            style={{
-                borderRadius: 8,
-            }}
-            bodyStyle={{
-                paddingTop: 0,
-                paddingLeft: 0,
-                paddingRight: 0
-            }}
-            ref={ref}
-        >
-            <Space direction="vertical" style={{ width: '100%' }} size={0}>
-                <Affix offsetTop={56}>
-                    <div style={{ backgroundColor: token.colorBgBase, padding: token.paddingContentHorizontalLG, borderRadius: 8, borderBottom: `1px solid ${token.colorBorderSecondary}` }}>
-                        <Form layout="inline" className='queryForm' name="basic" form={form} onFinish={onFinish}>
-                            <Row gutter={[0, 6]}>
-                                <Col>
-                                    <Form.Item name='name'>
-                                        <Input prefix={<SearchOutlined />} placeholder="任务名称" />
-                                    </Form.Item>
-                                </Col>
-
-                                <Col><Form.Item name='time'>
-                                    <DatePicker.RangePicker style={{ width: 250 }} placeholder={['发布开始日期', '发布开始日期']} />
-                                </Form.Item></Col>
-
-                                <Col><Form.Item name='taskType'>
-                                    <Select
-                                        placeholder="任务分类"
-                                        style={{ width: 100 }}
-                                        allowClear
-                                        options={Object.keys(TaskTypeEnum).map(key => ({ label: (TaskTypeEnum as any)[key], value: key }))}
-                                    />
-                                </Form.Item></Col>
-
-                                <Col><Form.Item name='status'>
-                                    <Select
-                                        placeholder="任务状态"
-                                        style={{ width: 100 }}
-                                        allowClear
-                                        options={Object.keys(StatusEnum).map(key => ({ label: (StatusEnum as any)[key], value: key }))}
-                                    />
-                                </Form.Item></Col>
-
-                                <Col><Form.Item name='urgency'>
-                                    <Select
-                                        placeholder="任务紧急度"
-                                        style={{ width: 120 }}
-                                        allowClear
-                                        options={Object.keys(UrgencyEnum).map(key => ({ label: (UrgencyEnum as any)[key], value: key }))}
-                                    />
-                                </Form.Item></Col>
-
-                                <Col><Form.Item name='materialType'>
-                                    <Select
-                                        placeholder="素材类型"
-                                        style={{ width: 100 }}
-                                        allowClear
-                                        options={Object.keys(MaterialTypeEnum).map(key => ({ label: (MaterialTypeEnum as any)[key], value: key }))}
-                                    />
-                                </Form.Item></Col>
-
-                                <Col><Form.Item name='materialSource'>
-                                    <Select
-                                        placeholder="素材来源"
-                                        style={{ width: 100 }}
-                                        allowClear
-                                        options={Object.keys(MaterialSourceEnum).map(key => ({ label: (MaterialSourceEnum as any)[key], value: key }))}
-                                    />
-                                </Form.Item></Col>
-
-                                <Col>
-                                    <Space>
-                                        <Button type="primary" htmlType="submit">搜索</Button>
-                                        <Button onClick={() => form.resetFields()}>重置</Button>
-                                        <Button type="primary" onClick={() => { setInitialState({}); setVisible(true) }}>发布素材任务</Button>
-                                    </Space>
-                                </Col>
-                            </Row>
-                        </Form>
-
-                    </div>
-                </Affix>
-
-                <List
-                    itemLayout="vertical"
-                    size="large"
-                    dataSource={getTaskList.data?.data?.records}
-                    loading={getTaskList.loading}
-                    pagination={{
-                        onChange: (page) => {
-                            console.log(page);
-                            setQueryForm({ ...queryForm, pageNum: page })
-                        },
-                        current: queryForm.pageNum,
-                        pageSize: queryForm.pageSize,
-                    }}
-                    renderItem={(item: any, index) => (
-                        <List.Item
-                            key={item.id}
-                            extra={<Flex gap={20} style={{ height: '100%' }}>
-                                <Flex style={{ width: size?.width ? `${size?.width / 2 - 265}px` : 300 }} vertical={true} align="flex-start" justify="space-between">
-                                    <Material items={item?.materialExample?.split(',')} resourceUrl={item.materialResource} claimJson={item.materialClaimJson} />
-                                </Flex>
-                                <Flex style={{ width: 165, height: '100%' }} gap={10} vertical={true} align="center" justify="center">
-                                    {TaskStatusEle[item.status]}
-                                    <Tag color="error">结算:{item.checkoutType === 'CHECKOUT_TYPE_SCALE' ? `总消耗 ${item.checkout * 100}%` : `${item.checkout}元(审核合格)`}</Tag>
-                                    <Button type="primary" onClick={() => editHandle(item)}>修改任务</Button>
-                                </Flex>
-                            </Flex>}
-                            actions={[
-                                <Tooltip title='发布时间'><span>{item.createTime}</span></Tooltip>,
-                                <Tooltip title='任务分类'>{TaskTypeEle[item.taskType]}</Tooltip>,
-                                <Tooltip title='素材类型'>{MaterialTypeEle[item.materialType]}</Tooltip>,
-                                <Tooltip title='素材来源要求'><Tag>{(MaterialSourceEnum as any)[item.materialSource]}</Tag></Tooltip>
-                            ]}
-                        >
-                            <List.Item.Meta
-                                avatar={<Avatar src={item.avatar || `https://xsgames.co/randomusers/avatar.php?g=pixel&key=${index}`} style={{ backgroundColor: '#87d068' }} />}
-                                title={<Space size={20}>
-                                    <Typography.Title level={5} style={{ marginBottom: 0 }}>{item.name}</Typography.Title>
-                                    {!!item.urgency && <Space size={4}><span style={{ color: 'red' }}>置顶</span>🆙</Space>}
-                                </Space>}
-                                description={<Space direction="vertical" style={{ width: '100%' }} size={0}>
-                                    {/* 有效时间 */}
-                                    <Typography.Text type="secondary" ellipsis>任务有效时间:{item.startTime}~{item.endTime ? item.endTime : '长期有效'}</Typography.Text>
-                                    {/* 任务描述 */}
-                                    {item.remark && <Typography.Text type="secondary" ellipsis={{ tooltip: true }}>{item.remark}</Typography.Text>}
-                                </Space>}
-                            />
-                            <Typography.Paragraph ellipsis={{ rows: 3, tooltip: true }} style={{ marginBottom: 0 }}>{item.remarkMore}</Typography.Paragraph>
-                        </List.Item>
-                    )}
-                />
-            </Space>
-
-            {visible && <TaskModal visible={visible} onClose={() => setVisible(false)} onChange={() => { setVisible(false); getTaskList.refresh() }} initialValues={initialValues} />}
-        </Card>
-    </PageContainer>
-}
-
-export default MyTask
+import Material from '@/components/Material';
+import { getTaskListApi } from '@/services/task-api/myTask';
+import {
+  MaterialSourceEnum,
+  MaterialTypeEle,
+  MaterialTypeEnum,
+  StatusEnum,
+  TaskStatusEle,
+  TaskTypeEle,
+  TaskTypeEnum,
+  UrgencyEnum,
+} from '@/utils/constEnum';
+import { SearchOutlined } from '@ant-design/icons';
+import { PageContainer } from '@ant-design/pro-components';
+import { useRequest, useSize } from 'ahooks';
+import {
+  Affix,
+  Avatar,
+  Button,
+  Card,
+  Col,
+  DatePicker,
+  Flex,
+  Form,
+  Input,
+  List,
+  Row,
+  Select,
+  Space,
+  Tag,
+  theme,
+  Tooltip,
+  Typography,
+} from 'antd';
+import moment from 'moment';
+import { useEffect, useRef, useState } from 'react';
+import TaskModal from './taskModal';
+
+/**
+ * 我的任务
+ * @returns
+ */
+const MyTask: React.FC = () => {
+  /***********************************/
+  const { useToken } = theme;
+  const ref = useRef(null);
+  const size = useSize(ref);
+  const { token } = useToken();
+  const [form] = Form.useForm();
+  const [initialValues, setInitialState] = useState<any>({});
+  const [visible, setVisible] = useState<boolean>(false);
+  const [queryForm, setQueryForm] = useState<TASKAPI.TaskList>({ pageNum: 1, pageSize: 10 });
+  const getTaskList = useRequest(getTaskListApi, { manual: true }); //添加请求
+  /***********************************/
+
+  useEffect(() => {
+    getTaskList.run(queryForm);
+  }, [queryForm]);
+
+  const onFinish = (data: any) => {
+    const { time, ...pre } = data;
+    let newQueryForm = JSON.parse(JSON.stringify(queryForm));
+    newQueryForm.pageNum = 1;
+    if (time) {
+      newQueryForm.startTime = moment(time[0]).format('YYYY-MM-DD');
+      newQueryForm.endTime = moment(time[1]).format('YYYY-MM-DD');
+    } else {
+      delete newQueryForm?.startTime;
+      delete newQueryForm?.endTime;
+    }
+    setQueryForm({ ...newQueryForm, ...pre });
+  };
+
+  const editHandle = (data: any) => {
+    console.log(data);
+    let newData: any = {};
+    const {
+      startTime,
+      endTime,
+      materialClaimJson,
+      materialExample,
+      checkoutType,
+      checkout,
+      urgency,
+      ...pre
+    } = data;
+    if (checkoutType === 'CHECKOUT_TYPE_SCALE') {
+      newData['checkout'] = checkout * 100;
+    } else {
+      newData['checkout'] = checkout;
+    }
+    newData['endTime'] = endTime ? moment(endTime) : null;
+    newData['materialExamples'] = materialExample ? materialExample.split(',') : [];
+    setInitialState({
+      ...newData,
+      ...pre,
+      ...JSON.parse(materialClaimJson),
+      checkoutType,
+      startTime: moment(startTime),
+      urgency: urgency + '',
+    });
+    setVisible(true);
+  };
+
+  return (
+    <PageContainer>
+      <Card
+        style={{
+          borderRadius: 8,
+        }}
+        bodyStyle={{
+          paddingTop: 0,
+          paddingLeft: 0,
+          paddingRight: 0,
+        }}
+        ref={ref}
+      >
+        <Space direction="vertical" style={{ width: '100%' }} size={0}>
+          <Affix offsetTop={56}>
+            <div
+              style={{
+                backgroundColor: token.colorBgBase,
+                padding: token.paddingContentHorizontalLG,
+                borderRadius: 8,
+                borderBottom: `1px solid ${token.colorBorderSecondary}`,
+              }}
+            >
+              <Form
+                layout="inline"
+                className="queryForm"
+                name="basic"
+                form={form}
+                onFinish={onFinish}
+              >
+                <Row gutter={[0, 6]}>
+                  <Col>
+                    <Form.Item name="name">
+                      <Input prefix={<SearchOutlined />} placeholder="任务名称" />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="time">
+                      <DatePicker.RangePicker
+                        style={{ width: 250 }}
+                        placeholder={['发布开始日期', '发布开始日期']}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="taskType">
+                      <Select
+                        placeholder="任务分类"
+                        style={{ width: 100 }}
+                        allowClear
+                        options={Object.keys(TaskTypeEnum).map((key) => ({
+                          label: (TaskTypeEnum as any)[key],
+                          value: key,
+                        }))}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="status">
+                      <Select
+                        placeholder="任务状态"
+                        style={{ width: 100 }}
+                        allowClear
+                        options={Object.keys(StatusEnum).map((key) => ({
+                          label: (StatusEnum as any)[key],
+                          value: key,
+                        }))}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="urgency">
+                      <Select
+                        placeholder="任务紧急度"
+                        style={{ width: 120 }}
+                        allowClear
+                        options={Object.keys(UrgencyEnum).map((key) => ({
+                          label: (UrgencyEnum as any)[key],
+                          value: key,
+                        }))}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="materialType">
+                      <Select
+                        placeholder="素材类型"
+                        style={{ width: 100 }}
+                        allowClear
+                        options={Object.keys(MaterialTypeEnum).map((key) => ({
+                          label: (MaterialTypeEnum as any)[key],
+                          value: key,
+                        }))}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="materialSource">
+                      <Select
+                        placeholder="素材来源"
+                        style={{ width: 100 }}
+                        allowClear
+                        options={Object.keys(MaterialSourceEnum).map((key) => ({
+                          label: (MaterialSourceEnum as any)[key],
+                          value: key,
+                        }))}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Space>
+                      <Button type="primary" htmlType="submit">
+                        搜索
+                      </Button>
+                      <Button onClick={() => form.resetFields()}>重置</Button>
+                      <Button
+                        type="primary"
+                        onClick={() => {
+                          setInitialState({});
+                          setVisible(true);
+                        }}
+                      >
+                        发布素材任务
+                      </Button>
+                    </Space>
+                  </Col>
+                </Row>
+              </Form>
+            </div>
+          </Affix>
+
+          <List
+            itemLayout="vertical"
+            size="large"
+            dataSource={getTaskList.data?.data?.records}
+            loading={getTaskList.loading}
+            pagination={{
+              onChange: (page) => {
+                console.log(page);
+                setQueryForm({ ...queryForm, pageNum: page });
+              },
+              current: queryForm.pageNum,
+              pageSize: queryForm.pageSize,
+            }}
+            renderItem={(item: any, index) => (
+              <List.Item
+                key={item.id}
+                extra={
+                  <Flex gap={20} style={{ height: '100%' }}>
+                    <Flex
+                      style={{ width: size?.width ? `${size?.width / 2 - 265}px` : 300 }}
+                      vertical={true}
+                      align="flex-start"
+                      justify="space-between"
+                    >
+                      <Material
+                        items={item?.materialExample?.split(',')}
+                        resourceUrl={item.materialResource}
+                        claimJson={item.materialClaimJson}
+                      />
+                    </Flex>
+                    <Flex
+                      style={{ width: 165, height: '100%' }}
+                      gap={10}
+                      vertical={true}
+                      align="center"
+                      justify="center"
+                    >
+                      {TaskStatusEle[item.status]}
+                      <Tag color="error">
+                        结算:
+                        {item.checkoutType === 'CHECKOUT_TYPE_SCALE'
+                          ? `总消耗 ${item.checkout * 100}%`
+                          : `${item.checkout}元(审核合格)`}
+                      </Tag>
+                      <Button type="primary" onClick={() => editHandle(item)}>
+                        修改任务
+                      </Button>
+                    </Flex>
+                  </Flex>
+                }
+                actions={[
+                  <Tooltip title="发布时间" key={1}>
+                    <span>{item.createTime}</span>
+                  </Tooltip>,
+                  <Tooltip title="任务分类" key={2}>
+                    {TaskTypeEle[item.taskType]}
+                  </Tooltip>,
+                  <Tooltip title="素材类型" key={3}>
+                    {MaterialTypeEle[item.materialType]}
+                  </Tooltip>,
+                  <Tooltip title="素材来源要求" key={4}>
+                    <Tag>{(MaterialSourceEnum as any)[item.materialSource]}</Tag>
+                  </Tooltip>,
+                ]}
+              >
+                <List.Item.Meta
+                  avatar={
+                    <Avatar
+                      src={
+                        item.avatar ||
+                        `https://xsgames.co/randomusers/avatar.php?g=pixel&key=${index}`
+                      }
+                      style={{ backgroundColor: '#87d068' }}
+                    />
+                  }
+                  title={
+                    <Space size={20}>
+                      <Typography.Title level={5} style={{ marginBottom: 0 }}>
+                        {item.name}
+                      </Typography.Title>
+                      {!!item.urgency && (
+                        <Space size={4}>
+                          <span style={{ color: 'red' }}>置顶</span>🆙
+                        </Space>
+                      )}
+                    </Space>
+                  }
+                  description={
+                    <Space direction="vertical" style={{ width: '100%' }} size={0}>
+                      {/* 有效时间 */}
+                      <Typography.Text type="secondary" ellipsis>
+                        任务有效时间:{item.startTime}~{item.endTime ? item.endTime : '长期有效'}
+                      </Typography.Text>
+                      {/* 任务描述 */}
+                      {item.remark && (
+                        <Typography.Text type="secondary" ellipsis={{ tooltip: true }}>
+                          {item.remark}
+                        </Typography.Text>
+                      )}
+                    </Space>
+                  }
+                />
+                <Typography.Paragraph
+                  ellipsis={{ rows: 3, tooltip: true }}
+                  style={{ marginBottom: 0 }}
+                >
+                  {item.remarkMore}
+                </Typography.Paragraph>
+              </List.Item>
+            )}
+          />
+        </Space>
+
+        {visible && (
+          <TaskModal
+            visible={visible}
+            onClose={() => setVisible(false)}
+            onChange={() => {
+              setVisible(false);
+              getTaskList.refresh();
+            }}
+            initialValues={initialValues}
+          />
+        )}
+      </Card>
+    </PageContainer>
+  );
+};
+
+export default MyTask;

+ 442 - 284
src/pages/MyTask/taskModal.tsx

@@ -1,284 +1,442 @@
-import UploadImg from "@/components/UploadImg"
-import { QuestionCircleOutlined } from "@ant-design/icons"
-import { Col, DatePicker, Form, Input, InputNumber, Modal, Radio, Row, Select, Space, Tooltip, message } from "antd"
-import { RangePickerProps } from "antd/es/date-picker"
-import React from "react"
-import moment from "moment"
-import { CheckoutTypeEnum, MaterialSourceEnum, MaterialTypeEnum, RatioEnum, StatusEnum, TaskTypeEnum, UrgencyEnum } from "@/const"
-import Interval from "@/components/Interval"
-import UploadImgs from "@/components/UploadImgs"
-import UploadZip from "@/components/UploadZip"
-import { addTaskApi, modifyTaskApi } from "@/services/task-api/myTask"
-import { useRequest } from "ahooks"
-
-const disabledRangeTime = (current: any) => {
-    let upDate = moment(current).format('YYYY-MM-DD')//选中的年月日
-    let upHours = moment(current).format('HH')//选中的时
-    let upMinutes = moment(current).format('mm')//选中的分
-    let atDate = moment(new Date()).format('YYYY-MM-DD')//今天的年月日
-    let atHours = moment(new Date()).format('HH')//现在的时
-    let atMinutes = moment(new Date()).format('mm')//现在的分
-    let atSeconds = moment(new Date()).format('ss')//现在的秒
-    let range = (num: number, at: string) => {
-        if (upDate == atDate) {//假如选中时间的今天
-            let arr: number[] = []
-            Array(num).fill('').forEach((a, b) => { if (b < Number(at)) { arr.push(b) } })
-            return arr
-        } else {
-            return []
-        }
-    }
-    let minutes = (num: number, at: string) => {
-        if (upDate == atDate) {//假如选中时间的今天
-            let arr: number[] = []
-            if (upHours == atHours) {
-                Array(num).fill('').forEach((a, b) => { if (b < Number(at) + 5) { arr.push(b) } })
-            }
-            return arr
-        } else {
-            return []
-        }
-    }
-    let seconds = (num: number, at: string) => {
-        if (upDate == atDate) {//假如选中时间的今天
-            let arr: number[] = []
-            if (upHours == atHours) {
-                Array(num).fill('').forEach((a, b) => {
-                    if (Number(upMinutes) - Number(atMinutes) > 5) {
-                    } else {
-                        if (b < Number(at) + 5) { arr.push(b) }
-                    }
-                })
-            }
-            return arr
-        } else {
-            return []
-        }
-    }
-    return {
-        disabledHours: () => range(24, atHours),
-        disabledMinutes: () => minutes(60, atMinutes),
-        disabledSeconds: () => seconds(60, atSeconds),
-    }
-};
-
-interface FormProps extends TASKAPI.AddTask {
-    ratio: any
-    size: any
-    extent: any
-    materialExamples: string[]
-}
-
-interface ModifyFormProps extends TASKAPI.ModifyTask {
-    materialExamples: string[]
-}
-
-interface Props {
-    initialValues?: any
-    visible?: boolean
-    onChange?: () => void
-    onClose?: () => void
-}
-/**
- * 发布修改任务
- * @returns 
- */
-const TaskModal: React.FC<Props> = ({ initialValues, visible, onChange, onClose }) => {
-
-    /*************************************/
-    const [form] = Form.useForm<FormProps>()
-    const endTime = Form.useWatch('endTime', form)
-    const startTime = Form.useWatch('startTime', form)
-    const checkoutType = Form.useWatch('checkoutType', form)
-    const addTask = useRequest(addTaskApi, { manual: true })
-    const modifyTask = useRequest(modifyTaskApi, { manual: true })
-    /*************************************/
-
-    const handleOk = async () => {
-        form.submit()
-        let data = await form.validateFields()
-        if (initialValues?.id) {
-            data.id = initialValues?.id
-            const { startTime, materialExamples, ...par } = data as ModifyFormProps
-            if (par?.endTime) {
-                par['endTime'] = moment(par.endTime).format('YYYY-MM-DD HH:mm:ss')
-            }
-            if (materialExamples?.length > 0) {
-                par['materialExample'] = materialExamples.toString()
-            }
-            modifyTask.runAsync({ ...par, startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss') }).then(res => {
-                if (res?.data) {
-                    message.success('修改成功')
-                    onChange?.()
-                }
-            })
-        } else {
-            const { size, extent, startTime, checkoutType, checkout, materialExamples, ...par } = data
-            let materialClaimJson = JSON.stringify({ size, extent })
-            if (par?.endTime) {
-                par['endTime'] = moment(par.endTime).format('YYYY-MM-DD HH:mm:ss')
-            }
-            if (materialExamples?.length > 0) {
-                par['materialExample'] = materialExamples.toString()
-            }
-            addTask.runAsync({ ...par, materialClaimJson, startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'), checkoutType, checkout: checkoutType === 'CHECKOUT_TYPE_SCALE' ? checkout / 100 : checkout }).then(res => {
-                console.log(res)
-                if (res?.data) {
-                    message.success('发布成功')
-                    onChange?.()
-                }
-            })
-        }
-    }
-
-    const disabledStartDate: RangePickerProps['disabledDate'] = (current) => {
-        // Can not select days before today and today
-        if (endTime) {
-            return current && (current < moment().startOf('day') || current > moment(endTime).endOf('day'));
-        }
-        return current && (current < moment().startOf('day') || current > moment().add(30, 'day').endOf('day'));
-    };
-
-    const disabledEndDate: RangePickerProps['disabledDate'] = (current) => {
-        // Can not select days before today and today
-        if (startTime) {
-            return current && (current < moment(startTime).startOf('day') || current > moment().add(30, 'day').endOf('day'));
-        }
-        return current && (current < moment().startOf('day') || current > moment().add(30, 'day').endOf('day'));
-    };
-
-    return <Modal
-        title={initialValues?.id ? `修改任务` : `发布任务`}
-        open={visible}
-        onCancel={onClose}
-        onOk={handleOk}
-        width={1000}
-        confirmLoading={addTask.loading || modifyTask.loading}
-    >
-        <Form
-            initialValues={
-                Object.keys(initialValues).length > 0 ?
-                    initialValues :
-                    {
-                        taskType: 'TASK_TYPE_GAME',
-                        materialType: 'MATERIAL_TYPE_VIDEO',
-                        status: 'STATUS_NORMAL',
-                        materialSource: 'MATERIAL_SOURCE_ORIGINAL',
-                        checkoutType: 'CHECKOUT_TYPE_SCALE',
-                        checkout: 2,
-                        // avatar: 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/AAFE07F5819A488EB0A7B14E03FBBE7E.jpg',
-                        // materialExamples: ['https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/AAFE07F5819A488EB0A7B14E03FBBE7E.jpg', 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/2E6677DBEC314344B0416557531899D2.jpg', 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/AAFE07F5819A488EB0A7B14E03FBBE7E.jpg', 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/2E6677DBEC314344B0416557531899D2.jpg'],
-                        // materialResource: 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/zip/BBB2531663F448A7A4D599469F354DC9.zip'
-                    }
-            }
-            name="taskModal"
-            form={form}
-            labelCol={{ span: 6 }}
-            wrapperCol={{ span: 18 }}
-            colon={false}
-            labelAlign="left"
-        >
-            <Row gutter={20}>
-                <Col span={12}>
-                    <Form.Item label={<strong>任务头像</strong>} name='avatar'>
-                        <UploadImg isUpload={true} />
-                    </Form.Item>
-                    <Form.Item label={<strong>任务名称</strong>} name='name' rules={[{ required: true, message: '请输入任务名称!' }]}>
-                        <Input placeholder="请输入任务名称" />
-                    </Form.Item>
-                    <Form.Item label={<strong>描述</strong>} name='remark'>
-                        <Input placeholder="请输入任务描述" />
-                    </Form.Item>
-                    <Form.Item label={<strong>说明</strong>} name='remarkMore' rules={[{ required: true, message: '请输入说明!' }]}>
-                        <Input placeholder="请输入补充说明" />
-                    </Form.Item>
-                    <Form.Item label={<strong>
-                        <span style={{ color: '#ff4d4f' }}>*</span>有效时间
-                        <Tooltip title="开始日期必填">
-                            <QuestionCircleOutlined style={{ marginLeft: 2 }} />
-                        </Tooltip>
-                    </strong>}>
-                        <Space>
-                            <Form.Item name="startTime" noStyle rules={[{ required: true, message: '请设置开始日期!' }]}>
-                                <DatePicker placeholder="开始日期" disabledDate={disabledStartDate} showTime disabledTime={disabledRangeTime} />
-                            </Form.Item>
-                            <span>-</span>
-                            <Form.Item name="endTime" noStyle>
-                                <DatePicker placeholder="结束日期" disabledDate={disabledEndDate} showTime disabledTime={disabledRangeTime} />
-                            </Form.Item>
-                        </Space>
-                    </Form.Item>
-                    <Form.Item label={<strong>紧急度</strong>} name='urgency' rules={[{ required: true, message: '请选择任务紧急度!' }]} tooltip="任务紧急度决定任务的展示情况,默认将“置顶”任务展示页面最前,其次按照发布时间倒序排列展示(即最新发布的任务展示在页面最前端)">
-                        <Select
-                            placeholder="请选择任务紧急度"
-                            options={Object.keys(UrgencyEnum).map(key => ({ label: (UrgencyEnum as any)[key], value: key }))}
-                        />
-                    </Form.Item>
-                    <Form.Item label={<strong>任务状态</strong>} name='status' rules={[{ required: true, message: '请选择任务状态!' }]}>
-                        <Radio.Group>
-                            {Object.keys(StatusEnum).filter(key => initialValues?.id ? true : key === 'STATUS_EXPIRE' ? false : true).map(key => <Radio value={key} key={key}>{(StatusEnum as any)[key]}</Radio>)}
-                        </Radio.Group>
-                    </Form.Item>
-                    {!initialValues?.id && <>
-                        <Form.Item label={<strong>任务分类</strong>} name='taskType' rules={[{ required: true, message: '请选择任务分类!' }]}>
-                            <Radio.Group>
-                                {Object.keys(TaskTypeEnum).map(key => <Radio value={key} key={key}>{(TaskTypeEnum as any)[key]}</Radio>)}
-                            </Radio.Group>
-                        </Form.Item>
-                        <Form.Item label={<strong>素材类型</strong>} name='materialType' rules={[{ required: true, message: '请选择素材类型!' }]}>
-                            <Radio.Group>
-                                {Object.keys(MaterialTypeEnum).map(key => <Radio value={key} key={key}>{(MaterialTypeEnum as any)[key]}</Radio>)}
-                            </Radio.Group>
-                        </Form.Item>
-                        <Form.Item label={<strong>素材来源</strong>} name='materialSource' rules={[{ required: true, message: '请选择素材来源!' }]}>
-                            <Radio.Group>
-                                {Object.keys(MaterialSourceEnum).map(key => <Radio value={key} key={key}>{(MaterialSourceEnum as any)[key]}</Radio>)}
-                            </Radio.Group>
-                        </Form.Item>
-                        <Form.Item label={<strong>结算类型</strong>} name='checkoutType' rules={[{ required: true, message: '请选择结算类型!' }]} tooltip="消耗比例:按照最终素材的总消耗比例计算奖励,月度结算奖励;任务:按照任务最终审核状态为“合格”的素材计算奖励,月度结算奖励">
-                            <Radio.Group onChange={(e) => {
-                                if (e.target.value === 'CHECKOUT_TYPE_SCALE') form.setFieldsValue({ checkout: 2 })
-                                else form.setFieldsValue({ checkout: 100 })
-                            }}>
-                                {Object.keys(CheckoutTypeEnum).map(key => <Radio value={key} key={key}>{(CheckoutTypeEnum as any)[key]}</Radio>)}
-                            </Radio.Group>
-                        </Form.Item>
-                        <Form.Item label={<strong>奖励结算</strong>} name='checkout' rules={[{ required: true, message: '请输入奖励结算!' }]}>
-                            {checkoutType === 'CHECKOUT_TYPE_SCALE' ? <InputNumber
-                                min={0}
-                                max={100}
-                                formatter={(value) => `${value}%`}
-                                parser={(value) => value!.replace('%', '') as any}
-                            /> : <InputNumber min={0} suffix="元" />}
-                        </Form.Item>
-                    </>}
-                </Col>
-                <Col span={12}>
-                    {!initialValues?.id && <>
-                        {/* <Form.Item label={<strong>素材比例</strong>} name='ratio' rules={[{ required: true, message: '请选择素材比例!' }]}>
-                            <Select
-                                placeholder="请选择素材比例"
-                                options={Object.keys(RatioEnum).map(key => ({ label: (RatioEnum as any)[key], value: key }))}
-                            />
-                        </Form.Item> */}
-                        <Form.Item label={<strong>素材尺寸</strong>} name='size' rules={[{ required: true, message: '请选择素材比例!' }]}>
-                            <Interval unit="*" placeholder={['720', '1280']} />
-                        </Form.Item>
-                        <Form.Item label={<strong>素材大小</strong>} name='extent' tooltip="单位(MB)" rules={[{ required: true, message: '请选择素材比例!' }]}>
-                            <Interval placeholder={['20M', '40M']} />
-                        </Form.Item>
-                    </>}
-                    <Form.Item label={<strong>素材示例</strong>} name='materialExamples'>
-                        <UploadImgs isUpload={true} maxCount={10} />
-                    </Form.Item>
-                    <Form.Item label={<strong>素材资源包</strong>} name='materialResource'>
-                        <UploadZip />
-                    </Form.Item>
-                </Col>
-            </Row>
-
-        </Form>
-    </Modal>
-}
-
-export default React.memo(TaskModal)
+import Interval from '@/components/Interval';
+import UploadImg from '@/components/UploadImg';
+import UploadImgs from '@/components/UploadImgs';
+import UploadZip from '@/components/UploadZip';
+import { addTaskApi, modifyTaskApi } from '@/services/task-api/myTask';
+import {
+  CheckoutTypeEnum,
+  MaterialSourceEnum,
+  MaterialTypeEnum,
+  StatusEnum,
+  TaskTypeEnum,
+  UrgencyEnum,
+} from '@/utils/constEnum';
+import { QuestionCircleOutlined } from '@ant-design/icons';
+import { useRequest } from 'ahooks';
+import {
+  Col,
+  DatePicker,
+  Form,
+  Input,
+  InputNumber,
+  message,
+  Modal,
+  Radio,
+  Row,
+  Select,
+  Space,
+  Tooltip,
+} from 'antd';
+import { RangePickerProps } from 'antd/es/date-picker';
+import moment from 'moment';
+import React from 'react';
+
+const disabledRangeTime = (current: any) => {
+  let upDate = moment(current).format('YYYY-MM-DD'); //选中的年月日
+  let upHours = moment(current).format('HH'); //选中的时
+  let upMinutes = moment(current).format('mm'); //选中的分
+  let atDate = moment(new Date()).format('YYYY-MM-DD'); //今天的年月日
+  let atHours = moment(new Date()).format('HH'); //现在的时
+  let atMinutes = moment(new Date()).format('mm'); //现在的分
+  let atSeconds = moment(new Date()).format('ss'); //现在的秒
+  let range = (num: number, at: string) => {
+    if (upDate === atDate) {
+      //假如选中时间的今天
+      let arr: number[] = [];
+      Array(num)
+        .fill('')
+        .forEach((a, b) => {
+          if (b < Number(at)) {
+            arr.push(b);
+          }
+        });
+      return arr;
+    } else {
+      return [];
+    }
+  };
+  let minutes = (num: number, at: string) => {
+    if (upDate === atDate) {
+      //假如选中时间的今天
+      let arr: number[] = [];
+      if (upHours === atHours) {
+        Array(num)
+          .fill('')
+          .forEach((a, b) => {
+            if (b < Number(at) + 5) {
+              arr.push(b);
+            }
+          });
+      }
+      return arr;
+    } else {
+      return [];
+    }
+  };
+  let seconds = (num: number, at: string) => {
+    if (upDate === atDate) {
+      //假如选中时间的今天
+      let arr: number[] = [];
+      if (upHours === atHours) {
+        Array(num)
+          .fill('')
+          .forEach((a, b) => {
+            if (Number(upMinutes) - Number(atMinutes) > 5) {
+            } else {
+              if (b < Number(at) + 5) {
+                arr.push(b);
+              }
+            }
+          });
+      }
+      return arr;
+    } else {
+      return [];
+    }
+  };
+  return {
+    disabledHours: () => range(24, atHours),
+    disabledMinutes: () => minutes(60, atMinutes),
+    disabledSeconds: () => seconds(60, atSeconds),
+  };
+};
+
+interface FormProps extends TASKAPI.AddTask {
+  ratio: any;
+  size: any;
+  extent: any;
+  materialExamples: string[];
+}
+
+interface ModifyFormProps extends TASKAPI.ModifyTask {
+  materialExamples: string[];
+}
+
+interface Props {
+  initialValues?: any;
+  visible?: boolean;
+  onChange?: () => void;
+  onClose?: () => void;
+}
+/**
+ * 发布修改任务
+ * @returns
+ */
+const TaskModal: React.FC<Props> = ({ initialValues, visible, onChange, onClose }) => {
+  /*************************************/
+  const [form] = Form.useForm<FormProps>();
+  const endTime = Form.useWatch('endTime', form);
+  const startTime = Form.useWatch('startTime', form);
+  const checkoutType = Form.useWatch('checkoutType', form);
+  const addTask = useRequest(addTaskApi, { manual: true });
+  const modifyTask = useRequest(modifyTaskApi, { manual: true });
+  /*************************************/
+
+  const handleOk = async () => {
+    form.submit();
+    let data = await form.validateFields();
+    if (initialValues?.id) {
+      data.id = initialValues?.id;
+      const { startTime, materialExamples, ...par } = data as ModifyFormProps;
+      if (par?.endTime) {
+        par['endTime'] = moment(par.endTime).format('YYYY-MM-DD HH:mm:ss');
+      }
+      if (materialExamples?.length > 0) {
+        par['materialExample'] = materialExamples.toString();
+      }
+      modifyTask
+        .runAsync({ ...par, startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss') })
+        .then((res) => {
+          if (res?.data) {
+            message.success('修改成功');
+            onChange?.();
+          }
+        });
+    } else {
+      const { size, extent, startTime, checkoutType, checkout, materialExamples, ...par } = data;
+      let materialClaimJson = JSON.stringify({ size, extent });
+      if (par?.endTime) {
+        par['endTime'] = moment(par.endTime).format('YYYY-MM-DD HH:mm:ss');
+      }
+      if (materialExamples?.length > 0) {
+        par['materialExample'] = materialExamples.toString();
+      }
+      addTask
+        .runAsync({
+          ...par,
+          materialClaimJson,
+          startTime: moment(startTime).format('YYYY-MM-DD HH:mm:ss'),
+          checkoutType,
+          checkout: checkoutType === 'CHECKOUT_TYPE_SCALE' ? checkout / 100 : checkout,
+        })
+        .then((res) => {
+          console.log(res);
+          if (res?.data) {
+            message.success('发布成功');
+            onChange?.();
+          }
+        });
+    }
+  };
+
+  const disabledStartDate: RangePickerProps['disabledDate'] = (current) => {
+    // Can not select days before today and today
+    if (endTime) {
+      return (
+        current && (current < moment().startOf('day') || current > moment(endTime).endOf('day'))
+      );
+    }
+    return (
+      current &&
+      (current < moment().startOf('day') || current > moment().add(30, 'day').endOf('day'))
+    );
+  };
+
+  const disabledEndDate: RangePickerProps['disabledDate'] = (current) => {
+    // Can not select days before today and today
+    if (startTime) {
+      return (
+        current &&
+        (current < moment(startTime).startOf('day') ||
+          current > moment().add(30, 'day').endOf('day'))
+      );
+    }
+    return (
+      current &&
+      (current < moment().startOf('day') || current > moment().add(30, 'day').endOf('day'))
+    );
+  };
+
+  return (
+    <Modal
+      title={initialValues?.id ? `修改任务` : `发布任务`}
+      open={visible}
+      onCancel={onClose}
+      onOk={handleOk}
+      width={1000}
+      confirmLoading={addTask.loading || modifyTask.loading}
+    >
+      <Form
+        initialValues={
+          Object.keys(initialValues).length > 0
+            ? initialValues
+            : {
+                taskType: 'TASK_TYPE_GAME',
+                materialType: 'MATERIAL_TYPE_VIDEO',
+                status: 'STATUS_NORMAL',
+                materialSource: 'MATERIAL_SOURCE_ORIGINAL',
+                checkoutType: 'CHECKOUT_TYPE_SCALE',
+                checkout: 2,
+                // avatar: 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/AAFE07F5819A488EB0A7B14E03FBBE7E.jpg',
+                // materialExamples: ['https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/AAFE07F5819A488EB0A7B14E03FBBE7E.jpg', 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/2E6677DBEC314344B0416557531899D2.jpg', 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/AAFE07F5819A488EB0A7B14E03FBBE7E.jpg', 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/image/2E6677DBEC314344B0416557531899D2.jpg'],
+                // materialResource: 'https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/zip/BBB2531663F448A7A4D599469F354DC9.zip'
+              }
+        }
+        name="taskModal"
+        form={form}
+        labelCol={{ span: 6 }}
+        wrapperCol={{ span: 18 }}
+        colon={false}
+        labelAlign="left"
+      >
+        <Row gutter={20}>
+          <Col span={12}>
+            <Form.Item label={<strong>任务头像</strong>} name="avatar">
+              <UploadImg isUpload={true} />
+            </Form.Item>
+            <Form.Item
+              label={<strong>任务名称</strong>}
+              name="name"
+              rules={[{ required: true, message: '请输入任务名称!' }]}
+            >
+              <Input placeholder="请输入任务名称" />
+            </Form.Item>
+            <Form.Item label={<strong>描述</strong>} name="remark">
+              <Input placeholder="请输入任务描述" />
+            </Form.Item>
+            <Form.Item
+              label={<strong>说明</strong>}
+              name="remarkMore"
+              rules={[{ required: true, message: '请输入说明!' }]}
+            >
+              <Input placeholder="请输入补充说明" />
+            </Form.Item>
+            <Form.Item
+              label={
+                <strong>
+                  <span style={{ color: '#ff4d4f' }}>*</span>有效时间
+                  <Tooltip title="开始日期必填">
+                    <QuestionCircleOutlined style={{ marginLeft: 2 }} />
+                  </Tooltip>
+                </strong>
+              }
+            >
+              <Space>
+                <Form.Item
+                  name="startTime"
+                  noStyle
+                  rules={[{ required: true, message: '请设置开始日期!' }]}
+                >
+                  <DatePicker
+                    placeholder="开始日期"
+                    disabledDate={disabledStartDate}
+                    showTime
+                    disabledTime={disabledRangeTime}
+                  />
+                </Form.Item>
+                <span>-</span>
+                <Form.Item name="endTime" noStyle>
+                  <DatePicker
+                    placeholder="结束日期"
+                    disabledDate={disabledEndDate}
+                    showTime
+                    disabledTime={disabledRangeTime}
+                  />
+                </Form.Item>
+              </Space>
+            </Form.Item>
+            <Form.Item
+              label={<strong>紧急度</strong>}
+              name="urgency"
+              rules={[{ required: true, message: '请选择任务紧急度!' }]}
+              tooltip="任务紧急度决定任务的展示情况,默认将“置顶”任务展示页面最前,其次按照发布时间倒序排列展示(即最新发布的任务展示在页面最前端)"
+            >
+              <Select
+                placeholder="请选择任务紧急度"
+                options={Object.keys(UrgencyEnum).map((key) => ({
+                  label: (UrgencyEnum as any)[key],
+                  value: key,
+                }))}
+              />
+            </Form.Item>
+            <Form.Item
+              label={<strong>任务状态</strong>}
+              name="status"
+              rules={[{ required: true, message: '请选择任务状态!' }]}
+            >
+              <Radio.Group>
+                {Object.keys(StatusEnum)
+                  .filter((key) =>
+                    initialValues?.id ? true : key === 'STATUS_EXPIRE' ? false : true,
+                  )
+                  .map((key) => (
+                    <Radio value={key} key={key}>
+                      {(StatusEnum as any)[key]}
+                    </Radio>
+                  ))}
+              </Radio.Group>
+            </Form.Item>
+            {!initialValues?.id && (
+              <>
+                <Form.Item
+                  label={<strong>任务分类</strong>}
+                  name="taskType"
+                  rules={[{ required: true, message: '请选择任务分类!' }]}
+                >
+                  <Radio.Group>
+                    {Object.keys(TaskTypeEnum).map((key) => (
+                      <Radio value={key} key={key}>
+                        {(TaskTypeEnum as any)[key]}
+                      </Radio>
+                    ))}
+                  </Radio.Group>
+                </Form.Item>
+                <Form.Item
+                  label={<strong>素材类型</strong>}
+                  name="materialType"
+                  rules={[{ required: true, message: '请选择素材类型!' }]}
+                >
+                  <Radio.Group>
+                    {Object.keys(MaterialTypeEnum).map((key) => (
+                      <Radio value={key} key={key}>
+                        {(MaterialTypeEnum as any)[key]}
+                      </Radio>
+                    ))}
+                  </Radio.Group>
+                </Form.Item>
+                <Form.Item
+                  label={<strong>素材来源</strong>}
+                  name="materialSource"
+                  rules={[{ required: true, message: '请选择素材来源!' }]}
+                >
+                  <Radio.Group>
+                    {Object.keys(MaterialSourceEnum).map((key) => (
+                      <Radio value={key} key={key}>
+                        {(MaterialSourceEnum as any)[key]}
+                      </Radio>
+                    ))}
+                  </Radio.Group>
+                </Form.Item>
+                <Form.Item
+                  label={<strong>结算类型</strong>}
+                  name="checkoutType"
+                  rules={[{ required: true, message: '请选择结算类型!' }]}
+                  tooltip="消耗比例:按照最终素材的总消耗比例计算奖励,月度结算奖励;任务:按照任务最终审核状态为“合格”的素材计算奖励,月度结算奖励"
+                >
+                  <Radio.Group
+                    onChange={(e) => {
+                      if (e.target.value === 'CHECKOUT_TYPE_SCALE')
+                        form.setFieldsValue({ checkout: 2 });
+                      else form.setFieldsValue({ checkout: 100 });
+                    }}
+                  >
+                    {Object.keys(CheckoutTypeEnum).map((key) => (
+                      <Radio value={key} key={key}>
+                        {(CheckoutTypeEnum as any)[key]}
+                      </Radio>
+                    ))}
+                  </Radio.Group>
+                </Form.Item>
+                <Form.Item
+                  label={<strong>奖励结算</strong>}
+                  name="checkout"
+                  rules={[{ required: true, message: '请输入奖励结算!' }]}
+                >
+                  {checkoutType === 'CHECKOUT_TYPE_SCALE' ? (
+                    <InputNumber
+                      min={0}
+                      max={100}
+                      formatter={(value) => `${value}%`}
+                      parser={(value) => value!.replace('%', '') as any}
+                    />
+                  ) : (
+                    <InputNumber min={0} suffix="元" />
+                  )}
+                </Form.Item>
+              </>
+            )}
+          </Col>
+          <Col span={12}>
+            {!initialValues?.id && (
+              <>
+                <Form.Item
+                  label={<strong>素材尺寸</strong>}
+                  name="size"
+                  rules={[{ required: true, message: '请选择素材比例!' }]}
+                >
+                  <Interval unit="*" placeholder={['720', '1280']} />
+                </Form.Item>
+                <Form.Item
+                  label={<strong>素材大小</strong>}
+                  name="extent"
+                  tooltip="单位(MB)"
+                  rules={[{ required: true, message: '请选择素材比例!' }]}
+                >
+                  <Interval placeholder={['20M', '40M']} />
+                </Form.Item>
+              </>
+            )}
+            <Form.Item label={<strong>素材示例</strong>} name="materialExamples">
+              <UploadImgs isUpload={true} maxCount={10} />
+            </Form.Item>
+            <Form.Item label={<strong>素材资源包</strong>} name="materialResource">
+              <UploadZip />
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    </Modal>
+  );
+};
+
+export default React.memo(TaskModal);

+ 144 - 113
src/pages/Opus/index.tsx

@@ -1,113 +1,144 @@
-import { SearchOutlined } from "@ant-design/icons"
-import { PageContainer } from "@ant-design/pro-components"
-import { Affix, Button, Card, Col, DatePicker, Form, Input, Radio, Row, Select, Space, theme } from "antd"
-import moment from "moment";
-import { useEffect, useState } from "react";
-import OpusRoll from "./opusRoll";
-import style from './index.less'
-import { useRequest } from "ahooks";
-import { getOpusListApi } from "@/services/task-api/opus";
-import { MaterialTypeEnum } from "@/const";
-
-const Opus: React.FC = () => {
-
-    /**************************************/
-    const { useToken } = theme;
-    const { token } = useToken();
-    const [form] = Form.useForm<TASKAPI.OpusList>()
-    const [queryForm, setQueryForm] = useState<TASKAPI.OpusList>({ pageNum: 1, pageSize: 10 })
-    const getOpusList = useRequest(getOpusListApi, { manual: true })
-    const [data, setData] = useState<any[]>([])
-    /**************************************/
-
-    useEffect(() => {
-        getOpusList.runAsync(queryForm).then(res => {
-            if (res) {
-                if (queryForm.pageNum === 1) {
-                    setData(res?.data?.records)
-                } else {
-                    setData(data.concat(res?.data?.records))
-                }
-            }
-        })
-    }, [queryForm])
-
-    const onFinish = (data: any) => {
-        const { taskTime, uploadTime, ...pre } = data
-        let newQueryForm = JSON.parse(JSON.stringify(queryForm))
-        newQueryForm.pageNum = 1
-        if (taskTime) {
-            newQueryForm.taskCreateStartTime = moment(taskTime[0]).format('YYYY-MM-DD')
-            newQueryForm.taskCreateEndTime = moment(taskTime[1]).format('YYYY-MM-DD')
-        } else {
-            delete newQueryForm?.taskCreateStartTime
-            delete newQueryForm?.taskCreateEndTime
-        }
-        if (uploadTime) {
-            newQueryForm.uploadStartTime = moment(uploadTime[0]).format('YYYY-MM-DD')
-            newQueryForm.uploadEndTime = moment(uploadTime[1]).format('YYYY-MM-DD')
-        } else {
-            delete newQueryForm?.uploadStartTime
-            delete newQueryForm?.uploadEndTime
-        }
-        setQueryForm({ ...newQueryForm, ...pre })
-    }
-
-    // 下一页
-    const loadMoreData = () => {
-        setQueryForm({ ...queryForm, pageNum: queryForm.pageNum + 1 })
-    }
-
-    return <PageContainer>
-        <Card
-            style={{ borderRadius: 8 }}
-            bodyStyle={{ padding: 0 }}
-        >
-            <Space direction="vertical" style={{ width: '100%' }} size={0}>
-                <Affix offsetTop={56}>
-                    <div style={{ backgroundColor: token.colorBgBase, padding: token.paddingContentHorizontalLG, borderRadius: 8, borderBottom: `1px solid ${token.colorBorderSecondary}` }}>
-                        <Form layout="inline" className='queryForm' name="basic" form={form} onFinish={onFinish}>
-                            <Row gutter={[0, 6]}>
-                                <Col>
-                                    <Form.Item name='materialType'>
-                                        <Select
-                                            placeholder="素材类型"
-                                            options={Object.keys(MaterialTypeEnum).map(key => ({ label: (MaterialTypeEnum as any)[key], value: key }))}
-                                        />
-                                    </Form.Item>
-                                </Col>
-
-                                <Col>
-                                    <Form.Item name='taskName'>
-                                        <Input prefix={<SearchOutlined />} placeholder="搜索任务名称" />
-                                    </Form.Item>
-                                </Col>
-
-                                <Col><Form.Item name='taskTime'>
-                                    <DatePicker.RangePicker style={{ width: 250 }} placeholder={['任务开始日期', '任务开始日期']} />
-                                </Form.Item></Col>
-
-                                <Col><Form.Item name='uploadTime'>
-                                    <DatePicker.RangePicker style={{ width: 250 }} placeholder={['上传开始日期', '上传开始日期']} />
-                                </Form.Item></Col>
-
-                                <Col>
-                                    <Space>
-                                        <Button type="primary" htmlType="submit">搜索</Button>
-                                        <Button onClick={() => form.resetFields()}>重置</Button>
-                                    </Space>
-                                </Col>
-                            </Row>
-                        </Form>
-                    </div>
-                </Affix>
-
-                <div style={{ padding: 19 }} className={style.weMaterial_left}>
-                    <OpusRoll data={data} total={getOpusList.data?.data?.total || 0} loading={getOpusList.loading} loadMore={loadMoreData}/>
-                </div>
-            </Space>
-        </Card>
-    </PageContainer>
-}
-
-export default Opus
+import { getOpusListApi } from '@/services/task-api/opus';
+import { MaterialTypeEnum } from '@/utils/constEnum';
+import { SearchOutlined } from '@ant-design/icons';
+import { PageContainer } from '@ant-design/pro-components';
+import { useRequest } from 'ahooks';
+import { Affix, Button, Card, Col, DatePicker, Form, Input, Row, Select, Space, theme } from 'antd';
+import moment from 'moment';
+import { useEffect, useState } from 'react';
+import style from './index.less';
+import OpusRoll from './opusRoll';
+
+const Opus: React.FC = () => {
+  /**************************************/
+  const { useToken } = theme;
+  const { token } = useToken();
+  const [form] = Form.useForm<TASKAPI.OpusList>();
+  const [queryForm, setQueryForm] = useState<TASKAPI.OpusList>({ pageNum: 1, pageSize: 10 });
+  const getOpusList = useRequest(getOpusListApi, { manual: true });
+  const [data, setData] = useState<any[]>([]);
+  /**************************************/
+
+  useEffect(() => {
+    getOpusList.runAsync(queryForm).then((res) => {
+      if (res) {
+        if (queryForm.pageNum === 1) {
+          setData(res?.data?.records);
+        } else {
+          setData(data.concat(res?.data?.records));
+        }
+      }
+    });
+  }, [queryForm]);
+
+  const onFinish = (data: any) => {
+    const { taskTime, uploadTime, ...pre } = data;
+    let newQueryForm = JSON.parse(JSON.stringify(queryForm));
+    newQueryForm.pageNum = 1;
+    if (taskTime) {
+      newQueryForm.taskCreateStartTime = moment(taskTime[0]).format('YYYY-MM-DD');
+      newQueryForm.taskCreateEndTime = moment(taskTime[1]).format('YYYY-MM-DD');
+    } else {
+      delete newQueryForm?.taskCreateStartTime;
+      delete newQueryForm?.taskCreateEndTime;
+    }
+    if (uploadTime) {
+      newQueryForm.uploadStartTime = moment(uploadTime[0]).format('YYYY-MM-DD');
+      newQueryForm.uploadEndTime = moment(uploadTime[1]).format('YYYY-MM-DD');
+    } else {
+      delete newQueryForm?.uploadStartTime;
+      delete newQueryForm?.uploadEndTime;
+    }
+    setQueryForm({ ...newQueryForm, ...pre });
+  };
+
+  // 下一页
+  const loadMoreData = () => {
+    setQueryForm({ ...queryForm, pageNum: queryForm.pageNum + 1 });
+  };
+
+  return (
+    <PageContainer>
+      <Card style={{ borderRadius: 8 }} bodyStyle={{ padding: 0 }}>
+        <Space direction="vertical" style={{ width: '100%' }} size={0}>
+          <Affix offsetTop={56}>
+            <div
+              style={{
+                backgroundColor: token.colorBgBase,
+                padding: token.paddingContentHorizontalLG,
+                borderRadius: 8,
+                borderBottom: `1px solid ${token.colorBorderSecondary}`,
+              }}
+            >
+              <Form
+                layout="inline"
+                className="queryForm"
+                name="basic"
+                form={form}
+                onFinish={onFinish}
+              >
+                <Row gutter={[0, 6]}>
+                  <Col>
+                    <Form.Item name="materialType">
+                      <Select
+                        placeholder="素材类型"
+                        options={Object.keys(MaterialTypeEnum).map((key) => ({
+                          label: (MaterialTypeEnum as any)[key],
+                          value: key,
+                        }))}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="taskName">
+                      <Input prefix={<SearchOutlined />} placeholder="搜索任务名称" />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="taskTime">
+                      <DatePicker.RangePicker
+                        style={{ width: 250 }}
+                        placeholder={['任务开始日期', '任务开始日期']}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Form.Item name="uploadTime">
+                      <DatePicker.RangePicker
+                        style={{ width: 250 }}
+                        placeholder={['上传开始日期', '上传开始日期']}
+                      />
+                    </Form.Item>
+                  </Col>
+
+                  <Col>
+                    <Space>
+                      <Button type="primary" htmlType="submit">
+                        搜索
+                      </Button>
+                      <Button onClick={() => form.resetFields()}>重置</Button>
+                    </Space>
+                  </Col>
+                </Row>
+              </Form>
+            </div>
+          </Affix>
+
+          <div style={{ padding: 19 }} className={style.weMaterial_left}>
+            <OpusRoll
+              data={data}
+              total={getOpusList.data?.data?.total || 0}
+              loading={getOpusList.loading}
+              loadMore={loadMoreData}
+            />
+          </div>
+        </Space>
+      </Card>
+    </PageContainer>
+  );
+};
+
+export default Opus;

+ 204 - 143
src/pages/Opus/opusRoll.tsx

@@ -1,143 +1,204 @@
-import React, { useEffect, useMemo, useRef, useState } from "react"
-import style from './index.less'
-import { Button, Card, Divider, Flex, Skeleton, Space, Spin, Tag, Typography } from "antd";
-import InfiniteScroll from "react-infinite-scroll-component";
-import { useRequest, useSize } from "ahooks";
-import VideoOpus from "./components/VideoOpus";
-import { MaterialSourceEnum } from "@/const";
-import { downloadEscalationApi } from "@/services/task-api/download";
-
-interface Props {
-    data: any[]
-    total: number,
-    loading?: boolean
-    loadMore?: () => void
-    flexNum?: number
-    id?: string
-}
-
-const OpusRoll: React.FC<Props> = ({ loading, data, total, loadMore, flexNum = 0, id = 'scrollableDiv' }) => {
-
-    /************************/
-    const ref = useRef<HTMLDivElement>(null);
-    const size = useSize(ref);
-    const [rowNum, setRowNum] = useState<number>(flexNum)
-    const [dataList, setDataList] = useState<any[]>([])
-    const downloadEscalation = useRequest(downloadEscalationApi, { manual: true })
-    /************************/
-
-    useEffect(() => {
-        setDataList(data || [])
-    }, [data])
-
-    // 根据内容宽度计算列数
-    useEffect(() => {
-        if (size?.width && !flexNum) {
-            let rowNum = Math.floor((size?.width - 26) / 260)
-            setRowNum(rowNum || 1)
-        }
-    }, [size?.width])
-
-    const download = (url: string, userMaterialId: number) => {
-        downloadEscalation.runAsync({ userMaterialId }).then(res => {
-            const fileName = 'downloaded.mp4'; // 可以自定义下载的文件名
-            let link = url
-            let x = new XMLHttpRequest()
-            x.open('GET', link, true)
-            x.responseType = 'blob'
-            x.onload = (e) => {
-                let url = window.URL.createObjectURL(x.response)
-                let a = document.createElement('a')
-                a.href = url
-                a.download = fileName
-                a.click()
-            }
-            x.send()
-        })
-    }
-
-    const content = useMemo(() => {
-        let sourceData: any[] = []
-        dataList.forEach((item, index) => {
-            sourceData[index % rowNum] ? sourceData[index % rowNum]?.push(item) : sourceData[index % rowNum] = [item]
-        })
-
-        // 不足
-        if (sourceData.length < rowNum) {
-            let fewNum = rowNum - sourceData.length
-            sourceData = [...sourceData, ...Array(fewNum).fill([]).map(item => item)]
-        }
-        return <>
-            {sourceData.map((item: any[], index) => <div key={index} className={style.content_row} style={{ width: (1 / rowNum * 100) + '%' }}>
-                {item?.map((list: any) => {
-                    const { size } = JSON.parse(list.materialClaimJson)
-                    return <Card
-                        className={style.left_content_card}
-                        key={list.id}
-                        bodyStyle={{ padding: 10, position: 'relative' }}
-                        headStyle={{ padding: '5px 10px' }}
-                        cover={<div className={style.left_content_caover}>
-                            <VideoOpus src={list.link} />
-                        </div>}
-                        title={<Space direction="vertical" size={0} style={{ width: '100%' }}>
-                            <Typography.Title style={{ margin: 0, fontSize: 15 }} level={5} ellipsis>{list.taskName}</Typography.Title>
-                            <Typography.Text type="secondary" style={{ fontSize: 13 }}>{list.createTime}</Typography.Text>
-                        </Space>}
-                    >
-                        <Flex justify="space-between">
-                            <Typography.Text>素材尺寸:{size[0]}*{size[1]}</Typography.Text>
-                            <Typography.Text>素材大小:{(list.size / 1024 / 1024).toFixed(2)}M</Typography.Text>
-                        </Flex>
-                        <Flex justify="space-between">
-                            <Typography.Text>素材ID:{list.taskId}</Typography.Text>
-                            {(!list.cost && list.cost !== 0) ? null : <Typography.Text>消耗:{list.cost}</Typography.Text>}
-                        </Flex>
-                        <Flex justify="space-between">
-                            <Space size={0}>
-                                <Tag color="#55acee">{(MaterialSourceEnum as any)[list.materialSource]}</Tag>
-                                {list.checkStatus ?  <Tag color="success">合格</Tag> :  <Tag color="warning">审核中</Tag>}
-                            </Space>
-                            <Space>
-                                <Button type="link" style={{ padding: 0 }}>下载</Button>
-                                {/* <Button type="link" style={{ padding: 0 }}>详情</Button> */}
-                            </Space>
-                        </Flex>
-                    </Card>
-                })}
-            </div>)}
-        </>
-    }, [rowNum, size, dataList])
-
-    const loadMoreData = () => {
-        console.log('触发了--->')
-        if (dataList.length === total) {
-            return
-        }
-        loadMore?.()
-    }
-
-    return <div className={style.left_root}>
-        <div className={style.left_content} ref={ref}>
-            <InfiniteScroll
-                dataLength={dataList.length}
-                next={loadMoreData}
-                hasMore={dataList.length < total}
-                loader={<div style={{ display: 'flex', marginTop: 10 }}>
-                    {Array(rowNum).fill('').map((item, index) => <div key={index} className={style.content_row} style={{ width: (1 / rowNum * 100) + '%', padding: '0 5px', boxSizing: 'border-box' }}>
-                        <Skeleton.Input active style={{ width: '100%' }} />
-                    </div>)}
-                </div>}
-                endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
-                scrollableTarget={id}
-                // height={height}
-            >
-                <div className={style.memo_content}>
-                    {content}
-                </div>
-            </InfiniteScroll>
-        </div>
-        {loading && <div className={style.content_spin}><Spin /></div>}
-    </div>
-}
-
-export default React.memo(OpusRoll)
+import { downloadEscalationApi } from '@/services/task-api/download';
+import { MaterialSourceEnum } from '@/utils/constEnum';
+import { useRequest, useSize } from 'ahooks';
+import { Button, Card, Divider, Flex, Skeleton, Space, Spin, Tag, Typography } from 'antd';
+import React, { useEffect, useMemo, useRef, useState } from 'react';
+import InfiniteScroll from 'react-infinite-scroll-component';
+import VideoOpus from './components/VideoOpus';
+import style from './index.less';
+
+interface Props {
+  data: any[];
+  total: number;
+  loading?: boolean;
+  loadMore?: () => void;
+  flexNum?: number;
+  id?: string;
+}
+
+const OpusRoll: React.FC<Props> = ({
+  loading,
+  data,
+  total,
+  loadMore,
+  flexNum = 0,
+  id = 'scrollableDiv',
+}) => {
+  /************************/
+  const ref = useRef<HTMLDivElement>(null);
+  const size = useSize(ref);
+  const [rowNum, setRowNum] = useState<number>(flexNum);
+  const [dataList, setDataList] = useState<any[]>([]);
+  const downloadEscalation = useRequest(downloadEscalationApi, { manual: true });
+  /************************/
+
+  useEffect(() => {
+    setDataList(data || []);
+  }, [data]);
+
+  // 根据内容宽度计算列数
+  useEffect(() => {
+    if (size?.width && !flexNum) {
+      let rowNum = Math.floor((size?.width - 26) / 260);
+      setRowNum(rowNum || 1);
+    }
+  }, [size?.width]);
+
+  const download = (url: string, userMaterialId: number) => {
+    downloadEscalation.runAsync({ userMaterialId }).then(() => {
+      const fileName = 'downloaded.mp4'; // 可以自定义下载的文件名
+      let link = url;
+      let x = new XMLHttpRequest();
+      x.open('GET', link, true);
+      x.responseType = 'blob';
+      x.onload = () => {
+        let url = window.URL.createObjectURL(x.response);
+        let a = document.createElement('a');
+        a.href = url;
+        a.download = fileName;
+        a.click();
+      };
+      x.send();
+    });
+  };
+
+  const content = useMemo(() => {
+    let sourceData: any[] = [];
+    dataList.forEach((item, index) => {
+      sourceData[index % rowNum]
+        ? sourceData[index % rowNum]?.push(item)
+        : (sourceData[index % rowNum] = [item]);
+    });
+
+    // 不足
+    if (sourceData.length < rowNum) {
+      let fewNum = rowNum - sourceData.length;
+      let newData = Array(fewNum).fill([]);
+      sourceData = [...sourceData, ...newData];
+    }
+    return (
+      <>
+        {sourceData.map((item: any[], index) => (
+          <div
+            key={index}
+            className={style.content_row}
+            style={{ width: (1 / rowNum) * 100 + '%' }}
+          >
+            {item?.map((list: any) => {
+              const { size } = JSON.parse(list.materialClaimJson);
+              return (
+                <Card
+                  className={style.left_content_card}
+                  key={list.id}
+                  bodyStyle={{ padding: 10, position: 'relative' }}
+                  headStyle={{ padding: '5px 10px' }}
+                  cover={
+                    <div className={style.left_content_caover}>
+                      <VideoOpus src={list.link} />
+                    </div>
+                  }
+                  title={
+                    <Space direction="vertical" size={0} style={{ width: '100%' }}>
+                      <Typography.Title style={{ margin: 0, fontSize: 15 }} level={5} ellipsis>
+                        {list.taskName}
+                      </Typography.Title>
+                      <Typography.Text type="secondary" style={{ fontSize: 13 }}>
+                        {list.createTime}
+                      </Typography.Text>
+                    </Space>
+                  }
+                >
+                  <Flex justify="space-between">
+                    <Typography.Text>
+                      素材尺寸:{size[0]}*{size[1]}
+                    </Typography.Text>
+                    <Typography.Text>
+                      素材大小:{(list.size / 1024 / 1024).toFixed(2)}M
+                    </Typography.Text>
+                  </Flex>
+                  <Flex justify="space-between">
+                    <Typography.Text>素材ID:{list.taskId}</Typography.Text>
+                    {!list.cost && list.cost !== 0 ? null : (
+                      <Typography.Text>消耗:{list.cost}</Typography.Text>
+                    )}
+                  </Flex>
+                  <Flex justify="space-between">
+                    <Space size={0}>
+                      <Tag color="#55acee">{(MaterialSourceEnum as any)[list.materialSource]}</Tag>
+                      {list.checkStatus ? (
+                        <Tag color="success">合格</Tag>
+                      ) : (
+                        <Tag color="warning">审核中</Tag>
+                      )}
+                    </Space>
+                    <Space>
+                      <Button
+                        type="link"
+                        style={{ padding: 0 }}
+                        onClick={() => download(list.link, 1)}
+                      >
+                        下载
+                      </Button>
+                      {/* <Button type="link" style={{ padding: 0 }}>详情</Button> */}
+                    </Space>
+                  </Flex>
+                </Card>
+              );
+            })}
+          </div>
+        ))}
+      </>
+    );
+  }, [rowNum, size, dataList]);
+
+  const loadMoreData = () => {
+    console.log('触发了--->');
+    if (dataList.length === total) {
+      return;
+    }
+    loadMore?.();
+  };
+
+  return (
+    <div className={style.left_root}>
+      <div className={style.left_content} ref={ref}>
+        <InfiniteScroll
+          dataLength={dataList.length}
+          next={loadMoreData}
+          hasMore={dataList.length < total}
+          loader={
+            <div style={{ display: 'flex', marginTop: 10 }}>
+              {Array(rowNum)
+                .fill('')
+                .map((item, index) => (
+                  <div
+                    key={index}
+                    className={style.content_row}
+                    style={{
+                      width: (1 / rowNum) * 100 + '%',
+                      padding: '0 5px',
+                      boxSizing: 'border-box',
+                    }}
+                  >
+                    <Skeleton.Input active style={{ width: '100%' }} />
+                  </div>
+                ))}
+            </div>
+          }
+          endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
+          scrollableTarget={id}
+          // height={height}
+        >
+          <div className={style.memo_content}>{content}</div>
+        </InfiniteScroll>
+      </div>
+      {loading && (
+        <div className={style.content_spin}>
+          <Spin />
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default React.memo(OpusRoll);

+ 253 - 145
src/pages/Task/index.tsx

@@ -1,145 +1,253 @@
-import Material from "@/components/Material";
-import { MaterialSourceEnum, MaterialTypeEle, StatusEnum, TaskStatusEle, TaskTypeEle, TaskTypeEnum } from "@/const"
-import { getTaskMemberListApi } from "@/services/task-api/task";
-import { AlertOutlined, SearchOutlined } from "@ant-design/icons"
-import { PageContainer } from "@ant-design/pro-components"
-import { useRequest, useSize } from "ahooks";
-import { Affix, Alert, Avatar, Badge, Button, Card, Flex, Input, List, Select, Space, Tag, Tooltip, Typography, theme } from "antd"
-import { useEffect, useRef, useState } from "react";
-import SubmitTask from "./submitTask";
-
-/**
- * 任务中心
- * @returns 
- */
-const Task: React.FC = () => {
-
-    /***********************************/
-    const { useToken } = theme;
-    const ref = useRef(null);
-    const size = useSize(ref)
-    const { token } = useToken();
-    const [queryForm, setQueryForm] = useState<TASKAPI.TaskMemberList>({ pageNum: 1, pageSize: 10 })
-    const getTaskMemberList = useRequest(getTaskMemberListApi, { manual: true })//添加请求
-    /***********************************/
-
-    useEffect(() => {
-        let data = JSON.parse(JSON.stringify(queryForm))
-        if (data?.taskType === 'taskType') {
-            delete data.taskType
-        }
-        if (data?.status === 'status') {
-            delete data.status
-        }
-        getTaskMemberList.run(data)
-    }, [queryForm])
-
-    return <PageContainer>
-        <Card
-            style={{
-                borderRadius: 8,
-            }}
-            bodyStyle={{
-                paddingTop: 0,
-                paddingLeft: 0,
-                paddingRight: 0
-            }}
-            ref={ref}
-        >
-            <Space direction="vertical" style={{ width: '100%' }} size={0}>
-
-                <Affix offsetTop={56}>
-                    <div style={{ backgroundColor: token.colorBgBase, padding: token.paddingContentHorizontalLG, paddingBottom: 5, borderRadius: 8, borderBottom: `1px solid ${token.colorBorderSecondary}` }}>
-                        <Space direction="vertical" style={{ width: '100%' }}>
-                            <Space wrap>
-                                <Input prefix={<SearchOutlined />} value={queryForm?.name} onChange={(e) => setQueryForm({ ...queryForm, name: e.target.value, pageNum: 1 })} placeholder="搜索你感兴趣的悬赏任务" style={{ width: 300 }} />
-                                <Alert
-                                    style={{ padding: `4px 12px` }}
-                                    type="warning"
-                                    showIcon
-                                    icon={<AlertOutlined />}
-                                    message="官方公告:点击任务内的素材上传按钮完成任务,即有机会获取丰厚的奖励!"
-                                />
-                            </Space>
-                            <Space>
-                                <Select
-                                    className="drawSelect"
-                                    style={{ width: 100, padding: 0 }}
-                                    bordered={false}
-                                    allowClear
-                                    placeholder="任务分类"
-                                    value={queryForm?.taskType}
-                                    onChange={(e) => setQueryForm({ ...queryForm, taskType: e, pageNum: 1 })}
-                                    options={Object.keys(TaskTypeEnum).map(key => ({ value: key, label: (TaskTypeEnum as any)[key] }))}
-                                />
-                                <Select
-                                    className="drawSelect"
-                                    style={{ width: 100, padding: 0 }}
-                                    bordered={false}
-                                    allowClear
-                                    value={queryForm?.status}
-                                    placeholder="任务状态"
-                                    onChange={(e) => setQueryForm({ ...queryForm, status: e, pageNum: 1 })}
-                                    options={Object.keys(StatusEnum).map(key => ({ value: key, label: (StatusEnum as any)[key] }))}
-                                />
-                            </Space>
-                        </Space>
-                    </div>
-                </Affix>
-
-                <List
-                    itemLayout="vertical"
-                    size="large"
-                    dataSource={getTaskMemberList.data?.data?.records}
-                    loading={getTaskMemberList.loading}
-                    pagination={{
-                        onChange: (page) => {
-                            console.log(page);
-                            setQueryForm({ ...queryForm, pageNum: page })
-                        },
-                        current: queryForm.pageNum,
-                        pageSize: queryForm.pageSize,
-                    }}
-                    renderItem={(item: any, index) => (
-                        <List.Item
-                            key={item.title}
-                            extra={<Flex gap={20} style={{ height: '100%' }}>
-                                <Flex style={{ width: size?.width ? `${size?.width / 2 - 265}px` : 300 }} vertical={true} align="flex-start" justify="space-between">
-                                    <Material items={item?.materialExample?.split(',')} resourceUrl={item.materialResource} claimJson={item.materialClaimJson} />
-                                </Flex>
-                                <Flex style={{ width: 165, height: '100%' }} gap={10} vertical={true} align="center" justify="center">
-                                    {TaskStatusEle[item.status]}
-                                    <Tag color="error">结算:{item.checkoutType === 'CHECKOUT_TYPE_SCALE' ? `总消耗 ${item.checkout * 100}%` : `${item.checkout}元(审核合格)`}</Tag>
-                                    <SubmitTask taskId={item.id} name={item.name} materialType={item.materialType} materialClaimJson={item.materialClaimJson} onChange={() => getTaskMemberList.refresh()}/>
-                                </Flex>
-                            </Flex>}
-                            actions={[
-                                <Tooltip title='发布时间'><span>{item.createTime}</span></Tooltip>,
-                                <Tooltip title='任务分类'>{TaskTypeEle[item.taskType]}</Tooltip>,
-                                <Tooltip title='素材类型'>{MaterialTypeEle[item.materialType]}</Tooltip>,
-                                <Tooltip title='素材来源要求'><Tag>{(MaterialSourceEnum as any)[item.materialSource]}</Tag></Tooltip>
-                            ]}
-                        >
-                            <List.Item.Meta
-                                avatar={<Avatar src={item.avatar || `https://xsgames.co/randomusers/avatar.php?g=pixel&key=${index}`} style={{ backgroundColor: '#87d068' }} />}
-                                title={<Space size={20}>
-                                    <Typography.Title level={5} style={{ marginBottom: 0 }}>{item.name}</Typography.Title>
-                                    {!!item.urgency && <Space size={4}><span style={{ color: 'red' }}>置顶</span>🆙</Space>}
-                                </Space>}
-                                description={<Space direction="vertical" style={{ width: '100%' }} size={0}>
-                                    {/* 有效时间 */}
-                                    <Typography.Text type="secondary" ellipsis>任务有效时间:{item.startTime}~{item.endTime ? item.endTime : '长期有效'}</Typography.Text>
-                                    {/* 任务描述 */}
-                                    {item.remark && <Typography.Text type="secondary" ellipsis={{ tooltip: true }}>{item.remark}</Typography.Text>}
-                                </Space>}
-                            />
-                            <Typography.Paragraph ellipsis={{ rows: 3, tooltip: true }} style={{ marginBottom: 0 }}>{item.remarkMore}</Typography.Paragraph>
-                        </List.Item>
-                    )}
-                />
-            </Space>
-        </Card>
-    </PageContainer>
-}
-
-export default Task
+import Material from '@/components/Material';
+import { getTaskMemberListApi } from '@/services/task-api/task';
+import {
+  MaterialSourceEnum,
+  MaterialTypeEle,
+  StatusEnum,
+  TaskStatusEle,
+  TaskTypeEle,
+  TaskTypeEnum,
+} from '@/utils/constEnum';
+import { AlertOutlined, SearchOutlined } from '@ant-design/icons';
+import { PageContainer } from '@ant-design/pro-components';
+import { useRequest, useSize } from 'ahooks';
+import {
+  Affix,
+  Alert,
+  Avatar,
+  Card,
+  Flex,
+  Input,
+  List,
+  Select,
+  Space,
+  Tag,
+  theme,
+  Tooltip,
+  Typography,
+} from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import SubmitTask from './submitTask';
+
+/**
+ * 任务中心
+ * @returns
+ */
+const Task: React.FC = () => {
+  /***********************************/
+  const { useToken } = theme;
+  const ref = useRef(null);
+  const size = useSize(ref);
+  const { token } = useToken();
+  const [queryForm, setQueryForm] = useState<TASKAPI.TaskMemberList>({ pageNum: 1, pageSize: 10 });
+  const getTaskMemberList = useRequest(getTaskMemberListApi, { manual: true }); //添加请求
+  /***********************************/
+
+  useEffect(() => {
+    let data = JSON.parse(JSON.stringify(queryForm));
+    if (data?.taskType === 'taskType') {
+      delete data.taskType;
+    }
+    if (data?.status === 'status') {
+      delete data.status;
+    }
+    getTaskMemberList.run(data);
+  }, [queryForm]);
+
+  return (
+    <PageContainer>
+      <Card
+        style={{
+          borderRadius: 8,
+        }}
+        bodyStyle={{
+          paddingTop: 0,
+          paddingLeft: 0,
+          paddingRight: 0,
+        }}
+        ref={ref}
+      >
+        <Space direction="vertical" style={{ width: '100%' }} size={0}>
+          <Affix offsetTop={56}>
+            <div
+              style={{
+                backgroundColor: token.colorBgBase,
+                padding: token.paddingContentHorizontalLG,
+                paddingBottom: 5,
+                borderRadius: 8,
+                borderBottom: `1px solid ${token.colorBorderSecondary}`,
+              }}
+            >
+              <Space direction="vertical" style={{ width: '100%' }}>
+                <Space wrap>
+                  <Input
+                    prefix={<SearchOutlined />}
+                    value={queryForm?.name}
+                    onChange={(e) =>
+                      setQueryForm({ ...queryForm, name: e.target.value, pageNum: 1 })
+                    }
+                    placeholder="搜索你感兴趣的悬赏任务"
+                    style={{ width: 300 }}
+                  />
+                  <Alert
+                    style={{ padding: `4px 12px` }}
+                    type="warning"
+                    showIcon
+                    icon={<AlertOutlined />}
+                    message="官方公告:点击任务内的素材上传按钮完成任务,即有机会获取丰厚的奖励!"
+                  />
+                </Space>
+                <Space>
+                  <Select
+                    className="drawSelect"
+                    style={{ width: 100, padding: 0 }}
+                    bordered={false}
+                    allowClear
+                    placeholder="任务分类"
+                    value={queryForm?.taskType}
+                    onChange={(e) => setQueryForm({ ...queryForm, taskType: e, pageNum: 1 })}
+                    options={Object.keys(TaskTypeEnum).map((key) => ({
+                      value: key,
+                      label: (TaskTypeEnum as any)[key],
+                    }))}
+                  />
+                  <Select
+                    className="drawSelect"
+                    style={{ width: 100, padding: 0 }}
+                    bordered={false}
+                    allowClear
+                    value={queryForm?.status}
+                    placeholder="任务状态"
+                    onChange={(e) => setQueryForm({ ...queryForm, status: e, pageNum: 1 })}
+                    options={Object.keys(StatusEnum).map((key) => ({
+                      value: key,
+                      label: (StatusEnum as any)[key],
+                    }))}
+                  />
+                </Space>
+              </Space>
+            </div>
+          </Affix>
+
+          <List
+            itemLayout="vertical"
+            size="large"
+            dataSource={getTaskMemberList.data?.data?.records}
+            loading={getTaskMemberList.loading}
+            pagination={{
+              onChange: (page) => {
+                console.log(page);
+                setQueryForm({ ...queryForm, pageNum: page });
+              },
+              current: queryForm.pageNum,
+              pageSize: queryForm.pageSize,
+            }}
+            renderItem={(item: any, index) => (
+              <List.Item
+                key={item.title}
+                extra={
+                  <Flex gap={20} style={{ height: '100%' }}>
+                    <Flex
+                      style={{ width: size?.width ? `${size?.width / 2 - 265}px` : 300 }}
+                      vertical={true}
+                      align="flex-start"
+                      justify="space-between"
+                    >
+                      <Material
+                        items={item?.materialExample?.split(',')}
+                        resourceUrl={item.materialResource}
+                        claimJson={item.materialClaimJson}
+                      />
+                    </Flex>
+                    <Flex
+                      style={{ width: 165, height: '100%' }}
+                      gap={10}
+                      vertical={true}
+                      align="center"
+                      justify="center"
+                    >
+                      {TaskStatusEle[item.status]}
+                      <Tag color="error">
+                        结算:
+                        {item.checkoutType === 'CHECKOUT_TYPE_SCALE'
+                          ? `总消耗 ${item.checkout * 100}%`
+                          : `${item.checkout}元(审核合格)`}
+                      </Tag>
+                      <SubmitTask
+                        taskId={item.id}
+                        name={item.name}
+                        materialType={item.materialType}
+                        materialClaimJson={item.materialClaimJson}
+                        onChange={() => getTaskMemberList.refresh()}
+                      />
+                    </Flex>
+                  </Flex>
+                }
+                actions={[
+                  <Tooltip title="发布时间" key={1}>
+                    <span>{item.createTime}</span>
+                  </Tooltip>,
+                  <Tooltip title="任务分类" key={2}>
+                    {TaskTypeEle[item.taskType]}
+                  </Tooltip>,
+                  <Tooltip title="素材类型" key={3}>
+                    {MaterialTypeEle[item.materialType]}
+                  </Tooltip>,
+                  <Tooltip title="素材来源要求" key={4}>
+                    <Tag>{(MaterialSourceEnum as any)[item.materialSource]}</Tag>
+                  </Tooltip>,
+                ]}
+              >
+                <List.Item.Meta
+                  avatar={
+                    <Avatar
+                      src={
+                        item.avatar ||
+                        `https://xsgames.co/randomusers/avatar.php?g=pixel&key=${index}`
+                      }
+                      style={{ backgroundColor: '#87d068' }}
+                    />
+                  }
+                  title={
+                    <Space size={20}>
+                      <Typography.Title level={5} style={{ marginBottom: 0 }}>
+                        {item.name}
+                      </Typography.Title>
+                      {!!item.urgency && (
+                        <Space size={4}>
+                          <span style={{ color: 'red' }}>置顶</span>🆙
+                        </Space>
+                      )}
+                    </Space>
+                  }
+                  description={
+                    <Space direction="vertical" style={{ width: '100%' }} size={0}>
+                      {/* 有效时间 */}
+                      <Typography.Text type="secondary" ellipsis>
+                        任务有效时间:{item.startTime}~{item.endTime ? item.endTime : '长期有效'}
+                      </Typography.Text>
+                      {/* 任务描述 */}
+                      {item.remark && (
+                        <Typography.Text type="secondary" ellipsis={{ tooltip: true }}>
+                          {item.remark}
+                        </Typography.Text>
+                      )}
+                    </Space>
+                  }
+                />
+                <Typography.Paragraph
+                  ellipsis={{ rows: 3, tooltip: true }}
+                  style={{ marginBottom: 0 }}
+                >
+                  {item.remarkMore}
+                </Typography.Paragraph>
+              </List.Item>
+            )}
+          />
+        </Space>
+      </Card>
+    </PageContainer>
+  );
+};
+
+export default Task;

+ 129 - 112
src/pages/Task/submitTask.tsx

@@ -1,112 +1,129 @@
-import { MaterialTypeEnum } from "@/const"
-import { App, Button, Form, Modal, Popconfirm, Space, message } from "antd"
-import React, { useMemo, useState } from "react"
-import Video from "./video"
-import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
-import { useRequest } from "ahooks"
-import { addMaterialApi } from "@/services/task-api/task"
-
-interface Props {
-    taskId: number,
-    name: string
-    materialType: MaterialTypeEnum,
-    materialClaimJson: string
-    onChange?: () => void
-}
-/**
- * 提交素材
- * @returns 
- */
-const SubmitTask: React.FC<Props> = ({ taskId, name, materialType, materialClaimJson, onChange }) => {
-
-    /*********************************/
-    const [form] = Form.useForm<{ materialBeanList: TASKAPI.MaterialBean[] }>()
-    const [visible, setVisible] = useState<boolean>(false)
-    const addMaterial = useRequest(addMaterialApi, { manual: true })
-    /*********************************/
-
-    const hanldeOk = async () => {
-        form.submit()
-        let data = await form.validateFields()
-        console.log(data)
-        addMaterial.runAsync({ ...data, taskId, materialType }).then(res => {
-            if (res?.data) {
-                message.success('提交成功')
-                setVisible(false)
-                form.resetFields()
-                onChange?.()
-            }
-        })
-    }
-
-    const typeEle = useMemo(() => {
-        switch (materialType) {
-            // 视频提交
-            case 'MATERIAL_TYPE_VIDEO' as MaterialTypeEnum:
-                return <Video materialClaimJson={materialClaimJson} />
-            default:
-                return null
-        }
-    }, [materialType, materialClaimJson])
-
-    return <>
-        <Button type="primary" onClick={() => { setVisible(true) }}>提交素材</Button>
-        {visible && <Modal
-            title={`提交${(MaterialTypeEnum as any)[materialType]}素材到 ${name}`}
-            open={visible}
-            onCancel={() => setVisible(false)}
-            onOk={hanldeOk}
-        >
-            <Form
-                name="submitTaskModal"
-                form={form}
-                initialValues={{
-                    materialBeanList: [
-                        {}
-                        // {
-                        //     md5: '9c7a0c1350f9d096b2fdfae1b38051f0',
-                        //     size: 10005155,
-                        //     url: "https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/video/14A37EB1B19641CEB337DBBBC58A7B60.mp4"
-
-                        // }, {
-                        //     md5: "b18584adcedff777f2c35685390209af",
-                        //     size: 2300583,
-                        //     url: "https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/video/1616F0AB21DE4F088D70A12FBB3F6C23.mp4"
-                        // }
-                    ]
-                }}
-            >
-                <Form.List name="materialBeanList">
-                    {(fields, { add, remove }) => {
-                        return <>
-                            {fields.map(({ key, name, ...restField }) => (
-                                <Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="center">
-                                    <Form.Item
-                                        {...restField}
-                                        name={[name]}
-                                        rules={[{ required: true, message: '请上传素材' }]}
-                                    >
-                                        {typeEle}
-                                    </Form.Item>
-                                    {fields?.length > 1 && <Popconfirm
-                                        title="确定删除?"
-                                        onConfirm={() => remove(name)}
-                                    >
-                                        <MinusCircleOutlined style={{ color: 'red' }} />
-                                    </Popconfirm>}
-                                </Space>
-                            ))}
-                            <Form.Item>
-                                <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
-                                    新增素材
-                                </Button>
-                            </Form.Item>
-                        </>
-                    }}
-                </Form.List>
-            </Form>
-        </Modal>}
-    </>
-}
-
-export default React.memo(SubmitTask)
+import { addMaterialApi } from '@/services/task-api/task';
+import { MaterialTypeEnum } from '@/utils/constEnum';
+import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
+import { useRequest } from 'ahooks';
+import { Button, Form, message, Modal, Popconfirm, Space } from 'antd';
+import React, { useMemo, useState } from 'react';
+import Video from './video';
+
+interface Props {
+  taskId: number;
+  name: string;
+  materialType: MaterialTypeEnum;
+  materialClaimJson: string;
+  onChange?: () => void;
+}
+/**
+ * 提交素材
+ * @returns
+ */
+const SubmitTask: React.FC<Props> = ({
+  taskId,
+  name,
+  materialType,
+  materialClaimJson,
+  onChange,
+}) => {
+  /*********************************/
+  const [form] = Form.useForm<{ materialBeanList: TASKAPI.MaterialBean[] }>();
+  const [visible, setVisible] = useState<boolean>(false);
+  const addMaterial = useRequest(addMaterialApi, { manual: true });
+  /*********************************/
+
+  const hanldeOk = async () => {
+    form.submit();
+    let data = await form.validateFields();
+    console.log(data);
+    addMaterial.runAsync({ ...data, taskId, materialType }).then((res) => {
+      if (res?.data) {
+        message.success('提交成功');
+        setVisible(false);
+        form.resetFields();
+        onChange?.();
+      }
+    });
+  };
+
+  const typeEle = useMemo(() => {
+    switch (materialType) {
+      // 视频提交
+      case 'MATERIAL_TYPE_VIDEO' as MaterialTypeEnum:
+        return <Video materialClaimJson={materialClaimJson} />;
+      default:
+        return null;
+    }
+  }, [materialType, materialClaimJson]);
+
+  return (
+    <>
+      <Button
+        type="primary"
+        onClick={() => {
+          setVisible(true);
+        }}
+      >
+        提交素材
+      </Button>
+      {visible && (
+        <Modal
+          title={`提交${(MaterialTypeEnum as any)[materialType]}素材到 ${name}`}
+          open={visible}
+          onCancel={() => setVisible(false)}
+          onOk={hanldeOk}
+        >
+          <Form
+            name="submitTaskModal"
+            form={form}
+            initialValues={{
+              materialBeanList: [
+                {},
+                // {
+                //     md5: '9c7a0c1350f9d096b2fdfae1b38051f0',
+                //     size: 10005155,
+                //     url: "https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/video/14A37EB1B19641CEB337DBBBC58A7B60.mp4"
+
+                // }, {
+                //     md5: "b18584adcedff777f2c35685390209af",
+                //     size: 2300583,
+                //     url: "https://zx-material-center-test.oss-cn-hangzhou.aliyuncs.com/video/1616F0AB21DE4F088D70A12FBB3F6C23.mp4"
+                // }
+              ],
+            }}
+          >
+            <Form.List name="materialBeanList">
+              {(fields, { add, remove }) => {
+                return (
+                  <>
+                    {fields.map(({ key, name, ...restField }) => (
+                      <Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="center">
+                        <Form.Item
+                          {...restField}
+                          name={[name]}
+                          rules={[{ required: true, message: '请上传素材' }]}
+                        >
+                          {typeEle}
+                        </Form.Item>
+                        {fields?.length > 1 && (
+                          <Popconfirm title="确定删除?" onConfirm={() => remove(name)}>
+                            <MinusCircleOutlined style={{ color: 'red' }} />
+                          </Popconfirm>
+                        )}
+                      </Space>
+                    ))}
+                    <Form.Item>
+                      <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
+                        新增素材
+                      </Button>
+                    </Form.Item>
+                  </>
+                );
+              }}
+            </Form.List>
+          </Form>
+        </Modal>
+      )}
+    </>
+  );
+};
+
+export default React.memo(SubmitTask);

+ 78 - 0
src/utils/constEnum.tsx

@@ -0,0 +1,78 @@
+import { Badge, Tag } from 'antd';
+
+/**
+ * 任务分类枚举
+ */
+export enum TaskTypeEnum {
+  TASK_TYPE_GAME = '游戏',
+  TASK_TYPE_NOVEL = '小说',
+  TASK_TYPE_SHORT_PLAY = '短剧',
+}
+
+export const TaskTypeEle: any = {
+  TASK_TYPE_GAME: <Tag color="success">游戏</Tag>,
+  TASK_TYPE_NOVEL: <Tag color="processing">小说</Tag>,
+  TASK_TYPE_SHORT_PLAY: <Tag color="error">短剧</Tag>,
+};
+
+/**
+ * 紧急度
+ */
+export const UrgencyEnum = {
+  '0': '普通',
+  '1': '置顶',
+};
+
+/**
+ * 任务状态
+ */
+export enum StatusEnum {
+  STATUS_NORMAL = '正常',
+  STATUS_EXPIRE = '失效',
+}
+export const TaskStatusEle: any = {
+  STATUS_NORMAL: <Badge status="success" text="正常" />,
+  STATUS_EXPIRE: <Badge status="error" text="失效" />,
+};
+
+/**
+ * 素材类型枚举
+ */
+export enum MaterialTypeEnum {
+  // MATERIAL_TYPE_SINGLE_PICTURE = '单图',
+  // MATERIAL_TYPE_GROUP_PICTURE = '组图',
+  // MATERIAL_TYPE_VOICE = '音频',
+  MATERIAL_TYPE_VIDEO = '视频',
+}
+
+export const MaterialTypeEle: any = {
+  MATERIAL_TYPE_SINGLE_PICTURE: <Tag color="#55acee">单图</Tag>,
+  MATERIAL_TYPE_GROUP_PICTURE: <Tag color="#3b5999">组图</Tag>,
+  MATERIAL_TYPE_VOICE: <Tag color="#cd201f">音频</Tag>,
+  MATERIAL_TYPE_VIDEO: <Tag color="#55acee">视频</Tag>,
+};
+
+/**
+ * 素材来源要求枚举
+ */
+export enum MaterialSourceEnum {
+  MATERIAL_SOURCE_ORIGINAL = '原创',
+}
+
+/**
+ * 素材比例枚举
+ */
+export enum RatioEnum {
+  ONE_ONE = '1:1',
+  FOUR_THREE = '4:3',
+  SIXTEEN_NINE = '16:9',
+  NINE_SIXTEEN = '9:16',
+}
+
+/**
+ * 结算类型
+ */
+export enum CheckoutTypeEnum {
+  CHECKOUT_TYPE_SCALE = '消耗比例',
+  CHECKOUT_TYPE_AMOUNT = '任务',
+}