Browse Source

feat: 添加使用模板弹窗,还差确认和接口调试

HOME\tianlin01 11 months ago
parent
commit
6da4a9ce2c

+ 14 - 0
alais.config.js

@@ -0,0 +1,14 @@
+const path = require('path')
+
+function resolve (dir) {
+  return path.join(__dirname, dir)
+}
+
+module.exports = {
+  resolve: {
+    alias: {
+      '@': resolve('src')
+    }
+  }
+}
+

+ 1 - 0
src/api/asset.js

@@ -64,6 +64,7 @@ export function getAsset (keyName) {
 }
 
 export function getAssetsByQuery (query) {
+  console.log(query)
   const { pageNum: currentPage, pageSize: pageCount, typeList, type, ...params } = query
   return request({
     url: '/minio-data/listByPage',

+ 0 - 1
src/api/program.js

@@ -28,7 +28,6 @@ export function getProgramDrafts (query) {
     })
   })
 }
-
 export function getTemplateType () {
   return tenantRequest({
     url: '/device/resolutionRatio',

+ 7 - 0
src/api/program.js.rej

@@ -0,0 +1,7 @@
+diff a/src/api/program.js b/src/api/program.js	(rejected hunks)
+@@ -50 +49 @@ export function getTemplateType () {
+-//获取模板数据
++// 获取模板数据
+@@ -63,2 +61,0 @@ export function getProgramTemplate (query) {
+-
+-

+ 987 - 0
src/components/dialog/TemplateAssetChooseDialog/index.vue

@@ -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&#45;&#45;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>

+ 18 - 0
src/constant.js

@@ -13,6 +13,24 @@ export const GATEWAY_CAMERA_RECORD = `${GATEWAY_WS}${process.env.VUE_APP_CAMERA_
 
 export const ONE_DAY = 3600 * 24 * 1000
 
+export const TemplateType = {
+  CWGK: 1,
+  GYXX: 2,
+  GG: 3,
+  YJFB: 4,
+  ZCXC: 5,
+  ZFTZ: 6
+}
+
+export const TemplateTypeInfo = {
+  [TemplateType.CWGK]: '村务公开',
+  [TemplateType.GYXX]: '公益信息',
+  [TemplateType.GG]: '广告',
+  [TemplateType.YJFB]: '应急发布',
+  [TemplateType.ZCXC]: '政策宣传',
+  [TemplateType.ZFTZ]: '政府通知'
+}
+
 export const AssetType = {
   IMAGE: 1,
   VIDEO: 2,

+ 63 - 8
src/views/screen/material/program/ProgramDesigner.vue

@@ -140,7 +140,10 @@
         </div>
       </div>
     </confirm-dialog>
-    <template-asset-choose-dialog ref="assetChooseDialog" />
+    <template-asset-choose-dialog
+      ref="assetChooseDialog"
+      :transform="transformDataToAsset"
+    />
   </wrapper>
 </template>
 
@@ -153,13 +156,15 @@ import {
   deleteProgramDraft,
   copyProgramDraft,
   copyProgram,
-  getProgramTemplate,
-  getTemplateType
+  getProgramTemplate
 } from '@/api/program'
 import Program from './components/Program.vue'
 import ProgramStore from './components/ProgramStore.vue'
 // import TemperatureConfigDialog from '@/views/realm/settings/components/TemperatureThresholdConfigDialog'
 import TemplateAssetChooseDialog from '@/components/dialog/TemplateAssetChooseDialog'
+import {
+  AssetTag, AssetTagInfo, AssetType, AssetTypeInfo, TemplateType, TemplateTypeInfo
+} from '@/constant'
 
 export default {
   name: 'ProgramDesigner',
@@ -187,8 +192,21 @@ export default {
       templateSchema: {
         list: getProgramTemplate,
         filters: [
-          { key: 'resolutionRatio', type: 'select', placeholder: '分辨率', remote: getRatios },
-          { key: 'templateType', type: 'select', placeholder: '模板类型', remote: getTemplateType },
+          // { key: 'resolutionRatio', type: 'select', placeholder: '分辨率', remote: getRatios },
+          {
+            key: 'templateType', type: 'select', placeholder: '模板类型', options:
+              [TemplateType.ZFTZ,
+               TemplateType.GYXX,
+               TemplateType.GG,
+               TemplateType.YJFB,
+               TemplateType.ZCXC,
+               TemplateType.ZFTZ].map(type => {
+                return {
+                  value: type,
+                  label: TemplateTypeInfo[type]
+                }
+              })
+          },
           { key: 'name', type: 'search', placeholder: '模板名称' },
           { type: 'refresh' }
         ]
@@ -217,6 +235,33 @@ export default {
     window.removeEventListener('message', this.onMessage)
   },
   methods: {
+    transformDataToAsset ({
+      tag = AssetTag.AD,
+      type,
+      keyName,
+      name,
+      size,
+      md5,
+      duration,
+      thumb
+    }, { durationEditable }) {
+      return {
+        tag,
+        type,
+        keyName,
+        name,
+        size,
+        md5,
+        duration,
+        disabled: !durationEditable || type === AssetType.VIDEO || type === AssetType.AUDIO,
+        info: `${AssetTagInfo[tag]} ${AssetTypeInfo[type]}`,
+        file: {
+          type,
+          url: keyName,
+          thumb
+        }
+      }
+    },
     getRatios () {
       return getRatios()
         .then(({ data }) => {
@@ -338,9 +383,19 @@ export default {
       // let idtest = '1856226701478084610'
       this.$refs.materialTemplateDialog.showProgram(id)
     },
-    onUse ({ id, name }) {
-      console.log(id, name)
-      this.$refs.assetChooseDialog.show()
+    onUse (item) {
+      console.log(item)
+
+      // this.$refs.transferDialog.show(
+      //   this.widget[attr.key],
+      //   {
+      //     types: attr.options.type,
+      //     directory: this.directoryOption,
+      //     duration: this.widget.interval || 10,
+      //     durationEditable: !this.widget.sameInterval
+      //   }
+      // )
+      this.$refs.assetChooseDialog.show(item.id)
     }
   }
 }