1.8.3 • Published 3 years ago

kpsapi v1.8.3

Weekly downloads
-
License
ISC
Repository
-
Last release
3 years ago

KPS SG api document

1. Reference

1/1 Shotgun Api Document

https://developer.shotgunsoftware.com/rest-api/?javascript

1/2 Introduction

在./src/index.js 中必需配置服务器信息

//./src/.index.js
import kpsapi from 'kpsapi'

kpsapi.config = {
  //全部必填实际信息
  grant_type: '',
  client_id: '',
  client_secret: '',
  host: '',
  hostApi: '',
}

React.Api = kpsapi //可选绑定至React,省略每个引入

//在其它项目文件内引入 kpsapi 即可使用其中的 api

2. Read/Create/Update/Delete

entityName 必须属于类似下面 entity type 中的类型,即 shotgun entity 在后台的名字

'Asset',
'Attachment',
'Department',
'EventLogEntry',
'Group',
'HumanUser',
'Note',
'Page',
'Phase',
'PhysicalAsset',
'Project',
'Reply',
'Software',
'Status',
'Step',
'Tag',
'Task',
'Version',
'CustomNonProjectEntity12',
'PermissionRuleSet'
......

2/1 Read

id 查询一个条目示例

kpsapi
  .getEntityFromId({
    entityName: 'Task', //必填
    id: 23844, //必填
    fields: ['id', 'content', 'created_at'], //必填 填入要查询的字段列表
    linkEntities: [
      //选填
      {
        fieldName: 'sg_title_type', //在上面fields中必须写入
        fieldType: 'CustomNonProjectEntity12',
        fields: ['id', 'code', 'sg_title_level'],
        multiple: false, //field 类型是entity or multi-entity
      },
      {
        fieldName: 'tags',
        fieldType: 'Tag',
        fields: ['id', 'name', 'sg_tag_type'],
        multiple: true,
      },
    ], //查询关联entity的详细Field,默认查询只有entity的id和name, [{fieldName:'some_field',fieldType: 'someEntityType', multiple: false/true, fields: ['id', 'code', ...]}], 注意linkEntities次级查询总条目数上限5000
  })
  .then((res) => {
    console.log('res', res)
  })

查询符合条件的一组数据示例

kpsapi.getEntityList({
  entityName: 'HumanUser', //必填
  fields: ['id', 'name', 'sg_title_type', 'department', 'tags'], //选填 填入要查询的字段列表
  filters: [//必填,可以是array或hash形式,hash形式示例见下个示例
      ['sg_status_list', 'is', 'act'],
      ['name', 'contains', 'li'],
    ],
  options: { sort: '-created_at', number: 1, size: 500 }, //选填 使用方式见上方kpsapi.getEntityFromId()
  linkEntities: [] //选填 使用方式见上方kpsapi.getEntityFromId()
  totalCount = true, //选填 是否返回符合条件的总条目数,分页时使用
}).then((res) => {
  console.log('res', res)
})

筛选 hash 形式示例

kpsapi
  .getEntityList({
    entityName: 'HumanUser',
    fields: ['id', 'name', 'sg_title_type', 'department', 'tags'],
    filters: {
      logical_operator: 'and',
      conditions: [
        ['sg_status_list', 'is', 'act'],
        ['name', 'contains', 'li'],
        //可继续添加复杂条件嵌套
        //{
        //  logical_operator: 'and',
        //  conditions: [
        //    ['name', 'is', 'Demo Project'],
        //    ['sg_status_list', 'is', 'res'],
        //  ],
        //},
      ],
    },
    options: { size: 500 },
  })
  .then((res) => {
    console.log('res', res)
  })

获取统计信息示例

kpsapi
  .getSummarize({
    entityName: 'HumanUser',
    filters: [['sg_status_list', 'is', 'act']],
    fields: [{ field: 'id', type: 'count' }],
    grouping: [{ field: 'sg_asset_type', type: 'exact', direction: 'asc' }],
    //or 不分组grouping: null
  })
  .then((res) => {
    console.log('res', res)
  })

//统计信息 type 类型详见文档
//https://developer.shotgunsoftware.com/rest-api/?javascript#summarize-field-data
//常用:record_count, count, sum, maximum, minimum, average, earliest, latest...

2/2 Create

创建一个条目

kpsapi
  .createEntity({
    entityName: 'Task', //必填
    fields: ['content', 'project', 'id'], //选填 返回的相关字段信息
    variables: {
      //必填 有些entity类型必填project字段
      content: 'task create demo',
      project: { id: 94, type: 'Project' },
      //other_field:value
    },
    linkEntities: [], //选填 返回的关联entity信息
  })
  .then((res) => {
    console.log('res', res)
  })

批量创建条目

kpsapi.batchCreate({
  entityName: 'Task', //必填
  fields: ['content', 'project', 'id'], //选填
  variables: [ //必填 array形式
    {
      content: 'task create demo 0',
      project: { id: 94, type: 'Project' },
      //other_field:value
    },
    {
      content: 'task create demo 1',
      project: { id: 94, type: 'Project' },
      //other_field:value
    },
  ],
  linkEntities:[] //选填 返回的关联entity信息
  options: null //选填
  totalCount: false //选填
}).then((res) => {
  console.log('res', res)
})

2/3 Update

更新一个条目

kpsapi
  .updateEntity({
    entityName: 'Task', //必填
    id: 24347, //必填
    variables: {
      //必填
      content: 'Cool task update',
      project: { id: 94, type: 'Project' },
      //other_field:value
    },
    fields: ['id', 'start_date'],
    linkEntities: [], //选填 返回的关联entity信息
  })
  .then((res) => {
    console.log('res', res)
  })

批量更新条目

kpsapi.batchUpdate({
  entityName: 'Task', //必填
  variables: [ //必填 array形式
    {
      id: 123,
      content: 'Cool task create update 1',
      project: { id: 94, type: 'Project' },
      //other_field:value
    },
    {
      id: 124,
      content: 'Cool task create update 2',
      project: { id: 97, type: 'Project' },
      //other_field:value
    },
  ],
  fields: ['id', 'start_date'],
  linkEntities:[] //选填 返回的关联entity信息
  options: null //选填
  totalCount: false //选填
}).then((res) => {
  console.log('res', res)
})

2/4 Delete

删除一个条目

kpsapi
  .deleteEntity({
    entityName: 'Version',
    id: 43691,
  })
  .then((res) => {
    console.log('res', res)
  })

批量删除条目

kpsapi
  .batchDelete({
    entityName: 'Version',
    ids: [121, 122, 123, 124],
  })
  .then((res) => {
    console.log('res', res)
  })

2/5 Track

建议在每个应用的./src/App.js 中定义全局配置,保证重复信息只输入一次

window.track = (e) => {
  e.channel = 'Application Center' //当前频道名
  e.code = 'Digital Center' //当前应用名
  if (window.userInfo) {
    //在查询到当前用户信息后赋值
    e.userInfo = { type: 'HumanUser', id: parseInt(window.userInfo.id) }
  }
  kpsapi.track(e)
}

在需要的节点传入相应值,调用 window.track(e),某个 track 示例:

window.track({
  page: 'issue management', //以下根据需要选填
  module: 'issue card',
  component: 'add button',
  actionType: 'click',
  actionResult: 'add',
  desc: '',
  projectId: 123,
})

2/6 Tools

使用时直接调用 React.Api.isImage(...)或 kpsapi.isImage()

const specialCode =
  /[(\~)(\!)(\@)(\#)(\$)(\%)(\^)(\&)(\()(\))(\+)(\=)(\[)(\])(\{)(\})(\;)(\')(\,)(\,)]+/g

const fileTypeIcon = {
  ppt: 'https://z3.ax1x.com/2021/08/11/fNDrtK.png',
  pptx: 'https://z3.ax1x.com/2021/08/11/fNDrtK.png',
  doc: 'https://z3.ax1x.com/2021/08/11/fNDOns.png',
  docx: 'https://z3.ax1x.com/2021/08/11/fNDOns.png',
  xls: 'https://z3.ax1x.com/2021/08/11/fNDMmn.png',
  xlsx: 'https://z3.ax1x.com/2021/08/11/fNDMmn.png',
  wire: 'https://z3.ax1x.com/2021/08/11/fNDItf.png',
  igs: 'https://z3.ax1x.com/2021/08/11/fNDUX9.png',
  iges: 'https://z3.ax1x.com/2021/08/11/fNDUX9.png',
  other: 'https://z3.ax1x.com/2021/08/11/fNDw01.png',
  psd: 'https://z3.ax1x.com/2021/08/11/fNDc1e.png',
  ps: 'https://z3.ax1x.com/2021/08/11/fNDc1e.png',
  sketch: 'https://z3.ax1x.com/2021/08/11/fNDWnA.png',
  ai: 'https://z3.ax1x.com/2021/08/11/fNDuOs.png',
  img: 'https://z3.ax1x.com/2021/08/11/fNDdmR.png',
  zip: 'https://z3.ax1x.com/2021/08/11/fNDXBn.png',
  rar: 'https://z3.ax1x.com/2021/08/11/fNDXBn.png',
  video: 'https://z3.ax1x.com/2021/08/11/fNDh7t.png',
  txt: 'https://z3.ax1x.com/2021/08/11/fNDf0I.png',
  stp: 'https://z3.ax1x.com/2021/08/11/fND5AP.png',
  step: 'https://z3.ax1x.com/2021/08/11/fND5AP.png',
  pdf: 'https://z3.ax1x.com/2021/08/11/fND0Tx.png',
  eps: 'https://z3.ax1x.com/2021/08/11/fNDnyj.png',
  '3dm': 'https://z3.ax1x.com/2021/08/11/fNDlT0.png',
  gh: 'https://z3.ax1x.com/2021/08/11/fNDQwq.png',
}

const haveSpecialCode = (name) => {
  return specialCode.test(name)
}

const replaceSpecialCode = (name, toCode = '_') => {
  return name.replace(specialCode, toCode)
}

const isImage = (origUrl) => {
  return /\.(png|tif|tiff|jpg|gif|jpeg|webp)$/i.test(origUrl)
}

const isVideo = (origUrl) => {
  return /\.(mp4|ogg|webm|m4v)$/i.test(origUrl)
}

const isVersionImage = (item) => {
  return (
    item.sg_uploaded_movie &&
    /\.(png|tif|tiff|jpg|gif|jpeg|webp)$/i.test(item.sg_uploaded_movie.name)
  )
}

const isVersionVideo = (item) => {
  return (
    item.sg_uploaded_movie &&
    /\.(mp4|ogg|webm|m4v)$/i.test(item.sg_uploaded_movie.name)
  )
}

const getReplaceThumbnailIcon = (name) => {
  const strList = name.split('.')
  const sub = strList[strList.length - 1].toLowerCase()
  if (strList.length === 1 || Object.keys(fileTypeIcon).indexOf(sub) < 0) {
    return fileTypeIcon.other
  } else {
    return fileTypeIcon[sub]
  }
}

const getVersionThumb = (item) => {
  // sg_thumb_image, sg_trans_image, image, sg_uploaded_movie
  let thumbUrl
  if (!item.sg_uploaded_movie) {
    thumbUrl = fileTypeIcon.other
  } else if (item.sg_thumb_image) {
    thumbUrl = item.sg_thumb_image.url
  } else if (
    item.image &&
    item.image.indexOf('/transient/thumbnail_pending.png') < 0
  ) {
    thumbUrl = item.image
  } else {
    if (isImage(item.sg_uploaded_movie.name)) {
      thumbUrl = fileTypeIcon.img
    } else if (isVideo(item.sg_uploaded_movie.name)) {
      thumbUrl = fileTypeIcon.video
    } else {
      thumbUrl = getReplaceThumbnailIcon(item.sg_uploaded_movie.name)
    }
  }
  return thumbUrl
}

const getVersionThumbType = (item) => {
  let type = 'original'
  if (!item.sg_uploaded_movie) {
    type = 'original'
  } else if (item.sg_thumb_image) {
    type = 'original'
  } else if (
    item.image &&
    item.image.indexOf('/transient/thumbnail_pending.png') < 0
  ) {
    type = 'original'
  } else {
    type = 'icon'
  }
  return type
}

const getVersionShow = (item) => {
  let url
  if (!item.sg_uploaded_movie) {
    url = fileTypeIcon.other
  } else if (item.sg_trans_image) {
    url = item.sg_trans_image.url
  } else {
    url = item.sg_uploaded_movie.url
  }
  return url
}

const isGroupOverlap = (a1, a2, targetKey = 'id') => {
  /*
    a1 = [{id:1, name:1},{id:2, name:2}]
    a2 = [{id:1, name:1},{id:3, name:3}]
    return true
  */
  let isIn = false
  a1.some((e1) => {
    if (a2.findIndex((e2) => e2[targetKey] === e1[targetKey]) > -1) {
      isIn = true
      return true
    }
    return false
  })
  return isIn
}

const isAppPermissioned = (userInfo, appInfo) => {
  // 判断用户是否拥有用户权限,参数是用户信息(含有'userInfo.groups' Fields)和app信息(含有四个权限Fields)
  if (
    appInfo.sg_excluding_people.data.length > 0 &&
    appInfo.sg_excluding_people.data.findIndex((e) => e.id === userInfo.id) >= 0
  ) {
    return false
  }
  if (
    appInfo.sg_including_people.data.length > 0 &&
    appInfo.sg_including_people.data.findIndex((e) => e.id === userInfo.id) >= 0
  ) {
    return true
  }
  if (
    appInfo.sg_excluding_groups.data.length > 0 &&
    isGroupOverlap(userInfo.groups.data, appInfo.sg_excluding_groups.data)
  ) {
    return false
  }
  if (appInfo.sg_including_groups.data.length > 0) {
    return isGroupOverlap(
      userInfo.groups.data,
      appInfo.sg_including_groups.data
    )
  } else {
    return true
  }
}

export const tools = {
  isAppPermissioned,
  isGroupOverlap,
  haveSpecialCode,
  replaceSpecialCode,
  isImage,
  isVideo,
  isVersionImage,
  isVersionVideo,
  getVersionThumb,
  getVersionShow,
  getVersionThumbType,
  getReplaceThumbnailIcon,
}

2/7 Download

kpsapi.downloadFileList({
  entityName: 'Version',
  field: 'sg_uploaded_movie' //entity文件类型的字段
  fileList: [
    {
      name: '1.jpg', //带有实际文件名后缀
      id: 123, //entity的id,不是file的id
    },
    {
      name: '2.jpg',
      id: 124,
    },
  ],
}) //没有返回值

2/8 获取 SG 文件或图片信息

// 获取单个文件内容
kpsapi
  .getFileContent({
    entityName: 'Version', //必填
    fileInfo: {
      id: 123, // entity id
      name: '123.jpg', // file name
    },
    field: 'sg_uploaded_movie', // 文件所在field
  })
  .then((res) => {
    console.log('res', res)
  })

// 获取多个文件内容
kpsapi
  .getFileListContent({
    entityName: 'Version', //必填
    fileList: [
      {
        id: 123, // entity id
        name: '123.jpg', // file name
      },
      {
        id: 125, // entity id
        name: 'hello.jpg', // file name
      },
    ],
    field: 'sg_uploaded_movie', // 文件所在field
  })
  .then((res) => {
    console.log('res', res)
  })

// 特殊接口:获取versions图片缩略图,避免跨域问题
// 使用async/await避免多次异步导致的代码层级显示混乱
const getVersions = async ()=>{
  // 获取versions,含有sg_uploaded_movie, image, sg_thumb_image等fields
  let versionRes = await kpsapi.getEntityList({...})
  // 获取versions图片
  let versions = await kpsapi.getVersionListImage(versionRes.data)
  // 处理结果
  console.log('versions: ',versions)
}
// 对于单个image大图显示,使用getFileContent得到文件信息res后,使用URL.createObjectURL(res.data)获取可直接显示的image data

3. Other Options

3/1. Filter / Search

使用复杂的条件 query

"filters": [field, relation, value(s)]

Array Style

//dmeo1:
"filters": [["name", "contains", "template"]]

//demo2:
"filters": [
    ["name", "contains", "template"],
    ["is_demo", "is", true]
  ]

Hash Style 多组及嵌套

"filters": {
  "logical_operator": "or", //'and'
  "conditions": [
    ["is_demo", "is", true],
    ["sg_status", "in", ["Hold", "Lost"]]
  ]
}

"filters":{
  "logical_operator": "or",
  "conditions": [
    {
      "logical_operator": "and",
      "conditions": [
        ["name", "is", "Demo Project"]
        ["sg_status_list", "is" "res"]
      ]
    },
    ["name", "is", "Game Demo"]
  ]
}

3/2. Operators and Arguments

操作符和对应的参数列表

OperatorArgumentsNotes
'is'field_value / None
'is_not'field_value / None
'less_than'field_valueNone
'greater_than'field_value / None
'contains'field_value / None
'not_contains'field_value / None
'starts_with'string
'ends_with'string
'between'[field_value / None, field_value/ None]
'not_between'[field_value / None, field_value / None]
'in_last'[int, 'HOUR' / 'DAY' / 'WEEK'/ 'MONTH' / 'YEAR']
'in_next'[int, 'HOUR'/ 'DAY' / 'WEEK' / 'MONTH' / 'YEAR']
'in'[field_value / None, ...] # Array of field values
'type_is'string / None # Shotgun entity type
'type_is_not'string / None # Shotgun entity type
'in_calendar_day'int # Offset (e.g. 0 = today, 1 = tomorrow, -1 = yesterday)
'in_calendar_week'int # Offset (e.g. 0 = this week, 1 = next week, -1 = last week)
'in_calendar_month'int # Offset (e.g. 0 = this month, 1 = next month, -1 = last month)
'name_contains'string
'name_not_contains'string
'name_starts_with'string
'name_ends_with'string

3/3. Valid Operators By Data Type

不同类型 Field 识别的操作符

Field typeOperators
addressing'is'/'is_not'/'contains'/'not_contains'/'in'/'type_is' /'type_is_not'/'name_contains' / 'name_not_contains'/'name_starts_with'/'name_ends_with'
checkbox'is'/'is_not'
currency'is'/'is_not'/'less_than'/'greater_than'/'between'/'not_between'/'in'/'not_in'/date/ 'is'/'is_not'/'greater_than'/'less_than'/'in_last'/'not_in_last'/'in_next'/'not_in_next' /'in_calendar_day'/'in_calendar_week'/'in_calendar_month' /'in_calendar_year' /'between'/'in'/'not_in'
date_time'is'/'is_not'/'greater_than'/'less_than'/'in_last'/'not_in_last'/'in_next'/'not_in_next' / 'in_calendar_day'/'in_calendar_week'/'in_calendar_month'/ 'in_calendar_year'/'between'/'in'/'not_in'
duration'is'/'is_not'/'greater_than'/'less_than'/'between'/'in'/'not_in'
entity'is'/'is_not'/'type_is'/'type_is_not'/'name_contains'/'name_not_contains'/'name_is'/'in'/'not_in'
float'is'/'is_not'/'greater_than'/'less_than'/'between'/'in'/'not_in'
image'is'/'is_not' Note: For both 'is' and 'is_not', the only supported value is None, which supports detecting the presence or lack of a thumbnail.
list'is'/'is_not'/'in'/'not_in'
multi_entity'is' ** Note: when used on multi_entity, this functions as you would expect 'contains' to function/ 'is_not'/'type_is'/'type_is_not'/ 'name_contains'/'name_not_contains'/'in'/'not_in'
number'is'/'is_not'/'less_than'/'greater_than'/'between'/'not_between'/'in'/'not_in'
password** Filtering by this data type field not supported
percent'is'/'is_not'/'greater_than'/'less_than'/'between'/'in'/'not_in'
serializable** Filtering by this data type field not supported
status_list'is'/'is_not'/'in'/'not_in'
summary** Filtering by this data type field not supported
text'is'/ is_not'/'contains'/'not_contains'/'starts_with'/'ends_with'/'in'/'not_in'
timecode'is'/'is_not'/'greater_than'/'less_than'/'between'/'in'/'not_in'
url** Filtering by this data type field is not supported

3/4. Data Types

写入不同类型 Field 的数据类型

NameTypeExample/FormatNotes
addressingarray{'type': "HumanUser" or "Group", "id": }, ...
checkboxbooltrue / false
colorstring255,0,0 / pipeline_steppipeline_step indicates the Task color inherits from the Pipeline Step color.
currencyfloat / nullRange: -9999999999999.99 to 9999999999999.99
datestring / nullYYYY-MM-DDYear must be >= 1970
date_timestring / nullYYYY-MM-DDThh:mm:ssZDatetimes must be in the ISO8601 format.
durationint / nullRange: -2147483648 to 2147483647. Length of time, in minutes
entityobject / null{'type': , "id": }
floatfloat / nullRange: -9999999999999.99 to 9999999999999.99
footagestring / nullFF-ffFrames must be < Preferences value for “Advanced > Number of frames per foot of film”> Format matches Preference value for “Formatting > Display of footage fields”. Example above is default.F=Feet f=Frames.
imagestring / null
liststring / null
multi_entityarray{'type': , "id": }, ...
passwordstring / null
numberint / null
serializableobject / null
status_liststring / null
textstring / null
timecodeint / nullRange: -2147483648 to 2147483647. Length of time, in milliseconds (1000 = 1 second)
url (file/link field)object / null

3/5. Updating a multi-entity field

更新某条目的某个属性是 multi entity 的字段,如 task 的 task_owners,version 的 tags

Simple assignment example (setting users to 436, 536)

{
  "users": [
      {
          "type": "HumanUser",
          "id": 436
      },
      {
          "type": "HumanUser",
          "id": 536
      }
  ]
}

Add multi-entity update mode example (adding users 574, 538 to the field)

{
  "users": {
    "multi_entity_update_mode": "add",
    "value": [
      {
          "type": "HumanUser",
          "id": 574
      },
      {
          "type": "HumanUser",
          "id": 538
      }
    ]
  }
}

Remove multi-entity update mode example (removing users 103, 203 from the field)

{
  "users": {
    "multi_entity_update_mode": "remove",
    "value": [
      {
          "type": "HumanUser",
          "id": 103
      },
      {
          "type": "HumanUser",
          "id": 203
      }
    ]
  }
}

Set multi-entity update mode example (setting users to 436, 536)

{
  "users": {
    "multi_entity_update_mode": "set",
    "value": [
      {
          "type": "HumanUser",
          "id": 436
      },
      {
          "type": "HumanUser",
          "id": 536
      }
    ]
  }
}

4. Upload Files

4/1 Upload Api

上传并返回生成的 version 的 id

kpsapi.createUploadVersion({
  file: fileToUpload, //必填
  variables: {
    //必填 version的其它信息
    code: 'upload version',
    project: { type: 'Project', id: 97 },
    //other_filed: value
  },
})

用于直接上传到各 entity 内的 file 类型 field,如已知 version 更新文件

kpsapi.uploadFileToField({
  entityName: 'Version', //必填
  id: 1234, //必填
  field: 'sg_uploaded_movie', //必填 要上传到的字段
  file: fileToUpload, //必填
})

4/2 React Upload Demo

import React, { useState, useEffect } from 'react'
import { Upload, Progress, Badge, Button, message } from 'antd'
import { InboxOutlined } from '@ant-design/icons'

const { Dragger } = Upload

export default function UploadModuleReact(props) {
  const [fileList, setFileList] = useState([])
  const [progressMock, setProgressMock] = useState(0)

  const uploadProps = {
    name: 'file',
    multiple: true,
    showUploadList: false,
    fileList,
    beforeUpload: (file, newAddFileList) => {
      // 如果需要立即上传,在此处拦截
      setFileList([...fileList, ...newAddFileList])
      return false
    },
  }

  const handleUpload = async () => {
    console.log('fileList', fileList)
    let createList = [],
      pMock = 1,
      sizeAll = 0
    fileList.forEach((f) => {
      sizeAll = sizeAll + f.size / 750000
      createList.push(
        React.Api.createUploadVersion({
          file: f,
          variables: {
            //TODO: your version variables here
            project: {
              type: 'Project',
              id: 0,
            },
            code: f.name,
            sg_task: {
              type: 'Task',
              id: 0,
            },
          },
        })
      )
    })
    let mock = setInterval(() => {
      if (pMock <= 99) {
        pMock = parseFloat((pMock + 99 / sizeAll).toFixed(1))
        pMock = Math.min(99, pMock)
        setProgressMock(pMock)
      }
    }, 500)
    let versionRes = await Promise.all(createList)
    console.log('created versions: ', versionRes)
    clearInterval(mock)
    setProgressMock(100)
    setFileList([])
    setTimeout(() => {
      setProgressMock(0)
      message.success('Upload successfully')
    }, 1000)

    // write your finish functions here
    // for images, use 'sg_thumb_image' to show image immediately
    // use 'sg_trans_image' to show image at web page
  }

  return (
    <div
      style={{
        width: '400px',
      }}>
      <Dragger {...uploadProps}>
        <div
          style={{
            height: '68px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}>
          {progressMock > 0 ? (
            <Progress
              percent={progressMock}
              type='circle'
              status='active'
              width={72}
            />
          ) : (
            <Badge count={fileList.length}>
              <InboxOutlined style={{ fontSize: '46px', color: '#40a9ff' }} />
            </Badge>
          )}
        </div>
        <p
          style={{
            margin: ' 0 0 4px',
            color: 'rgba(0, 0, 0, 0.85)',
            fontSize: '16px',
          }}>
          {progressMock === 0
            ? 'Click or drag file here'
            : progressMock === 99
            ? 'Finishing...'
            : 'Click or drag file here'}
        </p>
      </Dragger>
      <Button
        onClick={handleUpload}
        disabled={fileList.length === 0 || progressMock > 0}>
        Start
      </Button>
    </div>
  )
}
1.8.3

3 years ago

1.8.2

3 years ago

1.8.1

3 years ago

1.8.0

3 years ago

1.7.0

3 years ago

1.6.16

3 years ago

1.6.13

3 years ago

1.6.12

3 years ago

1.6.14

3 years ago

1.6.11

3 years ago

1.6.9

3 years ago

1.6.10

3 years ago

1.6.8

3 years ago

1.6.7

3 years ago

1.6.6

3 years ago

1.6.4

3 years ago

1.6.3

3 years ago

1.6.2

3 years ago

1.6.1

3 years ago

1.6.0

3 years ago

1.6.5

3 years ago

1.5.5

3 years ago

1.5.6

3 years ago

1.5.4

3 years ago

1.5.3

3 years ago

1.5.2

3 years ago

1.5.1

3 years ago

1.5.0

3 years ago

1.4.6

3 years ago

1.4.5

3 years ago

1.4.4

3 years ago

1.4.3

3 years ago

1.4.2

3 years ago

1.4.1

3 years ago

1.4.0

3 years ago

1.3.1

3 years ago

1.2.2

3 years ago

1.3.0

3 years ago

1.2.1

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.0.0

3 years ago