index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <template>
  2. <wrapper
  3. fill
  4. margin
  5. background
  6. >
  7. <div class="l-flex__none l-flex--row c-step has-padding">
  8. <button
  9. class="l-flex__none c-sibling-item o-button"
  10. :class="{ hidden: active === 0 }"
  11. @click="onPresent"
  12. >
  13. 上一步
  14. </button>
  15. <el-steps
  16. :active="active"
  17. class="l-flex__fill"
  18. finish-status="success"
  19. align-center
  20. >
  21. <el-step title="选择设备" />
  22. <el-step title="选择类型" />
  23. <el-step title="选择内容" />
  24. </el-steps>
  25. <button
  26. class="l-flex__none c-sibling-item o-button"
  27. :class="{ hidden: hideNext }"
  28. @click="onNext"
  29. >
  30. {{ btnMsg }}
  31. </button>
  32. </div>
  33. <div class="l-flex__fill l-flex">
  34. <device-tree
  35. v-show="active === 0"
  36. ref="tree"
  37. class="l-flex__fill has-padding"
  38. show-ratio
  39. @change="onChange"
  40. />
  41. <div
  42. v-if="active > 0"
  43. class="c-list has-padding u-overflow-y--auto"
  44. >
  45. <div class="c-list__item u-bold">{{ resolutionRatio }}</div>
  46. <div
  47. v-for="device in selectedDevices"
  48. :key="device.id"
  49. class="c-list__item"
  50. >
  51. {{ device.name }}
  52. </div>
  53. </div>
  54. <div
  55. v-if="active === 1"
  56. class="c-list fill has-padding u-overflow-y--auto"
  57. >
  58. <div class="c-list__section">
  59. <div class="o-type">发布类型</div>
  60. <el-select v-model="eventOptions.type">
  61. <el-option
  62. v-for="option in typeOptions"
  63. :key="option.value"
  64. :label="option.label"
  65. :value="option.value"
  66. />
  67. </el-select>
  68. </div>
  69. <event-picker
  70. v-if="isEvent"
  71. ref="picker"
  72. class="c-list__section"
  73. :event="eventOptions.inst"
  74. :priority="3"
  75. vertical
  76. />
  77. </div>
  78. <div
  79. v-if="active > 1"
  80. class="c-list small has-padding u-overflow-y--auto"
  81. :class="{ large: isEvent, fill: active === 1 }"
  82. >
  83. <div class="c-list__item u-bold">{{ typeMsg }}</div>
  84. <div
  85. v-for="msg in typeMsgs"
  86. :key="msg"
  87. class="c-list__item"
  88. >
  89. {{ msg }}
  90. </div>
  91. </div>
  92. <div
  93. v-if="active >= 2"
  94. class="c-list fill has-padding u-overflow-y--auto"
  95. >
  96. <div
  97. class="c-list__item o-choose-button"
  98. @click="showChoose"
  99. >
  100. 点击选择内容
  101. </div>
  102. <schedule
  103. v-if="scheduleId"
  104. class="c-list__item"
  105. :schedule="scheduleId"
  106. />
  107. <div
  108. v-if="programId"
  109. class="o-program u-pointer"
  110. @click="toViewProgram"
  111. >
  112. {{ eventTarget.name }}
  113. </div>
  114. </div>
  115. </div>
  116. <el-dialog
  117. title="排期选择"
  118. :visible.sync="choosingSchedule"
  119. custom-class="c-dialog"
  120. :close-on-click-modal="false"
  121. >
  122. <c-table
  123. v-if="choosingSchedule"
  124. :options="scheduleOptions"
  125. @pagination="getSchedules"
  126. @row-dblclick="onChooseSchedule"
  127. >
  128. <el-table-column
  129. prop="name"
  130. label="名称"
  131. align="center"
  132. show-overflow-tooltip
  133. />
  134. </c-table>
  135. </el-dialog>
  136. <el-dialog
  137. :visible.sync="choosingEventTarget"
  138. title="节目选择"
  139. custom-class="c-dialog"
  140. :close-on-click-modal="false"
  141. >
  142. <event-target-choose
  143. v-if="choosingEventTarget"
  144. :ratio="resolutionRatio"
  145. @choose="onChooseEventTarget"
  146. />
  147. </el-dialog>
  148. </wrapper>
  149. </template>
  150. <script>
  151. import { getSchedules } from '@/api/calendar'
  152. import { publish } from '@/api/publish'
  153. import { createListOptions } from '@/utils'
  154. import {
  155. State,
  156. ScheduleType,
  157. EventFreq,
  158. EventTarget,
  159. PublishType
  160. } from '@/constant'
  161. import EventPicker from '@/components/EventPicker'
  162. import EventTargetChoose from '@/components/EventTargetChoose'
  163. import DeviceTree from '@/components/DeviceTree'
  164. export default {
  165. name: 'ScheduleDeploy',
  166. components: {
  167. DeviceTree,
  168. EventPicker,
  169. EventTargetChoose
  170. },
  171. data () {
  172. return {
  173. typeOptions: [
  174. { value: PublishType.CALENDAR, label: '排期' },
  175. { value: PublishType.EVENT, label: '插播' }
  176. ],
  177. freqOptions: [
  178. { value: EventFreq.ONCE, label: '单次' },
  179. { value: EventFreq.WEEKLY, label: '每周重复' }
  180. ],
  181. weeks: [
  182. { value: '0', label: '日' },
  183. { value: '1', label: '一' },
  184. { value: '2', label: '二' },
  185. { value: '3', label: '三' },
  186. { value: '4', label: '四' },
  187. { value: '5', label: '五' },
  188. { value: '6', label: '六' }
  189. ],
  190. scheduleOptions: null,
  191. active: 0,
  192. selectedDevices: [],
  193. eventOptions: null,
  194. choosingSchedule: false,
  195. choosingEventTarget: false,
  196. eventTarget: null
  197. }
  198. },
  199. computed: {
  200. hideNext () {
  201. switch (this.active) {
  202. case 0:
  203. return this.selectedDevices.length === 0
  204. case 2:
  205. return !this.eventTarget
  206. default:
  207. return false
  208. }
  209. },
  210. btnMsg () {
  211. return this.active < 3 ? '下一步' : '发布'
  212. },
  213. resolutionRatio () {
  214. return this.selectedDevices[0]?.resolutionRatio
  215. },
  216. typeMsg () {
  217. return this.typeOptions[this.eventOptions?.type - 1]?.label
  218. },
  219. typeMsgs () {
  220. const msgs = []
  221. if (this.eventOptions?.type === PublishType.EVENT) {
  222. const { freq, start, until, byDay, startTime, endTime } = this.eventOptions.inst
  223. switch (freq) {
  224. case EventFreq.WEEKLY:
  225. msgs.push(`自${start.split(' ')[0]}开始`)
  226. until && msgs.push(`至${until.split(' ')[0]}前`)
  227. msgs.push(`每周${byDay.split(',').map(val => ['日', '一', '二', '三', '四', '五', '六'][val]).join('、')}`)
  228. msgs.push(`${startTime} - ${endTime}`)
  229. break
  230. default:
  231. msgs.push(`自${start}开始`)
  232. until && msgs.push(`${until}结束`)
  233. break
  234. }
  235. }
  236. return msgs
  237. },
  238. isCalendar () {
  239. return this.eventOptions?.type === PublishType.CALENDAR
  240. },
  241. isEvent () {
  242. return this.eventOptions?.type === PublishType.EVENT
  243. },
  244. scheduleId () {
  245. return this.eventOptions?.type === PublishType.CALENDAR || this.eventTarget?.type === EventTarget.RECUR ? this.eventTarget?.id : null
  246. },
  247. programId () {
  248. return this.eventTarget?.type === EventTarget.PROGRAM ? this.eventTarget.id : null
  249. }
  250. },
  251. methods: {
  252. onPresent () {
  253. if (this.active > 0) {
  254. if (this.active === 3) {
  255. this.active -= 2
  256. } else {
  257. this.active -= 1
  258. }
  259. }
  260. },
  261. onNext () {
  262. let pass = false
  263. switch (this.active) {
  264. case 0:
  265. pass = this.checkDevices()
  266. break
  267. case 1:
  268. pass = this.checkEventOptions()
  269. if (pass && this.checkEventTarget()) {
  270. this.active += 1
  271. }
  272. break
  273. case 2:
  274. pass = this.checkEventTarget()
  275. break
  276. case 3:
  277. this.publish()
  278. break
  279. default:
  280. return
  281. }
  282. if (pass) {
  283. this.active += 1
  284. }
  285. },
  286. _onError (message) {
  287. this.$message({
  288. type: 'warning',
  289. message
  290. })
  291. return false
  292. },
  293. onChange (devices) {
  294. this.selectedDevices = devices
  295. },
  296. checkDevices () {
  297. const devices = this.selectedDevices
  298. const length = devices.length
  299. if (!length) {
  300. return this._onError('请选择目标设备')
  301. }
  302. const resolutionRatio = this.resolutionRatio
  303. if (devices.some(device => device.resolutionRatio !== resolutionRatio)) {
  304. return this._onError('选择的设备分辨率不一致')
  305. }
  306. if (this.eventOptions) {
  307. if (this.resolutionRatio !== this.eventOptions.ratio) {
  308. this.eventTarget = null
  309. }
  310. } else {
  311. this.eventOptions = this.createEventOptions()
  312. }
  313. return true
  314. },
  315. createEventOptions () {
  316. return {
  317. ratio: this.resolutionRatio,
  318. type: PublishType.CALENDAR,
  319. inst: null
  320. }
  321. },
  322. checkEventOptions () {
  323. if (this.eventOptions.type === PublishType.EVENT) {
  324. const event = this.$refs.picker.getValue()
  325. if (!event) {
  326. return false
  327. }
  328. this.eventOptions.inst = event
  329. }
  330. return true
  331. },
  332. showChoose () {
  333. switch (this.eventOptions?.type) {
  334. case PublishType.CALENDAR:
  335. this.scheduleOptions = createListOptions({
  336. status: State.RESOLVED,
  337. type: ScheduleType.COMPLEX,
  338. resolutionRatio: this.resolutionRatio
  339. })
  340. this.getSchedules()
  341. this.choosingSchedule = true
  342. break
  343. case PublishType.EVENT:
  344. this.choosingEventTarget = true
  345. break
  346. default:
  347. break
  348. }
  349. },
  350. closeChoose () {
  351. switch (this.eventOptions?.type) {
  352. case PublishType.CALENDAR:
  353. this.choosingSchedule = false
  354. break
  355. case PublishType.EVENT:
  356. this.choosingEventTarget = false
  357. break
  358. default:
  359. break
  360. }
  361. },
  362. getSchedules () {
  363. const options = this.scheduleOptions
  364. options.error = false
  365. options.loading = true
  366. getSchedules(options.params).then(
  367. ({ data, totalCount }) => {
  368. options.list = data
  369. options.totalCount = totalCount
  370. },
  371. () => {
  372. options.error = true
  373. options.list = []
  374. }
  375. ).finally(() => {
  376. options.loading = false
  377. })
  378. },
  379. onChooseSchedule ({ id, name }) {
  380. this.closeChoose()
  381. this.eventTarget = { id, name }
  382. if (this.active === 2) {
  383. this.onNext()
  384. }
  385. },
  386. onChooseEventTarget (value) {
  387. this.closeChoose()
  388. this.eventTarget = value
  389. if (this.active === 2) {
  390. this.onNext()
  391. }
  392. },
  393. checkEventTarget () {
  394. if (this.isCalendar && this.eventTarget?.type || this.isEvent && this.eventTarget && !this.eventTarget.type) {
  395. this.eventTarget = null
  396. }
  397. if (!this.eventTarget) {
  398. this.showChoose()
  399. return false
  400. }
  401. return true
  402. },
  403. toViewProgram () {
  404. window.open(this.$router.resolve({
  405. name: 'view',
  406. params: { id: this.programId }
  407. }).href, '_blank')
  408. },
  409. _getPublishTarget () {
  410. switch (this.eventOptions.type) {
  411. case PublishType.CALENDAR:
  412. return {
  413. type: PublishType.CALENDAR,
  414. detail: this.eventTarget.id
  415. }
  416. case PublishType.EVENT:
  417. return {
  418. type: PublishType.EVENT,
  419. detail: {
  420. ...this.eventOptions.inst,
  421. target: this.eventTarget
  422. }
  423. }
  424. default:
  425. return null
  426. }
  427. },
  428. publish () {
  429. console.log('publish')
  430. const devices = this.selectedDevices
  431. this.$confirm(`确定对设备 ${devices.map(device => device.name)} 发布 ${this.typeMsg} ${this.eventTarget.name}?`).then(() => {
  432. return publish(devices.map(device => device.id), this._getPublishTarget(), {
  433. programCalendarName: this.eventTarget.name,
  434. resolutionRatio: this.resolutionRatio
  435. })
  436. }).then(() => {
  437. this.active = 0
  438. this.$refs.tree.reset()
  439. this.eventOptions = null
  440. this.eventTarget = null
  441. })
  442. }
  443. }
  444. }
  445. </script>
  446. <style lang="scss" scoped>
  447. .c-step {
  448. border-bottom: 1px solid $gray--light;
  449. .hidden {
  450. visibility: hidden;
  451. }
  452. }
  453. .c-list {
  454. flex: 1 0 200px;
  455. min-width: 200px;
  456. max-width: 300px;
  457. padding-right: $spacing;
  458. color: $black;
  459. & + & {
  460. border-left: 1px solid $gray--light;
  461. }
  462. &.small {
  463. min-width: 100px;
  464. max-width: 200px;
  465. }
  466. &.large {
  467. min-width: 240px;
  468. max-width: 320px;
  469. }
  470. &.fill {
  471. max-width: initial;
  472. }
  473. &__item + &__item {
  474. margin-top: 8px;
  475. }
  476. &__section + &__section {
  477. margin-top: $spacing;
  478. }
  479. }
  480. .o-type {
  481. margin-bottom: 8px;
  482. font-size: 14px;
  483. font-weight: bold;
  484. line-height: 1;
  485. }
  486. .o-choose-button {
  487. padding: 20px 0;
  488. font-size: 20px;
  489. text-align: center;
  490. border: 1px solid $gray--light;
  491. color: $black;
  492. cursor: pointer;
  493. &:hover {
  494. color: #409eff;
  495. border-color: #c6e2ff;
  496. background-color: $blue--light;
  497. }
  498. &:active {
  499. color: #3a8ee6;
  500. border-color: #3a8ee6;
  501. }
  502. }
  503. .o-program {
  504. margin-top: $spacing;
  505. font-size: 24px;
  506. font-weight: bold;
  507. &:hover {
  508. color: #409eff;
  509. }
  510. }
  511. </style>