|
|
@@ -0,0 +1,987 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <confirm-dialog
|
|
|
+ ref="dialog"
|
|
|
+ size="xl fixed"
|
|
|
+ title="编辑模板"
|
|
|
+ append-to-body
|
|
|
+ @closed="onClosed"
|
|
|
+ @confirm="onConfirm"
|
|
|
+ >
|
|
|
+ <template #default>
|
|
|
+ <div class="l-flex__auto l-flex">
|
|
|
+ <div class="l-flex__none l-flex--col c-viewer__asserts">
|
|
|
+ <div class="l-flex__none l-flex--row c-viewer__count has-padding--h">
|
|
|
+ <span class="l-flex__auto u-color--black u-font-size u-bold">
|
|
|
+ 资源
|
|
|
+ </span>
|
|
|
+ <span class="l-flex__none u-color--info u-font-size--xs">
|
|
|
+ {{ count }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <el-scrollbar
|
|
|
+ class="l-flex__fill l-flex--col c-viewer__scrollbar"
|
|
|
+ native
|
|
|
+ >
|
|
|
+ <div class="c-viewer__list">
|
|
|
+ <div
|
|
|
+ v-for="(widget,index) in layers"
|
|
|
+ ref="widgetElements"
|
|
|
+ :key="index"
|
|
|
+ class="c-sibling-item--v o-layer"
|
|
|
+ :class="{ active: index == selectedWidgetIndex }"
|
|
|
+ @click="onWidgetClick(widget,index,$event)"
|
|
|
+ >
|
|
|
+ <widget-shortcut
|
|
|
+ :widget="widget"
|
|
|
+ editable
|
|
|
+ @view="onView"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <!-- <div-->
|
|
|
+ <!-- v-if="hasRootAssets"-->
|
|
|
+ <!-- ref="rootElement"-->
|
|
|
+ <!-- key="root"-->
|
|
|
+ <!-- class="c-sibling-item--v o-layer"-->
|
|
|
+ <!-- :class="{ active: !selectedWidgetId }"-->
|
|
|
+ <!-- @click="onRootClick"-->
|
|
|
+ <!-- >-->
|
|
|
+ <!-- <widget-shortcut-->
|
|
|
+ <!-- :widget="node"-->
|
|
|
+ <!-- :custom-style="backgroundStyles"-->
|
|
|
+ <!-- source-key="bgm"-->
|
|
|
+ <!-- background-->
|
|
|
+ <!-- @view="onView"-->
|
|
|
+ <!-- />-->
|
|
|
+ <!-- </div>-->
|
|
|
+ </div>
|
|
|
+ </el-scrollbar>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="isMedia"
|
|
|
+ class="l-flex__auto l-flex"
|
|
|
+ >
|
|
|
+ <directory-tree
|
|
|
+ class="l-flex__none c-sibling-item c-sidebar u-width--md"
|
|
|
+ style="display:none;"
|
|
|
+ :class="{ mask: isStream }"
|
|
|
+ :option="directoryOption"
|
|
|
+ @change="onAssetDirectoryChanged"
|
|
|
+ />
|
|
|
+ <grid-table
|
|
|
+ ref="table"
|
|
|
+ class="l-flex__none c-sibling-item far"
|
|
|
+ style="width: 496px;"
|
|
|
+ size="large"
|
|
|
+ :schema="schema"
|
|
|
+ :custom="isDir"
|
|
|
+ @changed="onTableRefresh"
|
|
|
+ >
|
|
|
+ <template #header>
|
|
|
+ <template v-if="isDir">
|
|
|
+ <div class="l-flex__fill l-flex--row u-height">
|
|
|
+ <i
|
|
|
+ class="l-flex__none o-icon o-icon--hover el-icon-arrow-left u-bold u-pointer"
|
|
|
+ @click="fileDir = null"
|
|
|
+ />
|
|
|
+ <div class="l-flex__fill u-ellipsis u-bold">
|
|
|
+ {{ fileDir.name }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ <grid-table-item v-slot="item">
|
|
|
+ <media-card
|
|
|
+ :asset="item"
|
|
|
+ @click="onToggleAsset"
|
|
|
+ >
|
|
|
+ <el-checkbox
|
|
|
+ v-model="item.selected"
|
|
|
+ class="o-card__checkbox"
|
|
|
+ />
|
|
|
+ <i
|
|
|
+ class="o-card__play el-icon-video-play has-active"
|
|
|
+ @click.stop="onViewAsset(item)"
|
|
|
+ />
|
|
|
+ <i
|
|
|
+ v-if="item.file.files"
|
|
|
+ class="o-card__grid el-icon-s-grid has-active"
|
|
|
+ @click.stop="onViewDir(item)"
|
|
|
+ />
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ class="o-card__info u-font-size--xs"
|
|
|
+ >
|
|
|
+ {{ item.diff }}
|
|
|
+ </div>
|
|
|
+ </media-card>
|
|
|
+ </grid-table-item>
|
|
|
+ <div
|
|
|
+ v-if="isDir"
|
|
|
+ class="l-flex__auto c-sibling-item--v u-overflow-y--auto"
|
|
|
+ >
|
|
|
+ <div class="l-flex__none l-grid large">
|
|
|
+ <media-card
|
|
|
+ v-for="file in fileDir.files"
|
|
|
+ :key="file.keyName"
|
|
|
+ :asset="file"
|
|
|
+ @click="onToggleAsset"
|
|
|
+ >
|
|
|
+ <el-checkbox
|
|
|
+ v-model="file.selected"
|
|
|
+ class="o-card__checkbox"
|
|
|
+ />
|
|
|
+ <i
|
|
|
+ class="o-card__play el-icon-video-play has-active"
|
|
|
+ @click.stop="onViewAsset(file)"
|
|
|
+ />
|
|
|
+ </media-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </grid-table>
|
|
|
+ <div class="l-flex__none l-flex--row c-transfer-area">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="mini"
|
|
|
+ :disabled="!hasSelectionAssets"
|
|
|
+ @click="onAddAssets"
|
|
|
+ >
|
|
|
+ <i class="el-icon-arrow-right" />
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="l-flex__fill l-flex--col">
|
|
|
+ <template v-if="assets.length">
|
|
|
+ <div class="l-flex__none l-flex--row right c-sibling-item--v u-font-size--sm u-color-black u-bold">
|
|
|
+ <div class="c-sibling-item">
|
|
|
+ {{ resourceSize }}
|
|
|
+ </div>
|
|
|
+ <div class="c-sibling-item">
|
|
|
+ {{ statistics }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="options.durationEditable"
|
|
|
+ class="l-flex__none l-flex--row right c-sibling-item--v"
|
|
|
+ >
|
|
|
+ <div class="c-sibling-item u-color--info u-font-size--sm">
|
|
|
+ 图片统一上播时长(s)
|
|
|
+ </div>
|
|
|
+ <el-input-number
|
|
|
+ v-model="duration"
|
|
|
+ class="c-sibling-item u-width--xs"
|
|
|
+ size="small"
|
|
|
+ title="上播时长(s)"
|
|
|
+ controls-position="right"
|
|
|
+ :min="1"
|
|
|
+ :max="86400"
|
|
|
+ step-strictly
|
|
|
+ />
|
|
|
+ <div
|
|
|
+ class="c-sibling-item near o-button"
|
|
|
+ @click="onSetImageDuration"
|
|
|
+ >
|
|
|
+ 设置
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <draggable
|
|
|
+ v-model="assets"
|
|
|
+ class="l-flex__fill c-sibling-item--v u-font-size--sm u-overflow-y--auto"
|
|
|
+ handle=".mover"
|
|
|
+ animation="300"
|
|
|
+ @change="onDragChange"
|
|
|
+ @end="onDragEnd"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(asset, index) in assets"
|
|
|
+ :key="asset.key"
|
|
|
+ class="l-flex--row c-sibling-item--v near"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ ref="input"
|
|
|
+ v-model="inputValues[index]"
|
|
|
+ :value="index"
|
|
|
+ class="l-flex__none c-sibling-item u-width--3xs o-draggable-input"
|
|
|
+ @focus="clearInput(index)"
|
|
|
+ @blur="restoreInput(index, asset)"
|
|
|
+ @keyup.enter.native="onTargetIndexInput(index, asset)"
|
|
|
+ />
|
|
|
+ <draggable-item
|
|
|
+ class="l-flex__fill c-sibling-item near"
|
|
|
+ :item="asset"
|
|
|
+ @view="onViewAsset"
|
|
|
+ @del="onDelAsset(index)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </draggable>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-empty
|
|
|
+ v-else
|
|
|
+ class="l-flex__auto l-flex--row center"
|
|
|
+ description="请添加资源"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ class="l-flex__auto l-flex--col"
|
|
|
+ style="margin-left: 16px;"
|
|
|
+ >
|
|
|
+ <toolbar
|
|
|
+ style="border-bottom: 1px solid #ccc"
|
|
|
+ mode="simple"
|
|
|
+ :editor="editor"
|
|
|
+ :default-config="toolbarConfig"
|
|
|
+ />
|
|
|
+ <editor
|
|
|
+ v-model="selectedWidget.text"
|
|
|
+ mode="simple"
|
|
|
+ style="height: 500px; overflow: hidden;"
|
|
|
+ :default-config="editorConfig"
|
|
|
+ @onCreated="onEditorCreated"
|
|
|
+ @customPaste="onCustomPaste"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </confirm-dialog>
|
|
|
+ <preview-dialog
|
|
|
+ ref="previewDialog"
|
|
|
+ @closed="onClosedPreview"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ State,
|
|
|
+ AssetTag,
|
|
|
+ AssetTagInfo,
|
|
|
+ AssetType,
|
|
|
+ AssetTypeInfo
|
|
|
+} from '@/constant'
|
|
|
+import {
|
|
|
+ parseDuration,
|
|
|
+ getAssetDuration
|
|
|
+} from '@/utils'
|
|
|
+import { getAssetsByQuery } from '@/api/asset'
|
|
|
+import Draggable from 'vuedraggable'
|
|
|
+import { getProgram } from '@/api/program'
|
|
|
+import { WidgetType } from '@/views/screen/material/program/ast/core/constant'
|
|
|
+import WidgetShortcut from '@/views/screen/material/program/ast/components/WidgetShortcut'
|
|
|
+import {
|
|
|
+ Editor,
|
|
|
+ Toolbar
|
|
|
+} from '@wangeditor/editor-for-vue'
|
|
|
+import '@wangeditor/editor/dist/css/style.css'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'TemplateAssetChooseDialog',
|
|
|
+ components: {
|
|
|
+ Draggable,
|
|
|
+ WidgetShortcut,
|
|
|
+ Editor,
|
|
|
+ Toolbar
|
|
|
+ },
|
|
|
+ props: {
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ transform: {
|
|
|
+ type: Function,
|
|
|
+ default: null
|
|
|
+ },
|
|
|
+ choosen: {
|
|
|
+ type: Function,
|
|
|
+ default: null
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ inputValues: [],
|
|
|
+ lastIndex: 0,
|
|
|
+ types: [],
|
|
|
+ directoryOption: null,
|
|
|
+ fileDir: null,
|
|
|
+ assets: [],
|
|
|
+ tableAssets: [],
|
|
|
+ duration: 10,
|
|
|
+ options: {},
|
|
|
+ isStream: false,
|
|
|
+ selectedWidget: null, //
|
|
|
+ selectedWidgetIndex: 0,
|
|
|
+ program: null,
|
|
|
+ isMedia: true,
|
|
|
+ editor: null,
|
|
|
+ richHtml: '',
|
|
|
+ toolbarConfig: {
|
|
|
+ toolbarKeys: [
|
|
|
+ 'headerSelect',
|
|
|
+ '|',
|
|
|
+ 'bold',
|
|
|
+ // 'color',
|
|
|
+ // '|',
|
|
|
+ // 'fontSize',
|
|
|
+ // 'lineHeight',
|
|
|
+ '|',
|
|
|
+ 'justifyLeft',
|
|
|
+ 'justifyCenter',
|
|
|
+ 'justifyRight'
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ editorConfig: {
|
|
|
+ placeholder: '请输入内容...'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ // dialogTitle () {
|
|
|
+ // return this.title || (this.types.length > 1 ? '请选择资源' : `请选择${AssetTypeInfo[this.types[0]]}`)
|
|
|
+ // },
|
|
|
+ layers () {
|
|
|
+ const arr = this.program.detail.widgets.slice()
|
|
|
+ .reverse()
|
|
|
+ console.log('widgets', arr)
|
|
|
+ return arr
|
|
|
+ },
|
|
|
+ count () {
|
|
|
+ if (this.program.detail) {
|
|
|
+ return this.program.detail.widgets.length
|
|
|
+ }
|
|
|
+ return 0
|
|
|
+ },
|
|
|
+ // selectedWidgetId () {
|
|
|
+ // return this.selectedWidget?.id
|
|
|
+ // },
|
|
|
+ hasRootAssets () {
|
|
|
+ if (this.node) {
|
|
|
+ const { backgroundImage, bgm } = this.node
|
|
|
+ return backgroundImage.length > 0 || bgm.length > 0
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ },
|
|
|
+ schema () {
|
|
|
+ return {
|
|
|
+ pagination: {
|
|
|
+ layout: 'prev,pager,next'
|
|
|
+ },
|
|
|
+ list: this.getAssetsByQuery,
|
|
|
+ transform: this.transformAsset,
|
|
|
+ condition: {
|
|
|
+ pageSize: 18,
|
|
|
+ status: State.AVAILABLE,
|
|
|
+ tag: '',
|
|
|
+ type: this.types.length > 1 ? '' : this.types[0]
|
|
|
+ },
|
|
|
+ filters: [
|
|
|
+ {
|
|
|
+ key: 'tag', type: 'select', options: [
|
|
|
+ { value: AssetTag.AD, label: AssetTagInfo[AssetTag.AD] },
|
|
|
+ { value: AssetTag.PUBLICITY, label: AssetTagInfo[AssetTag.PUBLICITY] },
|
|
|
+ { value: AssetTag.LOCAL_PUBLICITY, label: AssetTagInfo[AssetTag.LOCAL_PUBLICITY] },
|
|
|
+ { value: AssetTag.SHIM, label: AssetTagInfo[AssetTag.SHIM] }
|
|
|
+ ], placeholder: '资源分类'
|
|
|
+ },
|
|
|
+ this.types.length > 1
|
|
|
+ ? {
|
|
|
+ key: 'type', type: 'select', options: this.types.map(type => {
|
|
|
+ return {
|
|
|
+ value: type,
|
|
|
+ label: AssetTypeInfo[type]
|
|
|
+ }
|
|
|
+ }), placeholder: '资源类型'
|
|
|
+ }
|
|
|
+ : null,
|
|
|
+ { key: 'originalName', type: 'search', placeholder: '资源名称' }
|
|
|
+ ].filter(Boolean),
|
|
|
+ isStream: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isDir () {
|
|
|
+ return !!this.fileDir
|
|
|
+ },
|
|
|
+ hasSelectionAssets () {
|
|
|
+ return this.isDir
|
|
|
+ ? this.fileDir.files.some(({ selected }) => selected)
|
|
|
+ : this.tableAssets.some(({ selected }) => selected)
|
|
|
+ },
|
|
|
+ statistics () {
|
|
|
+ const length = this.assets.length
|
|
|
+ return `共${length}条,总时长 ${parseDuration(this.assets.reduce((total, { duration }) => total + duration, 0), false)}`
|
|
|
+ },
|
|
|
+ resourceSize () {
|
|
|
+ const uniqueAssets = Array.from(new Set(this.assets.map(asset => asset.keyName)))
|
|
|
+ .map(keyName => this.assets.find(asset => asset.keyName === keyName))
|
|
|
+ const totalSizeMB = (uniqueAssets.reduce((total, item) => total + parseInt(item.size), 0) / (1024 * 1024))
|
|
|
+ if (totalSizeMB >= 1024) {
|
|
|
+ const totalSizeGB = totalSizeMB / 1024
|
|
|
+ return `资源包大小: ${totalSizeGB.toFixed(2)} GB`
|
|
|
+ }
|
|
|
+ return `资源包大小: ${totalSizeMB.toFixed(2)} MB`
|
|
|
+ },
|
|
|
+ mediaTypes () {
|
|
|
+ return this.types.filter(type => type !== AssetType.STREAMING_MEDIA)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ selectedWidget () {
|
|
|
+ this.program && setTimeout(() => {
|
|
|
+ if (this.selectedWidget) {
|
|
|
+ this.$refs.widgetElements?.find(element => element.classList.contains('active'))
|
|
|
+ ?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
|
+ } else if (this.hasRootAssets) {
|
|
|
+ this.$refs.rootElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted () {
|
|
|
+ this.types = [AssetType.IMAGE, AssetType.VIDEO, AssetType.PPT, AssetType.DOC, AssetType.PDF]
|
|
|
+ this.updateInputValues()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ onEditorCreated (editor) {
|
|
|
+ this.editor = Object.seal(editor)
|
|
|
+ },
|
|
|
+ onCustomPaste (editor, event, callback) {
|
|
|
+ // 返回 true ,继续默认的粘贴行为
|
|
|
+ callback(true)
|
|
|
+
|
|
|
+ this.$nextTick(() => {
|
|
|
+ editor.setHtml(editor.getHtml()
|
|
|
+ .replace(/text-indent[^;]+;/g, ''))
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onRichConfirm (done) {
|
|
|
+ this.widget[this.widgetAttr.key] = this.richHtml
|
|
|
+ done()
|
|
|
+ },
|
|
|
+ onDragChange () {
|
|
|
+ console.log('onDragChange', this.assets)
|
|
|
+ },
|
|
|
+ onDragEnd () {
|
|
|
+ console.log('onDragEnd:', this.assets)
|
|
|
+ const sources = this.assets.map(this.transformAssetToData)
|
|
|
+ this.selectedWidget.sources = sources
|
|
|
+ },
|
|
|
+ clearInput (index) {
|
|
|
+ this.$set(this.inputValues, index, '')
|
|
|
+ },
|
|
|
+ restoreInput (index, asset) {
|
|
|
+ if (this.inputValues[index] === '') {
|
|
|
+ this.$set(this.inputValues, index, String(index + 1))
|
|
|
+ } else {
|
|
|
+ this.onTargetIndexInput(index, asset)
|
|
|
+ }
|
|
|
+ this.sortAssets()
|
|
|
+ },
|
|
|
+ sortAssets () {
|
|
|
+ const inputValues = this.inputValues
|
|
|
+ this.assets.sort((a, b) => {
|
|
|
+ const indexA = parseInt(inputValues[this.assets.indexOf(a)])
|
|
|
+ const indexB = parseInt(inputValues[this.assets.indexOf(b)])
|
|
|
+ return indexA - indexB
|
|
|
+ })
|
|
|
+ this.updateInputValues()
|
|
|
+ console.log('sortAssets', this.assets)
|
|
|
+ },
|
|
|
+ onTargetIndexInput (index, asset) {
|
|
|
+ const inputValue = this.inputValues[index]
|
|
|
+ let targetIndex = parseInt(inputValue) - 1
|
|
|
+
|
|
|
+ if (isNaN(targetIndex)) {
|
|
|
+ targetIndex = index
|
|
|
+ }
|
|
|
+
|
|
|
+ if (targetIndex < 0) {
|
|
|
+ targetIndex = 0
|
|
|
+ } else if (targetIndex >= this.assets.length) {
|
|
|
+ targetIndex = this.assets.length - 1
|
|
|
+ }
|
|
|
+
|
|
|
+ this.assets.splice(index, 1)
|
|
|
+ this.assets.splice(targetIndex, 0, asset)
|
|
|
+ this.updateInputValues()
|
|
|
+ },
|
|
|
+ updateInputValues () {
|
|
|
+ for (let i = 0; i < this.assets.length; i++) {
|
|
|
+ const inputValue = (i + 1).toString()
|
|
|
+ this.inputValues[i] = inputValue
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ show (id) {
|
|
|
+ const loading = this.$showLoading()
|
|
|
+ getProgram(id)
|
|
|
+ .then(({ data }) => {
|
|
|
+ try {
|
|
|
+ console.log('showProgram', data)
|
|
|
+ const { id, status, name, resolutionRatio, itemJsonStr } = data
|
|
|
+ const [width, height] = resolutionRatio.split('x')
|
|
|
+ if (!width || !height) {
|
|
|
+ this.showMessage('error', '布局分辨率异常,请联系管理员')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.program = {
|
|
|
+ id, status, name, resolutionRatio,
|
|
|
+ detail: {
|
|
|
+ width: Number(width),
|
|
|
+ height: Number(height),
|
|
|
+ ...JSON.parse(itemJsonStr)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 默认选中第一个
|
|
|
+ const widget = this.layers[0]
|
|
|
+ this.chooseWidget(widget, 0)
|
|
|
+
|
|
|
+ this.$refs.dialog.show()
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e)
|
|
|
+ this.showMessage('error', '布局解析失败')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.$closeLoading(loading)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ showMessage (type, message) {
|
|
|
+ this.$message({
|
|
|
+ type,
|
|
|
+ message
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 使用右侧的文本编辑器
|
|
|
+ showText (widget) {
|
|
|
+ this.isMedia = false
|
|
|
+ this.richHtml = widget.text
|
|
|
+ },
|
|
|
+ // 使用右侧图片视频选择器
|
|
|
+ showMediaChoose (widget) {
|
|
|
+ console.log('showMediaChoose', widget)
|
|
|
+ const assets = widget.sources
|
|
|
+ this.options = {
|
|
|
+ duration: widget.interval || 10,
|
|
|
+ durationEditable: !widget.sameInterval
|
|
|
+ }
|
|
|
+ this.assets = assets?.map(this.defaultTransform) || []
|
|
|
+ this.updateInputValues()
|
|
|
+ },
|
|
|
+ defaultTransform (asset) {
|
|
|
+ const data = this.transform ? this.transform(asset, this.options) : { ...asset }
|
|
|
+ if (!data.key) {
|
|
|
+ data.key = `${Date.now()}_${Math.random()
|
|
|
+ .toString(16)
|
|
|
+ .slice(2)}`
|
|
|
+ }
|
|
|
+ return data
|
|
|
+ },
|
|
|
+ onClosedPreview () {
|
|
|
+ console.log('onClosedPreview')
|
|
|
+ },
|
|
|
+ onClosed () {
|
|
|
+ this.fileDir = null
|
|
|
+ this.assets = []
|
|
|
+ this.tableAssets = []
|
|
|
+ },
|
|
|
+ onAssetDirectoryChanged (directory) {
|
|
|
+ console.log('directory', directory)
|
|
|
+ this.directoryOption = directory
|
|
|
+ this.$emit('directory-changed', directory)
|
|
|
+ // this.$refs.table?.pageTo(1)
|
|
|
+ },
|
|
|
+ getAssetsByQuery (params) {
|
|
|
+ console.log('getAssetsByQuery', params)
|
|
|
+ const { type, ...options } = params
|
|
|
+ if (type === AssetType.STREAMING_MEDIA) {
|
|
|
+ this.isStream = true
|
|
|
+ return getAssetsByQuery({
|
|
|
+ ...options,
|
|
|
+ type
|
|
|
+ })
|
|
|
+ }
|
|
|
+ this.isStream = false
|
|
|
+ if (!this.directoryOption) {
|
|
|
+ return Promise.resolve({ data: [] })
|
|
|
+ }
|
|
|
+ const { root, id, group: { path } } = this.directoryOption
|
|
|
+
|
|
|
+ return getAssetsByQuery({
|
|
|
+ ...options,
|
|
|
+ ...(root
|
|
|
+ ? { org: path }
|
|
|
+ : { treeId: id, queryRelation: '1' }),
|
|
|
+ typeList: type ? [type] : [...this.mediaTypes]
|
|
|
+ })
|
|
|
+ },
|
|
|
+ transformAsset ({ tag, type, originalName, keyName, duration, size, md5, file, diff }) {
|
|
|
+ return {
|
|
|
+ tag,
|
|
|
+ type,
|
|
|
+ name: originalName,
|
|
|
+ keyName,
|
|
|
+ size,
|
|
|
+ md5,
|
|
|
+ duration,
|
|
|
+ selected: false,
|
|
|
+ file,
|
|
|
+ diff
|
|
|
+ }
|
|
|
+ },
|
|
|
+ transformAssetToData ({ tag, type, name, keyName, duration, size, md5, file }) {
|
|
|
+ const source = {
|
|
|
+ tag,
|
|
|
+ type,
|
|
|
+ name,
|
|
|
+ keyName,
|
|
|
+ size,
|
|
|
+ md5,
|
|
|
+ duration
|
|
|
+ }
|
|
|
+ if (file.thumb && keyName !== file.thumb) {
|
|
|
+ source.thumb = file.thumb
|
|
|
+ }
|
|
|
+ return source
|
|
|
+ },
|
|
|
+ onTableRefresh (assets) {
|
|
|
+ this.tableAssets = assets
|
|
|
+ },
|
|
|
+ onChoosenAsset (asset) {
|
|
|
+ console.log('onChoosenAsset', asset)
|
|
|
+ if (this.choosen) {
|
|
|
+ this.choosen(asset)
|
|
|
+ .then(() => {
|
|
|
+ this.$refs.dialog.hide()
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ this.$refs.dialog.hide()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onToggleAsset (asset) {
|
|
|
+ asset.selected = !asset.selected
|
|
|
+ },
|
|
|
+ onViewAsset ({ file }) {
|
|
|
+ // this.$emit('view', asset)
|
|
|
+
|
|
|
+ this.onView(file)
|
|
|
+ },
|
|
|
+ onViewDir ({ name, file: { files } }) {
|
|
|
+ this.fileDir = {
|
|
|
+ name,
|
|
|
+ files: files.map(({ tag, type, keyName, size, md5, sort }) => {
|
|
|
+ return {
|
|
|
+ tag,
|
|
|
+ type,
|
|
|
+ name: `第${sort + 1}页`,
|
|
|
+ keyName,
|
|
|
+ size,
|
|
|
+ md5,
|
|
|
+ sort,
|
|
|
+ selected: false,
|
|
|
+ file: {
|
|
|
+ type,
|
|
|
+ url: keyName
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onAddAssets () {
|
|
|
+ if (this.isDir) {
|
|
|
+ this.onAddDirAssets(this.fileDir)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.tableAssets.forEach(asset => {
|
|
|
+ if (asset.selected) {
|
|
|
+ if (asset.file.files) {
|
|
|
+ this.onAddDirAssets({
|
|
|
+ name: asset.name,
|
|
|
+ files: asset.file.files
|
|
|
+ }, true)
|
|
|
+ } else {
|
|
|
+ this.onAddAsset(asset)
|
|
|
+ }
|
|
|
+ asset.selected = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onAddAsset (asset) {
|
|
|
+ const { duration, durationEditable } = this.options
|
|
|
+ console.log('this.options.', this.options)
|
|
|
+
|
|
|
+ const { tag, type, name, keyName, size, md5, file } = asset
|
|
|
+ this.assets.push({
|
|
|
+ tag,
|
|
|
+ type,
|
|
|
+ keyName,
|
|
|
+ name,
|
|
|
+ size,
|
|
|
+ md5,
|
|
|
+ file,
|
|
|
+ duration: getAssetDuration(asset, duration),
|
|
|
+ key: `${Date.now()}_${Math.random()
|
|
|
+ .toString(16)
|
|
|
+ .slice(2)}`,
|
|
|
+ disabled: !durationEditable || type === AssetType.VIDEO || type === AssetType.AUDIO,
|
|
|
+ info: `${AssetTagInfo[tag]} ${AssetTypeInfo[type]}`
|
|
|
+ })
|
|
|
+
|
|
|
+ // 将先的资源传给sources,左侧更新UI
|
|
|
+ const sources = this.assets.map(this.transformAssetToData)
|
|
|
+ this.selectedWidget.sources = sources
|
|
|
+ this.updateInputValues()
|
|
|
+ },
|
|
|
+ onAddDirAssets (fileDir, force) {
|
|
|
+ const { name: dirName, files } = fileDir
|
|
|
+ files.forEach(asset => {
|
|
|
+ if (force) {
|
|
|
+ this.onAddDirAsset(asset, dirName)
|
|
|
+ } else if (asset.selected) {
|
|
|
+ this.onAddDirAsset(asset, dirName)
|
|
|
+ asset.selected = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onAddDirAsset (asset, dirName) {
|
|
|
+ const { duration, durationEditable } = this.options
|
|
|
+ const { tag, type, keyName, size, md5, sort } = asset
|
|
|
+ this.assets.push({
|
|
|
+ tag,
|
|
|
+ type,
|
|
|
+ keyName,
|
|
|
+ name: `${dirName}第${sort + 1}页`,
|
|
|
+ size,
|
|
|
+ md5,
|
|
|
+ duration,
|
|
|
+ disabled: !durationEditable,
|
|
|
+ key: `${Date.now()}_${Math.random()
|
|
|
+ .toString(16)
|
|
|
+ .slice(2)}`,
|
|
|
+ info: `${AssetTagInfo[tag]} ${AssetTypeInfo[type]}`,
|
|
|
+ file: {
|
|
|
+ type,
|
|
|
+ url: keyName
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onDelAsset (index) {
|
|
|
+ this.assets.splice(index, 1)
|
|
|
+ this.selectedWidget.sources = this.assets
|
|
|
+ },
|
|
|
+ onConfirm (done) {
|
|
|
+ // if (this.choosen) {
|
|
|
+ // this.choosen(this.assets)
|
|
|
+ // .then(done)
|
|
|
+ // } else {
|
|
|
+ done()
|
|
|
+ // }
|
|
|
+ },
|
|
|
+ onSetImageDuration () {
|
|
|
+ const duration = this.duration
|
|
|
+ this.assets.forEach(asset => {
|
|
|
+ const { type, disabled } = asset
|
|
|
+ if (!disabled && type === AssetType.IMAGE) {
|
|
|
+ asset.duration = duration
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ hasAssets (widget) {
|
|
|
+ switch (widget.type) {
|
|
|
+ case WidgetType.MEDIA:
|
|
|
+ case WidgetType.IMAGE:
|
|
|
+ case WidgetType.VIDEO:
|
|
|
+ return true
|
|
|
+ default:
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onView (source) {
|
|
|
+ // this.$muted = this.muted
|
|
|
+ // switch (source.type) {
|
|
|
+ // case AssetType.VIDEO:
|
|
|
+ // case AssetType.AUDIO:
|
|
|
+ // if (!this.muted) {
|
|
|
+ // this.muted = true
|
|
|
+ // }
|
|
|
+ // break
|
|
|
+ // default:
|
|
|
+ // break
|
|
|
+ // }
|
|
|
+ console.log('onView:', source)
|
|
|
+ this.$refs.previewDialog.show(source)
|
|
|
+ },
|
|
|
+
|
|
|
+ onWidgetClick (widget, index, evt) {
|
|
|
+ console.log('onWidgetClick', widget)
|
|
|
+ evt.stopPropagation()
|
|
|
+ this.chooseWidget(widget, index)
|
|
|
+ },
|
|
|
+ chooseWidget (widget, index) {
|
|
|
+ if (!this.selectedWidget || this.selectedWidget !== widget) {
|
|
|
+ this.selectedWidget = widget
|
|
|
+ if (this.selectedWidget.type === 'CText') {
|
|
|
+ this.isMedia = false
|
|
|
+ this.showText(widget)
|
|
|
+ } else {
|
|
|
+ this.isMedia = true
|
|
|
+ this.showMediaChoose(widget)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.selectedWidgetIndex = index
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+
|
|
|
+.c-viewer {
|
|
|
+ &__header {
|
|
|
+ height: $height--md;
|
|
|
+ line-height: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__wrapper {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: $spacing;
|
|
|
+ right: $font-size--xl + 2 * $padding--lg;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__asserts {
|
|
|
+ width: 160px;
|
|
|
+ border-right: 1px solid $border;
|
|
|
+ background-color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__count {
|
|
|
+ height: $height--md;
|
|
|
+ line-height: 1;
|
|
|
+ background-color: #e8eaee;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__scrollbar {
|
|
|
+ ::v-deep {
|
|
|
+ .el-scrollbar__wrap {
|
|
|
+ flex: 1 1 0;
|
|
|
+ min-height: 0;
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__list {
|
|
|
+ padding: $padding;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__canvas {
|
|
|
+ position: relative;
|
|
|
+ background-color: #000;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__background {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background-color: currentColor;
|
|
|
+ z-index: -1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.mask {
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ content: "";
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: $spacing;
|
|
|
+ bottom: 0;
|
|
|
+ background-color: rgba(#000, 0.6);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.o-card {
|
|
|
+ &__checkbox {
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ left: 10px;
|
|
|
+ pointer-events: none;
|
|
|
+ z-index: 9;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__play {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ padding: 2px;
|
|
|
+ font-size: 24px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: rgba(#000, 0.4);
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &__grid {
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ right: 10px;
|
|
|
+ font-size: 18px;
|
|
|
+ z-index: 9;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__info {
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ right: 10px;
|
|
|
+ z-index: 9;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.c-transfer-area {
|
|
|
+ padding: 0 $spacing;
|
|
|
+ margin: 0 $spacing;
|
|
|
+ border-left: 1px solid $border;
|
|
|
+ border-right: 1px solid $border;
|
|
|
+}
|
|
|
+
|
|
|
+.c-transfer-area {
|
|
|
+ padding: 0 $spacing;
|
|
|
+ margin: 0 $spacing;
|
|
|
+ border-left: 1px solid $border;
|
|
|
+ border-right: 1px solid $border;
|
|
|
+}
|
|
|
+.c-textfield-area {
|
|
|
+ padding: 0 $spacing;
|
|
|
+ margin: 0 $spacing;
|
|
|
+ border-left: 1px solid $border;
|
|
|
+}
|
|
|
+
|
|
|
+.o-draggable-input {
|
|
|
+ ::v-deep .el-input__inner {
|
|
|
+ text-align: center;
|
|
|
+ padding: 0 $padding--sm;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.o-layer {
|
|
|
+ color: $black;
|
|
|
+ user-select: none;
|
|
|
+ border-radius: $radius;
|
|
|
+ background-color: #f4f7f8;
|
|
|
+ overflow: hidden;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ color: #fff;
|
|
|
+ outline: 2px solid $blue;
|
|
|
+ background-color: $blue;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|