Jelajahi Sumber

feat: transcode

Casper Dai 2 tahun lalu
induk
melakukan
679465c525

+ 79 - 4
src/api/asset.js

@@ -1,11 +1,15 @@
-import request, { tenantRequest } from '@/utils/request'
+import request, {
+  tenantRequest,
+  downloadRequest
+} from '@/utils/request.js'
 import {
   State,
   Dataset
-} from '@/constant'
+} from '@/constant.js'
 import {
   getAssetThumb,
-  getAssetDiff
+  getAssetDiff,
+  parseTime
 } from '@/utils'
 import {
   send,
@@ -18,7 +22,30 @@ import {
   confirmAndSend,
   addStatus,
   addTenant
-} from './base'
+} from './base.js'
+
+function download ({ data, headers }, fileName) {
+  const blob = new Blob([data], { type: headers['content-type'] })
+  const url = window.URL.createObjectURL(blob)
+  const dom = document.createElement('a')
+  dom.href = url
+  dom.download = decodeURI(fileName)
+  dom.style.display = 'none'
+  document.body.appendChild(dom)
+  dom.click()
+  dom.parentNode.removeChild(dom)
+  window.URL.revokeObjectURL(url)
+}
+
+export function resourceExport (data) {
+  return send({
+    url: '/minio-data/usage/count/export',
+    method: 'POST',
+    data
+  }, downloadRequest).then(response => {
+    download(response, `资源曝光率${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.zip`)
+  })
+}
 
 export function addAsset (data) {
   return add({
@@ -51,6 +78,8 @@ export function getAssetsByQuery (query) {
     data.forEach(asset => {
       if (asset.status === State.DRAFT) {
         asset.draft = '解析中'
+      } else if (asset.status === State.TRANSCODE_FAILURE) {
+        asset.draft = '转码失败'
       } else {
         const tag = asset.tag
         asset.file = {
@@ -303,6 +332,52 @@ export function getDevicesByDataset (query) {
   })
 }
 
+export function countByDay (query) {
+  return request({
+    url: '/minio-data/usage/countByDay',
+    method: 'GET',
+    params: query
+  })
+}
+
+// 查询媒资执行队列
+export function listActivate (query) {
+  return request({
+    url: '/minio-data/mediaProcessTask/listActivate',
+    method: 'GET',
+    params: query
+  })
+}
+
+export function mediaList (query) {
+  const { pageNum: pageIndex, pageSize, ...params } = query
+  return tenantRequest({
+    url: '/minio-data/mediaProcessTask/pageQuery',
+    method: 'GET',
+    params: addTenant({
+      pageIndex, pageSize,
+      ...params
+    })
+  })
+}
+
+// 转换旧媒资
+export function secondaryTransList (data) {
+  return request({
+    url: '/minio-data/mediaProcessTask/batchSave',
+    method: 'POST',
+    data
+  })
+}
+
+export function treeLocation (data) {
+  return request({
+    url: '/minio-data/treeLocation',
+    method: 'PUT',
+    data
+  })
+}
+
 export function getDatasetByDevice (id) {
   return request({
     url: `/media/dataset/${id}`,

+ 20 - 0
src/constant.js

@@ -31,6 +31,25 @@ export const AssetTypeInfo = {
   [AssetType.STREAMING_MEDIA]: '流媒体'
 }
 
+export const CourseType = {
+  IMAGE: 1,
+  VIDEO: 2,
+  PPT: 3,
+  PDF: 4,
+  DOC: 5,
+  STREAMING_MEDIA: 100
+}
+
+export const CourseTypeInfo = {
+  [CourseType.IMAGE]: '截图解析',
+  [CourseType.VIDEO]: '视频转码',
+  [CourseType.AUDIO]: '音频转码',
+  [CourseType.PPT]: 'PPT转码',
+  [CourseType.PDF]: 'PDF转码',
+  [CourseType.DOC]: 'WORD转码',
+  [CourseType.STREAMING_MEDIA]: '流媒体转码'
+}
+
 export const AssetTag = {
   AD: 1,
   PUBLICITY: 2,
@@ -46,6 +65,7 @@ export const AssetTagInfo = {
 }
 
 export const State = {
+  TRANSCODE_FAILURE: -2,
   DRAFT: -1,
   READY: 0,
   SUBMITTED: 1,

+ 15 - 5
src/layout/components/Navbar/UploadDashboard/FileProgress.vue

@@ -12,12 +12,22 @@
       <div class="l-flex__auto l-flex--col c-sibling-item">
         <div class="l-flex--row c-sibling-item--v u-font-size--sm">
           <div class="l-flex__fill l-flex--row">
-            <div class="l-flex__self c-sibling-item u-ellipsis">{{ file.directory }}</div>
-            <div class="l-flex__none c-sibling-item">{{ file.tag }}</div>
-            <div class="l-flex__none c-sibling-item">{{ file.type }}</div>
-            <div class="l-flex__self c-sibling-item u-ellipsis">{{ file.name }}</div>
+            <div class="l-flex__self c-sibling-item u-ellipsis">
+              {{ file.directory }}
+            </div>
+            <div class="l-flex__none c-sibling-item">
+              {{ file.tag }}
+            </div>
+            <div class="l-flex__none c-sibling-item">
+              {{ file.type }}
+            </div>
+            <div class="l-flex__self c-sibling-item u-ellipsis">
+              {{ file.name }}
+            </div>
+          </div>
+          <div class="l-flex__none c-sibling-item u-font-size--xs">
+            {{ file.percentage }}%
           </div>
-          <div class="l-flex__none c-sibling-item u-font-size--xs">{{ file.percentage }}%</div>
         </div>
         <el-progress
           class="c-sibling-item--v nearer"

+ 33 - 8
src/layout/components/Navbar/UploadDashboard/index.vue

@@ -25,13 +25,17 @@
           <div class="l-flex__none l-flex--row c-sibling-item--v">
             <div class="l-flex__fill" />
             <div class="l-flex--row c-sibling-item">
-              <span class="c-sibling-item u-color--info">目录</span>
+              <span class="c-sibling-item u-color--info">
+                目录
+              </span>
               <div class="c-sibling-item el-input__inner u-width u-ellipsis">
                 {{ directory }}
               </div>
             </div>
             <div class="l-flex--row c-sibling-item">
-              <span class="c-sibling-item u-color--info">类型</span>
+              <span class="c-sibling-item u-color--info">
+                类型
+              </span>
               <schema-select
                 v-model="tag"
                 class="c-sibling-item u-width--xs"
@@ -39,13 +43,26 @@
               />
             </div>
             <div class="l-flex--row c-sibling-item far">
-              <span class="c-sibling-item u-color--info">标签</span>
+              <span class="c-sibling-item u-color--info">
+                标签
+              </span>
               <schema-select
                 v-model="subTag"
                 class="c-sibling-item u-width--xs"
                 :schema="subTagSelectSchema"
               />
             </div>
+            <div class="l-flex--row c-sibling-item far">
+              <span class="c-sibling-item u-color--info">
+                转码
+              </span>
+              <el-switch
+                v-model="videoTranscode"
+                class="c-sibling-item"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+              />
+            </div>
           </div>
           <el-upload
             ref="upload"
@@ -61,10 +78,16 @@
           >
             <div class="l-flex--row">
               <i class="c-sibling-item o-upload__icon el-icon-upload" />
-              <span class="c-sibling-item near">拖拽文件到此或</span>
-              <span class="c-sibling-item near u-color--blue">点击选择文件</span>
+              <span class="c-sibling-item near">
+                拖拽文件到此或
+              </span>
+              <span class="c-sibling-item near u-color--blue">
+                点击选择文件
+              </span>
+            </div>
+            <div class="o-upload__accept">
+              {{ accept }}
             </div>
-            <div class="o-upload__accept">{{ accept }}</div>
           </el-upload>
           <file-progress
             v-if="hasFile"
@@ -103,6 +126,7 @@ export default {
       hasFile: false,
       tag: AssetTag.AD,
       subTag: '',
+      videoTranscode: true,
       tagSelectSchema: { options: [
         { value: AssetTag.AD, label: AssetTagInfo[AssetTag.AD] },
         { value: AssetTag.PUBLICITY, label: AssetTagInfo[AssetTag.PUBLICITY] },
@@ -122,7 +146,7 @@ export default {
   computed: {
     ...mapGetters(['org']),
     accept () {
-      return '.png,.jpg,.jpeg,.gif,.mp4,audio/mpeg,.ppt,.pptx,application/pdf,.doc,.docx'
+      return '.png,.jpg,.jpeg,.gif,.mp4,audio/mpeg,.ppt,.pptx,application/pdf,.doc,.docx,.ts,.mpg,.wmv,.avi,.mov,.m4v'
     },
     directory () {
       return this.directoryInfo?.directory
@@ -145,7 +169,8 @@ export default {
         subtag: this.subTag,
         org,
         directory,
-        treeId
+        treeId,
+        videoTranscode: this.videoTranscode
       })
     },
     open () {

+ 19 - 11
src/utils/upload.js

@@ -210,25 +210,32 @@ function emit (...args) {
   EventBus.$emit(...args)
 }
 
+const imageNameRegex = /\.(png|jpg|jpeg|gif)$/i
+const imageTypeRegex = /png|jpg|jpeg|gif/i
+const videoNameRegex = /\.(mp4|ts|wmv|avi|mpg|mov|m4v)$/i
+const videoTypeRegex = /mp4|ts|wmv|avi|mpg|mov|m4v/i
+const audioRegex = /audio\//
+const pptRegex = /application\/(vnd.ms-powerpoint|vnd.openxmlformats-officedocument.presentationml.presentation)/
+const pdfRegex = /application\/pdf/
+const docRegex = /application\/(msword|vnd.openxmlformats-officedocument.wordprocessingml.document)/
 function getType ({ name, type }) {
   switch (true) {
-    case /png|jpg|jpeg|gif/i.test(type):
+    case imageNameRegex.test(name):
+    case imageTypeRegex.test(type):
       return AssetType.IMAGE
-    case /mp4/i.test(type):
+    case videoNameRegex.test(name):
+    case videoTypeRegex.test(type):
       return AssetType.VIDEO
-    case /audio\/mpeg/.test(type):
+    case audioRegex.test(type):
       return AssetType.AUDIO
-    case /application\/(vnd.ms-powerpoint|vnd.openxmlformats-officedocument.presentationml.presentation)/.test(type):
+    case pptRegex.test(type):
       return AssetType.PPT
-    case /application\/pdf/.test(type):
+    case pdfRegex.test(type):
       return AssetType.PDF
-    case /application\/(msword|vnd.openxmlformats-officedocument.wordprocessingml.document)/i.test(type):
+    case docRegex.test(type):
       return AssetType.DOC
     default:
-      Message({
-        type: 'warning',
-        message: `暂不支持${name}该类型文件`
-      })
+      console.warn(`暂不支持${name}该类型文件`)
       return null
   }
 }
@@ -701,7 +708,8 @@ function startMerge (obj) {
       filename: transformName(name),
       totalSize,
       totalChunks,
-      ...options
+      ...options,
+      videoBitRate: String(Math.floor((totalSize * 8) / (options.duration * 1000)))
     }),
     cancelToken: obj.source.token,
     timeout: 0,

+ 155 - 0
src/views/screen/material/media/components/LineChart.vue

@@ -0,0 +1,155 @@
+<template>
+  <div class="chart-container">
+    <div
+      v-if="mergedData.length > 0"
+      ref="canvas"
+      class="chart"
+    />
+    <el-empty
+      v-else
+      class="l-flex__fill l-flex--row center"
+      description="暂无数据"
+    />
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+
+export default {
+  name: 'BarChart',
+  props: {
+    data: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    }
+  },
+  computed: {
+    title () {
+      if (this.mergedData.length === 0) {
+        return ''
+      }
+      const startDate = this.mergedData[0].date
+      const endDate = this.mergedData[this.mergedData.length - 1].date
+      return `${endDate} - ${startDate}`
+    },
+    mergedData () {
+      const mergedData = []
+      Object.entries(this.data).forEach(([date, dateData]) => {
+        dateData.forEach(deviceData => {
+          const existingData = mergedData.find(
+            item => item.deviceId === deviceData.deviceId
+          )
+          if (existingData) {
+            existingData.validCount += parseInt(deviceData.validCount)
+          } else {
+            mergedData.push({
+              date,
+              deviceId: deviceData.deviceId,
+              deviceName: deviceData.deviceName,
+              validCount: parseInt(deviceData.validCount),
+              duration: (parseFloat(deviceData.duration) / 60).toFixed(2)
+            })
+          }
+        })
+      })
+
+      return mergedData
+    }
+  },
+  watch: {
+    mergedData: {
+      immediate: true,
+      handler () {
+        if (this.mergedData.length > 0) {
+          this.$nextTick(() => {
+            this.drawChart()
+          })
+        }
+      }
+    }
+  },
+  mounted () {
+    if (this.mergedData.length > 0) {
+      this.$nextTick(() => {
+        this.drawChart()
+      })
+    }
+  },
+  methods: {
+    drawChart () {
+      try {
+        const deviceDataArray = this.mergedData
+        const container = this.$refs.canvas
+        const chart = echarts.init(container)
+        const options = {
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+              type: 'shadow'
+            }
+          },
+          legend: {
+          },
+          xAxis: {
+
+            type: 'category',
+            data: deviceDataArray.map(item => item.deviceName),
+            axisLabel: {
+              formatter (value) {
+                if (value.length > 10) {
+                  return `${value.substring(0, 10)}...`
+                }
+                return value
+              }
+            }
+          },
+          yAxis: {
+          },
+          series: [
+            {
+              name: '播放次数',
+              type: 'bar',
+              data: deviceDataArray.map(item => item.validCount)
+            },
+            {
+              name: '播放时长(分钟)',
+              type: 'bar',
+              data: deviceDataArray.map(item => item.duration)
+            }
+          ]
+        }
+
+        chart.setOption(options)
+      } catch (error) {
+        console.error('图表绘制错误:', error)
+      }
+    }
+  }
+}
+</script>
+
+<style>
+.chart-container {
+  margin-top: 50px;
+  width: 100%;
+  height: 400px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.chart {
+  width: 100%;
+  height: 90%;
+}
+
+.no-data-message {
+  text-align: center;
+  margin-top: 20px;
+  font-size: 16px;
+  color: #777;
+}
+</style>

+ 289 - 51
src/views/screen/material/media/index.vue

@@ -19,33 +19,137 @@
       :schema="schema"
     />
     <preview-dialog ref="previewDialog" />
+    <confirm-dialog
+      ref="schedulingOptionsDialog"
+      title="资源日期"
+      @confirm="onConfirm"
+    >
+      <div class="c-grid-form auto u-align-self--center">
+        <div class="c-grid-form__label">
+          资源日期
+        </div>
+        <el-date-picker
+          v-model="dateRange"
+          type="daterange"
+          range-separator="至"
+          value-format="yyyy-MM-dd"
+        />
+      </div>
+    </confirm-dialog>
+    <confirm-dialog
+      ref="moveDataDialog"
+      title="资源移动"
+      @confirm="onAsstesMove"
+    >
+      <template #default>
+        <el-tree
+          class="el-tree--highlight-current"
+          :data="treeData"
+          :props="treeProps"
+          default-expand-all
+          @node-click="handleNodeClick"
+        />
+      </template>
+    </confirm-dialog>
+    <c-dialog
+      ref="chartOptionsDialog"
+      :title="dialogTitle"
+    >
+      <template #default>
+        <div class="c-grid-form auto u-align-self--center">
+          <div class="c-grid-form__label">
+            播放日期
+          </div>
+          <el-date-picker
+            v-model="dateRange"
+            type="datetimerange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            :default-time="['00:00:00', '23:59:59']"
+            @change="fetchData"
+          />
+        </div>
+        <line-chart :data="chartData" />
+      </template>
+    </c-dialog>
+    <table-dialog
+      ref="mediaDialog"
+      title="转码进度"
+      size="lg"
+      :schema="mediaSchema"
+    />
+    <confirm-dialog
+      ref="bitRateDialog"
+      title="码率"
+      @confirm="onBitrateConfirm"
+    >
+      <div class="c-grid-form u-align-self--center">
+        <span class="c-grid-form__label u-required">
+          目标码率
+        </span>
+        <div
+          class="has-info"
+          data-info="仅可输入3000~14000的正整数"
+        >
+          <el-input
+            v-model.trim="bitrate"
+            placeholder="请输入码率"
+            clearable
+            maxlength="5"
+            @change="checkBitrate"
+          />
+        </div>
+      </div>
+    </confirm-dialog>
   </wrapper>
 </template>
 
 <script>
 import {
-  State,
   AssetTypeInfo,
   AssetTagInfo,
   AssetTag,
-  AssetType
+  AssetType,
+  CourseTypeInfo
 } from '@/constant'
 import {
   EventBus,
   parseByte,
-  getAIState
+  getAIState,
+  parseTime
 } from '@/utils'
 import {
   getAssetsByQuery,
   updateAsset,
   deleteAsset,
-  deleteAssets
+  deleteAssets,
+  resourceExport,
+  countByDay,
+  treeLocation,
+  secondaryTransList,
+  mediaList
 } from '@/api/asset'
+import LineChart from './components/LineChart.vue'
 
 export default {
+  components: {
+    LineChart
+  },
   data () {
     return {
-      treeParams: null
+      bitrate: '',
+      keyName: '',
+      chartData: {},
+      dateRange: [],
+      treeParams: null,
+      dialogTitle: '',
+      treeData: [/* 数据源 */],
+      treeProps: {
+        label: 'name',
+        children: 'childrenNode'
+      },
+      selectedData: []
     }
   },
   computed: {
@@ -59,13 +163,15 @@ export default {
           'row-click': this.onToggleSelection,
           'selection-change': this.onSelectionChange
         },
-        autoRefreshEachPage: true,
-        refreshInterval: 30000,
         list: this.getAssetsByQuery,
         transform: this.transformAsset,
         buttons: [
           { type: 'add', label: '上传', on: this.onUpload },
-          { type: 'del', on: this.onBatchDel }
+          { type: 'del', on: this.onBatchDel },
+          { label: '移动', icon: 'el-icon-rank', on: this.onMove },
+          { label: '曝光率导出', on: this.onExport },
+          { label: '二次转码', on: this.onSecondaryTranscod },
+          { label: '转码进度', on: this.onProgressBar }
         ],
         filters: [
           { key: 'tag', type: 'select', placeholder: '资源分类', options: [
@@ -86,7 +192,7 @@ export default {
           { type: 'refresh' }
         ],
         cols: [
-          { type: 'selection', selectable: this.canDel, fixed: true },
+          { type: 'selection', fixed: true },
           { prop: 'tagInfo', label: '分类', width: 72, 'align': 'center', fixed: true },
           { prop: 'typeInfo', label: '资源', width: 72, 'align': 'center', fixed: true },
           { prop: 'file', label: '', type: 'asset', on: this.onView, fixed: true },
@@ -97,15 +203,42 @@ export default {
             on: { edit: val => this.onEdit(data, val) }
           }), 'class-name': 'c-edit-column', 'min-width': 120, fixed: true },
           { prop: 'ai', label: 'AI审核', type: 'tag' },
-          // { prop: 'statusTag', type: 'tag' },
           { prop: 'size', label: '资源大小', 'align': 'right' },
           { prop: 'diff', label: '其他', 'align': 'right' },
           { prop: 'userName', label: '上传人', 'align': 'right' },
           { prop: 'createTime', label: '上传时间', width: 140, 'align': 'right' },
           { type: 'invoke', render: [
-            { label: '查看', allow: ({ file }) => !!file, on: this.onView },
-            { label: '删除', allow: this.canDel, on: this.onDel }
-          ], width: 100, fixed: 'right' }
+            { label: '图表', allow: this.isReady, on: this.onChart },
+            { label: '查看', allow: this.isReady, on: this.onView },
+            { label: '删除', on: this.onDel }
+          ], width: 140, fixed: 'right' }
+        ]
+      }
+    },
+    mediaSchema () {
+      return {
+        autoRefreshEachPage: true,
+        refreshInterval: 30000,
+        list: mediaList,
+        filters: [
+          { key: 'originalName', type: 'search', placeholder: '资源名称' },
+          { type: 'refresh' }
+        ],
+        cols: [
+          { type: 'refresh' },
+          { prop: 'originalName', label: '素材名称', 'align': 'center' },
+          { label: '类型', 'align': 'center', render: ({ taskType }) => CourseTypeInfo[taskType] },
+          {
+            label: '执行结果', type: 'tag', render: ({ taskStatus }) => {
+              return {
+                type: ['', 'info', ' ', 'success', 'danger', 'warning'][taskStatus],
+                label: ['', '执行中', '等待中', '成功', '失败', '取消'][taskStatus]
+              }
+            }, width: 160
+          },
+          { prop: 'departmentName', label: '部门名', 'align': 'center' },
+          { prop: 'username', label: '用户名', 'align': 'center' },
+          { prop: 'createTime', label: '上传时间', 'align': 'center' }
         ]
       }
     }
@@ -117,13 +250,108 @@ export default {
     EventBus.$off('directory-refresh', this.onDirectoryRefresh)
   },
   methods: {
+    isReady ({ file }) {
+      return !!file
+    },
+    checkBitrate () {
+      if (!/^\d+$/.test(this.bitrate) || this.bitrate < 3000 || this.bitrate > 14000) {
+        this.bitrate = ''
+      }
+    },
+    onSecondaryTranscod () {
+      if (this.$selectionItems?.length) {
+        this.$refs.bitRateDialog.show()
+      } else {
+        this.$message({
+          type: 'warning',
+          message: '请先选择需要转码的资源'
+        })
+      }
+    },
+    onBitrateConfirm (done) {
+      if (this.bitrate === '') {
+        this.$message({
+          type: 'warning',
+          message: '请填写码率'
+        })
+        return
+      }
+      this.$progressObj = this.$selectionItems.map(item => {
+        return {
+          keyName: item.keyName,
+          originalName: item.originalName,
+          tenant: item.tenant,
+          org: item.org,
+          type: item.type,
+          taskType: item.type,
+          extraAttributes: this.bitrate
+        }
+      })
+      secondaryTransList(this.$progressObj).then(() => {
+        done()
+        this.bitrate = ''
+        this.$refs.mediaDialog.show()
+      })
+    },
+    onProgressBar () {
+      this.$refs.mediaDialog.show()
+    },
     onDirectoryRefresh (directoryOption) {
       if (this.$directoryOption?.group?.path && directoryOption.group.path) {
         this.$refs.tree?.refresh()
       }
     },
-    canDel ({ status }) {
-      return status !== State.DRAFT
+    onMove () {
+      if (this.$selectionItems?.length) {
+        this.treeData = this.$refs.tree.directories
+        this.$refs.moveDataDialog.show()
+      } else {
+        this.$message({
+          type: 'warning',
+          message: '请先选择需要移动的资源'
+        })
+      }
+    },
+    extractNodes (data, nodes = []) {
+      for (const node of data) {
+        nodes.push(node)
+        if (node.childrenNode && node.childrenNode.length > 0) {
+          this.extractNodes(node.childrenNode, nodes)
+        }
+      }
+      return nodes
+    },
+    transformDataToTransferFormat (data) {
+      const transformedData = []
+      for (const node of data) {
+        const newNode = {
+          key: node.id,
+          label: node.name,
+          disabled: false
+        }
+        if (node.childrenNode && node.childrenNode.length > 0) {
+          newNode.children = this.transformDataToTransferFormat(node.childrenNode)
+        }
+        transformedData.push(newNode)
+      }
+      return transformedData
+    },
+    handleNodeClick (data) {
+      this.selectedData = data
+    },
+    onAsstesMove (done) {
+      const asstes = {
+        keyNameList: this.$selectionItems.map(item => item.keyName),
+        newTreeId: this.selectedData.id
+      }
+      treeLocation(asstes).then(() => {
+        done()
+        this.$refs.tree?.refresh()
+        this.$message({
+          type: 'success',
+          message: '移动成功'
+        })
+      })
     },
     onDirectoryChanged (directory) {
       this.$directoryOption = directory
@@ -146,41 +374,8 @@ export default {
       asset.typeInfo = AssetTypeInfo[asset.type]
       asset.size = parseByte(asset.size)
       asset.ai = getAIState(asset)
-      // asset.statusTag = this.getStatusTag(asset)
       return asset
     },
-    getStatusTag ({ status, remark }) {
-      switch (status) {
-        case State.DRAFT:
-          return {
-            type: 'info',
-            label: '解析中'
-          }
-        case State.READY:
-          return {
-            type: 'primary',
-            label: '待使用'
-          }
-        case State.SUBMITTED:
-          return {
-            type: 'warning',
-            label: '待审核'
-          }
-        case State.RESOLVED:
-          return {
-            type: 'success',
-            label: '通过'
-          }
-        case State.REJECTED:
-          return {
-            type: 'danger',
-            label: '驳回',
-            msg: remark
-          }
-        default:
-          return null
-      }
-    },
     onView ({ file }) {
       this.$refs.previewDialog.show(file)
     },
@@ -222,11 +417,53 @@ export default {
         })
       }
     },
-    onToggleSelection (row) {
-      if (row.status !== State.DRAFT) {
-        this.$refs.table.getInst().toggleRowSelection(row)
+    onExport () {
+      if (this.$selectionItems?.length) {
+        const date = parseTime(new Date(), '{y}-{m}-{d}')
+        this.dateRange = [date, date]
+        this.$refs.schedulingOptionsDialog.show()
+      } else {
+        this.$message({
+          type: 'warning',
+          message: '请先选择导出的资源'
+        })
       }
     },
+    async onChart ({ keyName, originalName }) {
+      this.dialogTitle = `${originalName}-播放统计`
+      this.keyName = keyName
+      const currentDate = new Date()
+      const startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate(), 0, 0, 0)
+      const formattedStartDate = parseTime(startDate, '{y}-{m}-{d} {h}:{i}:{s}')
+      const formattedEndDate = parseTime(currentDate, '{y}-{m}-{d} {h}:{i}:{s}')
+      this.dateRange = [formattedStartDate, formattedEndDate]
+      await this.updateChartData()
+      this.$refs.chartOptionsDialog.show()
+    },
+    fetchData () {
+      this.updateChartData()
+    },
+    async updateChartData () {
+      const startTime = parseTime(this.dateRange[0], '{y}-{m}-{d} {h}:{i}:{s}')
+      const endTime = parseTime(this.dateRange[1], '{y}-{m}-{d} {h}:{i}:{s}')
+
+      try {
+        const { data } = await countByDay({ keyName: this.keyName, startTime, endTime })
+        this.chartData = data
+      } catch (error) {
+        console.error(error)
+      }
+    },
+    onConfirm (done) {
+      resourceExport({
+        keyNameList: this.$selectionItems.map(({ keyName }) => keyName),
+        startDate: this.dateRange[0],
+        endDate: this.dateRange[1]
+      }).then(done)
+    },
+    onToggleSelection (row) {
+      this.$refs.table.getInst().toggleRowSelection(row)
+    },
     onSelectionChange (val) {
       this.$selectionItems = val
     },
@@ -236,3 +473,4 @@ export default {
   }
 }
 </script>
+

+ 3 - 6
src/views/screen/material/stream/index.vue

@@ -19,17 +19,16 @@
 
 <script>
 import {
-  State,
   AssetTagInfo,
   AssetTag,
   AssetType
-} from '@/constant'
+} from '@/constant.js'
 import {
   getAssetsByQuery,
   updateAsset,
   deleteAsset,
   deleteAssets
-} from '@/api/asset'
+} from '@/api/asset.js'
 import StreamingMediaDialog from './components/StreamingMediaDialog.vue'
 
 export default {
@@ -135,9 +134,7 @@ export default {
       }
     },
     onToggleSelection (row) {
-      if (row.status !== State.DRAFT) {
-        this.$refs.table.getInst().toggleRowSelection(row)
-      }
+      this.$refs.table.getInst().toggleRowSelection(row)
     },
     onSelectionChange (val) {
       this.$selectionItems = val