2.0.0 • Published 5 months ago

kps-component v2.0.0

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

KPS-Component Api/Components 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 Api, { CorsImage, CorsVideo, ImageViewer } from 'kps-components'

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

const dev = process.env.NODE_ENV === 'development'
if (!dev) {
  Api.trackClient() // 注册追踪页面接口,开发环境下不追踪
}

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 查询一个条目示例

Api.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)
})

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

Api.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 形式示例

Api.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)
})

获取统计信息示例

Api.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

创建一个条目

Api.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)
})

批量创建条目

Api.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

更新一个条目

Api.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)
})

批量更新条目

Api.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

删除一个条目

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

批量删除条目

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

2/5 Track

1. normal 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) }
  }
  Api.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. client track

在 App.js 的初始 useEffect()注册

// ./App.js
// function App(){
useEffect(() => {
  Api.trackClient()
}, [])
// }

2/6 Tools

使用时直接调用 Api.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

// 单个文件下载
Api.downloadFile({
  entityName: 'Version',
  field: 'sg_uploaded_movie' //entity文件类型的字段
  fileList: {
      name: '1.jpg', // 带有实际文件名后缀
      id: 123, // entity的id,不是file的id
    },
}).then(res=>{
  // 成功返回 'success'
}).catch(err=>{
  // 失败返回 'fail'
})

// 多个文件下载
Api.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 文件或图片信息

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

## 3. Other Options

### 3/1. Filter / Search

使用复杂的条件 query

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

Array Style

```js
//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

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

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

Api.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(
        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>
  )
}

COMPONENTS

基于 React 和 Antd

// index.js
// 配置见 文档开头配置处

Image

 跨域图片获取,必包含 info 内的内容

versions.map((v) => (
  <CorsImage
    key={v.id}
    info={{
      id: v.id,
      type: 'Version',
      name: v.sg_uploaded_movie.name, //此键值也可以是code, content, 分别对应version,task等,选一即可
      field: 'sg_thumb_image',
      className: 'hello-image',
    }}
    style={{
      width: '300px',
      height: '200px',
      padding: '10px',
      objectFit: 'auto', // 不填或'auto',则自动判断contain或cover
    }}
  />
))

Video

 跨域图片获取,必包含 info 内的内容

versions.map((v) => (
  <CorsVideo
    key={v.id}
    info={{
      id: v.id,
      type: 'Version',
      name: v.sg_uploaded_movie.name, //此键值也可以是code, content, 分别对应version,task等,选一即可
      field: 'sg_thumb_image',
      className: 'hello-image'

      autoply:'autoplay',
      loop:'loop',
      controls: 'controls'
    }}
    style={{
      width: '300px',
      height: '200px',
    }}
  />
))

ImageViewer

基本的图片列表和看图组件,包含右键下载和删除功能

TODO Update:

  1. 滚轮放大和缩小,左键拖拽
  2. 与父级框选操作冲突问题
import { ImageViewer } from 'kps-component'
...
const versionList = [........]
...
// Basic Demo
versionList.map(v => <ImageViewer
  key={v.id}
  item={v} //必填
  itemList={versionList} //必填
  onDelete={()=>{}} //默认undefined,必填,此处需要更新传入的VersionList

  style={{}} //img wrapper style, 选填
  showName={true} //默认false, 选填
  onCloseViewModal={()=>{}} //默认undefined, 选填
  onClickLeft={()=>{}} //默认undefined, 选填
  onClickRight={()=>{}} //默认undefined, 选填
  onDownload={()=>{}} //默认undefined, 选填
/>)

KpsIcons

kps 2.0 新图标svg icon,使用方式与antd icon相同

图标与与图片对应关系见...shotgun.../page/57145

2.0.0

5 months ago

1.3.35

5 months ago

1.3.36

5 months ago

1.3.32

5 months ago

1.3.33

5 months ago

1.3.31

5 months ago

1.3.30

6 months ago

1.3.19

6 months ago

1.3.20

6 months ago

1.3.21

6 months ago

1.3.24

6 months ago

1.3.25

6 months ago

1.3.22

6 months ago

1.3.23

6 months ago

1.3.28

6 months ago

1.3.29

6 months ago

1.3.26

6 months ago

1.3.27

6 months ago

1.3.17

2 years ago

1.3.18

2 years ago

1.3.15

2 years ago

1.3.16

2 years ago

1.3.14

2 years ago

1.3.10

3 years ago

1.3.13

3 years ago

1.3.11

3 years ago

1.3.12

3 years ago

1.3.9

3 years ago

1.3.7

3 years ago

1.3.6

3 years ago

1.3.5

3 years ago

1.3.4

3 years ago

1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.2.17

3 years ago

1.2.18

3 years ago

1.2.19

3 years ago

1.3.0

3 years ago

1.2.16

3 years ago

1.2.14

3 years ago

1.2.15

3 years ago

1.2.12

3 years ago

1.2.13

3 years ago

1.2.10

3 years ago

1.2.11

3 years ago

1.2.8

3 years ago

1.2.7

3 years ago

1.2.6

3 years ago

1.2.5

3 years ago

1.2.4

3 years ago

1.2.9

3 years ago

1.2.0

3 years ago

1.2.3

3 years ago

1.2.2

3 years ago

1.2.1

3 years ago

1.1.23

3 years ago

1.1.22

3 years ago

1.1.21

3 years ago

1.1.19

3 years ago

1.1.20

3 years ago

1.1.18

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.1.9

3 years ago

1.1.8

3 years ago

1.1.7

3 years ago

1.1.6

3 years ago

1.1.5

3 years ago

1.1.4

3 years ago

1.1.3

3 years ago

1.1.12

3 years ago

1.1.11

3 years ago

1.1.10

3 years ago

1.1.16

3 years ago

1.1.15

3 years ago

1.1.14

3 years ago

1.1.13

3 years ago

1.1.17

3 years ago

1.0.19

3 years ago

1.0.18

3 years ago

1.0.17

3 years ago

1.0.20

3 years ago

1.0.16

3 years ago

1.0.15

3 years ago

1.0.14

3 years ago

1.0.11

3 years ago

1.0.13

3 years ago

1.0.12

3 years ago

1.0.10

3 years ago

1.0.9

3 years ago

1.0.8

3 years ago

1.0.7

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago