index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <template>
  2. <div class="l-flex--col">
  3. <div class="c-sibling-item--v has-bottom-padding--sm has-bottom-border">
  4. <div
  5. class="l-flex--row inline c-sibling-item--v u-font-size--sm u-bold"
  6. :class="{ 'has-active': isAssets }"
  7. @click="onEditAssets"
  8. >
  9. <span class="c-sibling-item">上播内容</span>
  10. <i
  11. v-if="isAssets"
  12. class="c-sibling-item near el-icon-edit"
  13. />
  14. </div>
  15. <div class="l-flex--row c-sibling-item--v near">
  16. <el-select
  17. v-model="currentTarget.type"
  18. class="l-flex__none c-sibling-item u-width--sm"
  19. @change="onChangeTargetType"
  20. >
  21. <el-option
  22. v-for="option in eventTypeOptions"
  23. :key="option.value"
  24. :label="option.label"
  25. :value="option.value"
  26. />
  27. </el-select>
  28. <template v-if="isAssets">
  29. <div
  30. class="l-flex__none c-sibling-item o-button"
  31. @click="onImportDataset"
  32. >
  33. 导入
  34. </div>
  35. <div class="l-flex__fill l-flex--row right c-sibling-item u-font-size--sm u-color-black u-bold">
  36. {{ statistics }}
  37. </div>
  38. </template>
  39. <div
  40. v-else
  41. class="l-flex__auto l-flex--row c-sibling-item far"
  42. >
  43. <div
  44. class="has-active u-ellipsis"
  45. @click="onViewCurrentTarget"
  46. >
  47. {{ targetInfo }}
  48. </div>
  49. </div>
  50. </div>
  51. </div>
  52. <template v-if="isAssets">
  53. <template v-if="currentTarget.assets.length">
  54. <draggable
  55. v-model="currentTarget.assets"
  56. class="l-flex__fill c-sibling-item--v u-overflow-y--auto"
  57. handle=".mover"
  58. animation="300"
  59. >
  60. <draggable-item
  61. v-for="(asset, index) in currentTarget.assets"
  62. :key="asset.key"
  63. :item="asset"
  64. @view="onViewAsset"
  65. @del="onDelAsset(index)"
  66. />
  67. </draggable>
  68. </template>
  69. <el-empty
  70. v-else
  71. class="l-flex__fill l-flex--row center"
  72. description="请添加资源"
  73. />
  74. </template>
  75. <schema-table
  76. v-else
  77. :key="currentTarget.type"
  78. class="c-sibling-item--v"
  79. :schema="schema"
  80. row-key="id"
  81. :current-row-key="selectedId"
  82. highlight-current-row
  83. @row-click="onClickRow"
  84. />
  85. <multi-asset-transfer-dialog
  86. ref="transferDialog"
  87. title="上播内容"
  88. :choosen="onSaveAssets"
  89. ignore-duration
  90. @view="onViewAsset"
  91. />
  92. <radio-table-dialog
  93. ref="datasetDialog"
  94. title="素材包"
  95. message="请选择素材包"
  96. :schema="datasetSchema"
  97. append-to-body
  98. @confirm="onChoosenDataset"
  99. />
  100. <table-dialog
  101. ref="contentDialog"
  102. title="素材包"
  103. :schema="contentSchema"
  104. append-to-body
  105. />
  106. <preview-dialog ref="previewDialog" />
  107. <material-dialog ref="materialDialog" />
  108. </div>
  109. </template>
  110. <script>
  111. import {
  112. State,
  113. ScheduleType,
  114. EventTarget,
  115. EventTargetInfo,
  116. AssetTagInfo,
  117. AssetType,
  118. AssetTypeInfo,
  119. Dataset
  120. } from '@/constant'
  121. import { transformDatasetAssetToAsset } from '@/utils'
  122. import { getRatios } from '@/api/device'
  123. import {
  124. getDatasets,
  125. getCommonDataset
  126. } from '@/api/asset'
  127. import { getPrograms } from '@/api/program'
  128. import { getSchedules } from '@/api/calendar'
  129. import Draggable from 'vuedraggable'
  130. export default {
  131. name: 'EventTargetPicker',
  132. components: {
  133. Draggable
  134. },
  135. props: {
  136. eventTarget: {
  137. type: Object,
  138. default: null
  139. },
  140. ratio: {
  141. type: String,
  142. default: ''
  143. }
  144. },
  145. data () {
  146. return {
  147. eventTypeOptions: [
  148. { value: EventTarget.ASSETS, label: EventTargetInfo[EventTarget.ASSETS] },
  149. { value: EventTarget.PROGRAM, label: EventTargetInfo[EventTarget.PROGRAM] }/* ,
  150. { value: EventTarget.RECUR, label: EventTargetInfo[EventTarget.RECUR] } */
  151. ],
  152. currentTarget: null,
  153. datasetSchema: {
  154. list: getDatasets,
  155. condition: { type: Dataset.COMMON },
  156. cols: [
  157. { prop: 'name', label: '名称', 'align': 'center' },
  158. { type: 'invoke', render: [
  159. { label: '查看', on: this.onViewDataset }
  160. ] }
  161. ]
  162. },
  163. contentSchema: {
  164. singlePage: true,
  165. list: this.getAssets,
  166. cols: [
  167. { prop: 'tagInfo', label: '类型', width: 80, align: 'center' },
  168. { prop: 'typeInfo', label: '资源', width: 72, align: 'center' },
  169. { prop: 'file', label: '', type: 'asset', on: this.onViewAsset },
  170. { prop: 'name', label: '' },
  171. { prop: 'duration', label: '上播时长', 'align': 'center', width: 100 },
  172. { type: 'invoke', render: [
  173. { label: '查看', allow: ({ minioData }) => !!minioData, on: this.onViewAsset }
  174. ] }
  175. ]
  176. }
  177. }
  178. },
  179. computed: {
  180. isAssets () {
  181. return this.currentTarget?.type === EventTarget.ASSETS
  182. },
  183. statistics () {
  184. if (this.isAssets) {
  185. const assets = this.currentTarget.assets
  186. const length = assets.length
  187. return `共${length}条,总时长${assets.reduce((total, { duration }) => total + duration, 0)}秒`
  188. }
  189. return ''
  190. },
  191. selectedId () {
  192. return this.currentTarget?.detail?.id
  193. },
  194. targetInfo () {
  195. if (this.isAssets) {
  196. return ''
  197. }
  198. return this.currentTarget?.detail
  199. ? this.currentTarget.detail.name || '未知'
  200. : ''
  201. },
  202. schema () {
  203. if (!this.currentTarget) {
  204. return {}
  205. }
  206. const { type } = this.currentTarget
  207. return {
  208. list: this.getListInvoke,
  209. condition: { resolutionRatio: this.ratio, name: '' },
  210. filters: [
  211. { key: 'resolutionRatio', type: 'select', placeholder: '分辨率', remote: getRatios },
  212. { key: 'name', type: 'search', placeholder: '名称' }
  213. ],
  214. cols: [
  215. { render: ({ id }, h) => h(
  216. 'span',
  217. { staticClass: `el-radio__input ${id === this.selectedId ? 'is-checked' : ''}` },
  218. [h('span', { staticClass: 'el-radio__inner' })]
  219. ), width: 60, align: 'center' },
  220. type === EventTarget.PROGRAM
  221. ? { label: '缩略图', type: 'asset', render ({ img }) {
  222. return img
  223. ? { thumb: img }
  224. : null
  225. }, on: this.onView }
  226. : null,
  227. { prop: 'name', label: '名称', 'min-width': 120 },
  228. { prop: 'resolutionRatio', label: '分辨率' },
  229. { type: 'invoke', render: [
  230. { label: '查看', on: this.onView }
  231. ] }
  232. ]
  233. }
  234. }
  235. },
  236. watch: {
  237. eventTarget: {
  238. handler () {
  239. this.init()
  240. },
  241. immediate: true
  242. }
  243. },
  244. methods: {
  245. init () {
  246. const target = this.eventTarget
  247. switch (target?.type) {
  248. case EventTarget.PROGRAM:
  249. case EventTarget.RECUR:
  250. this.currentTarget = {
  251. type: target.type,
  252. detail: target,
  253. assets: []
  254. }
  255. break
  256. default:
  257. this.currentTarget = {
  258. type: EventTarget.ASSETS,
  259. detail: null,
  260. assets: (target?.sources || []).map(asset => {
  261. return {
  262. key: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
  263. disabled: asset.type === AssetType.VIDEO,
  264. info: `${AssetTagInfo[asset.tag]} ${AssetTypeInfo[asset.type]}`,
  265. file: {
  266. type: asset.type,
  267. url: asset.keyName
  268. },
  269. ...asset
  270. }
  271. })
  272. }
  273. break
  274. }
  275. },
  276. onChangeTargetType () {
  277. this.currentTarget.detail = null
  278. this.currentTarget.assets = []
  279. },
  280. getListInvoke (params) {
  281. switch (this.currentTarget.type) {
  282. case EventTarget.RECUR:
  283. return getSchedules({
  284. type: ScheduleType.RECUR,
  285. status: State.AVAILABLE,
  286. ...params
  287. })
  288. default:
  289. return getPrograms({
  290. status: State.AVAILABLE,
  291. ...params
  292. })
  293. }
  294. },
  295. onView ({ id }) {
  296. switch (this.currentTarget.type) {
  297. case EventTarget.PROGRAM:
  298. this.$refs.materialDialog.showProgram(id)
  299. break
  300. case EventTarget.RECUR:
  301. this.$refs.materialDialog.showSchedule(id)
  302. break
  303. default:
  304. break
  305. }
  306. },
  307. onViewCurrentTarget () {
  308. this.onView(this.currentTarget.detail)
  309. },
  310. onClickRow (row) {
  311. this.currentTarget.detail = row
  312. },
  313. onViewAsset ({ file }) {
  314. this.$refs.previewDialog.show(file)
  315. },
  316. onEditAssets () {
  317. if (!this.isAssets) {
  318. return
  319. }
  320. this.$refs.transferDialog.show(this.currentTarget.assets)
  321. },
  322. onSaveAssets (assets) {
  323. this.currentTarget.assets = assets
  324. return Promise.resolve()
  325. },
  326. onImportDataset () {
  327. this.$datasetAssets = null
  328. this.$refs.datasetDialog.show()
  329. },
  330. onViewDataset ({ id }) {
  331. this.$datasetId = id
  332. this.$datasetAssets = null
  333. this.$refs.contentDialog.show()
  334. },
  335. getAssets () {
  336. if (this.$datasetAssets) {
  337. return Promise.resolve({ data: this.$datasetAssets })
  338. }
  339. return getCommonDataset(this.$datasetId).then(({ data: { mediaList } }) => {
  340. this.$datasetAssets = mediaList.map(transformDatasetAssetToAsset)
  341. return { data: this.$datasetAssets }
  342. })
  343. },
  344. onChoosenDataset ({ value, done }) {
  345. if (this.$datasetId !== value.id) {
  346. this.$datasetId = value.id
  347. this.$datasetAssets = null
  348. }
  349. this.getAssets().then(({ data }) => {
  350. data.forEach(item => {
  351. const { tag, type, keyName, adDuration, name, file, minioData } = item
  352. if (minioData) {
  353. this.currentTarget.assets.push({
  354. tag,
  355. type,
  356. name,
  357. keyName,
  358. size: minioData.size,
  359. md5: minioData.md5,
  360. duration: adDuration,
  361. key: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
  362. disabled: type === AssetType.VIDEO,
  363. info: `${AssetTagInfo[tag]} ${AssetTypeInfo[type]}`,
  364. file
  365. })
  366. }
  367. })
  368. done()
  369. })
  370. },
  371. onDelAsset (index) {
  372. this.currentTarget.assets.splice(index, 1)
  373. },
  374. createEventTarget () {
  375. if (this.isAssets ? !this.currentTarget.assets.length : !this.selectedId) {
  376. this.$message({
  377. type: 'warning',
  378. message: '请选择上播内容'
  379. })
  380. return null
  381. }
  382. const { type, detail, assets } = this.currentTarget
  383. return type === EventTarget.PROGRAM
  384. ? {
  385. type,
  386. id: detail.id,
  387. name: detail.name,
  388. programUrl: `${detail.buckets}/${detail.itemConfigName}`
  389. }
  390. : type === EventTarget.RECUR
  391. ? {
  392. type,
  393. id: detail.id,
  394. name: detail.name
  395. }
  396. : {
  397. type,
  398. sources: assets.map(({ tag, type, name, keyName, duration, size, md5 }) => {
  399. return { tag, type, name, keyName, duration, size, md5 }
  400. })
  401. }
  402. },
  403. getSnapshot () {
  404. return this.currentTarget
  405. },
  406. getValue () {
  407. const eventTarget = this.createEventTarget()
  408. if (eventTarget && !this.isAssets && (!this.ratio || this.ratio !== this.currentTarget.detail.resolutionRatio)) {
  409. eventTarget.adaptive = 1
  410. }
  411. return eventTarget
  412. }
  413. }
  414. }
  415. </script>