index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <template>
  2. <wrapper
  3. fill
  4. margin
  5. background
  6. >
  7. <div class="l-flex__none l-flex--row c-step">
  8. <div class="l-flex__none c-sibling-item o-button hidden" />
  9. <div class="l-flex__fill u-text-center">
  10. 请选择需要上播的设备对应的宽高比并配置相关上播内容
  11. </div>
  12. <button
  13. class="l-flex__none c-sibling-item o-button"
  14. :class="{ hidden: hideNext }"
  15. @click="publish"
  16. >
  17. 发布
  18. </button>
  19. </div>
  20. <div class="l-flex__fill l-flex has-padding">
  21. <ratio-tree-single
  22. ref="tree"
  23. class="c-sibling-item"
  24. @change="onChange"
  25. />
  26. <div class="c-sibling-item far c-list u-overflow-y--auto">
  27. <div class="l-flex--row c-sibling-item--v u-bold">上播时间</div>
  28. <div class="c-sibling-item--v far u-font-size--sm">优先级</div>
  29. <el-select
  30. v-model="priority"
  31. class="c-sibling-item--v near"
  32. >
  33. <el-option
  34. v-for="option in priorityOptions"
  35. :key="option.value"
  36. :label="option.label"
  37. :value="option.value"
  38. />
  39. </el-select>
  40. <div class="c-sibling-item--v u-font-size--sm u-required">开始时间</div>
  41. <el-date-picker
  42. v-model="startDate"
  43. class="c-sibling-item--v near"
  44. type="datetime"
  45. placeholder="请选择开始时间"
  46. value-format="yyyy-MM-dd HH:mm:ss"
  47. :picker-options="pickerOptions"
  48. @change="onDateTimeChange('startDate')"
  49. />
  50. <div class="c-sibling-item--v u-font-size--sm u-required">结束时间</div>
  51. <el-date-picker
  52. v-model="endDate"
  53. class="c-sibling-item--v near"
  54. type="datetime"
  55. popper-class="is-hide-now"
  56. :disabled="!startDate"
  57. placeholder="请选择结束时间"
  58. :default-value="defaultEndDateDate"
  59. value-format="yyyy-MM-dd HH:mm:ss"
  60. :picker-options="endPickerOptions"
  61. @change="onDateTimeChange('endDate')"
  62. />
  63. </div>
  64. <div class="l-flex--col c-sibling-item far c-list fill">
  65. <div class="l-flex--row c-sibling-item--v u-bold">
  66. <span class="c-sibling-item u-bold">上播内容</span>
  67. <i
  68. class="c-sibling-item el-icon-circle-plus-outline u-color--blue u-pointer"
  69. @click="onAddAsset"
  70. />
  71. </div>
  72. <draggable
  73. v-model="assets"
  74. class="l-flex__fill c-sibling-item--v far u-overflow-y--auto"
  75. handle=".mover"
  76. animation="300"
  77. >
  78. <div
  79. v-for="(asset, index) in assets"
  80. :key="asset.key"
  81. class="l-flex--row c-sibling-item--v o-publish-asset"
  82. >
  83. <div class="l-flex__auto l-flex--row mover">
  84. <i class="l-flex__none o-publish-asset__mover el-icon-sort has-active" />
  85. <div
  86. class="l-flex__auto o-publish-asset__name u-ellipsis has-active u-pointer"
  87. @click="onViewAssetItem(asset)"
  88. >
  89. {{ asset.tagInfo }} {{ asset.name }}
  90. </div>
  91. </div>
  92. <template v-if="needDuration">
  93. <span class="l-flex__none u-color--info">上播时长(s):</span>
  94. <el-input-number
  95. v-model="asset.duration"
  96. class="l-flex__none o-publish-asset__seconds"
  97. controls-position="right"
  98. :min="1"
  99. :max="86400"
  100. :step="1"
  101. :disabled="asset.disabled"
  102. step-strictly
  103. />
  104. </template>
  105. <i
  106. class="l-flex__none o-publish-asset__del el-icon-delete has-active u-pointer"
  107. @click="onDelAsset(asset, index)"
  108. />
  109. </div>
  110. </draggable>
  111. </div>
  112. </div>
  113. <table-dialog
  114. ref="assetTableDialog"
  115. title="上播内容选择"
  116. :schema="assetTableSchema"
  117. @choosen="onChoosenAsset"
  118. />
  119. <preview-dialog ref="previewDialog" />
  120. </wrapper>
  121. </template>
  122. <script>
  123. import {
  124. AssetTagInfo,
  125. AssetType,
  126. EventFreq,
  127. EventTarget,
  128. PublishType,
  129. PublishTargetType,
  130. EventPriority,
  131. EventPriorityInfo
  132. } from '@/constant'
  133. import {
  134. parseTime,
  135. getAssetDuration
  136. } from '@/utils'
  137. import { toDate } from '@/utils/event'
  138. import { publish } from '@/api/platform'
  139. import { assetTableMixin } from '@/mixins/asset-table'
  140. import Draggable from 'vuedraggable'
  141. export default {
  142. name: 'DeployRatio',
  143. components: {
  144. Draggable
  145. },
  146. mixins: [assetTableMixin],
  147. data () {
  148. return {
  149. priority: EventPriority.INSERTED,
  150. priorityOptions: [
  151. { value: EventPriority.SCHEDULING, label: EventPriorityInfo[EventPriority.SCHEDULING] },
  152. { value: EventPriority.INSERTED, label: EventPriorityInfo[EventPriority.INSERTED] },
  153. { value: EventPriority.EMBEDDED, label: EventPriorityInfo[EventPriority.EMBEDDED] },
  154. { value: EventPriority.EMERGENT, label: EventPriorityInfo[EventPriority.EMERGENT] }
  155. ],
  156. productType: null,
  157. startDate: null,
  158. endDate: null,
  159. assets: []
  160. }
  161. },
  162. computed: {
  163. hideNext () {
  164. return !this.productType || !this.startDate || !this.endDate || this.assets.length === 0
  165. },
  166. defaultEndDateDate () {
  167. if (this.startDate) {
  168. const date = new Date(this.startDate)
  169. date.setDate(date.getDate() + 1)
  170. return `${parseTime(date, '{y}-{m}-{d}')} 00:00:00`
  171. }
  172. return null
  173. },
  174. pickerOptions () {
  175. return {
  176. disabledDate: this.isDisableDate
  177. }
  178. },
  179. endPickerOptions () {
  180. return {
  181. disabledDate: this.isDisableEndDate
  182. }
  183. },
  184. minDate () {
  185. const now = new Date()
  186. return new Date(now.getFullYear(), now.getMonth(), now.getDate())
  187. },
  188. needDuration () {
  189. return this.assets.length > 1
  190. }
  191. },
  192. methods: {
  193. isDisableDate (date) {
  194. return date < this.minDate
  195. },
  196. isDisableEndDate (date) {
  197. if (date < this.minDate) {
  198. return true
  199. }
  200. const startDate = this.startDate
  201. if (startDate) {
  202. return date < new Date(startDate.replace(/\d{2}:\d{2}:\d{2}$/, '00:00:00'))
  203. }
  204. return false
  205. },
  206. onDateTimeChange (type) {
  207. const { startDate, endDate } = this
  208. if (startDate && endDate && startDate > endDate) {
  209. if (type === 'startDate') {
  210. this.endDate = startDate
  211. } else {
  212. this.startDate = endDate
  213. }
  214. }
  215. },
  216. onChange (productType) {
  217. this.productType = productType
  218. },
  219. onAddAsset () {
  220. this.$refs.assetTableDialog.show()
  221. },
  222. onChoosenAsset ({ value, done }) {
  223. const { tag, type, keyName, originalName, size, md5 } = value
  224. this.assets.push({
  225. key: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
  226. tag,
  227. type,
  228. keyName,
  229. size,
  230. md5,
  231. tagInfo: AssetTagInfo[tag],
  232. name: originalName,
  233. duration: getAssetDuration(value),
  234. disabled: type === AssetType.VIDEO
  235. })
  236. done()
  237. },
  238. onDelAsset (asset, index) {
  239. this.assets.splice(index, 1)
  240. },
  241. onError (message) {
  242. this.$message({
  243. type: 'warning',
  244. message
  245. })
  246. return Promise.reject()
  247. },
  248. getPublishTarget () {
  249. if (this.startDate === this.endDate) {
  250. return this.onError('开始时间与结束时间不能一样')
  251. }
  252. const sources = this.assets.map(({ tag, type, keyName, duration, size, md5 }) => {
  253. return { tag, type, keyName, duration, size, md5 }
  254. })
  255. if (sources.length === 1) {
  256. sources.duration = Math.floor((toDate(this.endDate) - toDate(this.startDate)) / 1000)
  257. }
  258. return Promise.resolve({
  259. type: PublishTargetType.EVENT,
  260. detail: {
  261. freq: EventFreq.ONCE,
  262. start: this.startDate,
  263. until: this.endDate,
  264. priority: this.priority,
  265. target: {
  266. type: EventTarget.ASSETS,
  267. sources
  268. }
  269. }
  270. })
  271. },
  272. publish () {
  273. this.getPublishTarget().then(
  274. publishTarget => this.$confirm(
  275. '发布需审核生效,操作完成后请通知相关人员进行审核',
  276. `立即发布?`,
  277. {
  278. type: 'warning',
  279. confirmButtonText: '发布'
  280. }
  281. ).then(
  282. () => publish(
  283. PublishType.PRODUCT_TYPE,
  284. [this.productType.id],
  285. publishTarget,
  286. {
  287. programCalendarName: this.productType.name
  288. }
  289. )
  290. )
  291. ).then(() => {
  292. this.$refs.tree.reset()
  293. this.productType = null
  294. this.assets = []
  295. })
  296. }
  297. }
  298. }
  299. </script>
  300. <style lang="scss" scoped>
  301. .c-step {
  302. padding: $spacing 0;
  303. margin: 0 $spacing;
  304. border-bottom: 1px solid $gray--light;
  305. .hidden {
  306. visibility: hidden;
  307. }
  308. }
  309. .c-list {
  310. flex: 0 0 auto;
  311. min-width: 200px;
  312. color: $black;
  313. & + & {
  314. padding-left: $spacing;
  315. border-left: 1px solid $gray--light;
  316. }
  317. &.fill {
  318. flex: 1 0 auto;
  319. }
  320. }
  321. .o-publish-asset {
  322. padding: 4px 0;
  323. border: 1px solid $gray;
  324. border-radius: $radius--mini;
  325. &.sortable-chosen {
  326. border-color: #c6e2ff;
  327. background-color: $blue--light;
  328. }
  329. &__mover {
  330. padding: 10px 16px;
  331. font-size: 18px;
  332. cursor: move;
  333. }
  334. &__name {
  335. padding: 16px 10px 16px 0;
  336. }
  337. &__seconds {
  338. width: 120px;
  339. }
  340. &__del {
  341. display: inline-flex;
  342. justify-content: center;
  343. align-items: center;
  344. width: 50px;
  345. height: 40px;
  346. color: $gray;
  347. font-size: 18px;
  348. &.mini {
  349. width: 40px;
  350. }
  351. &:hover {
  352. color: $primary;
  353. }
  354. }
  355. }
  356. </style>