Browse Source

feat: event supports configuration by time

Casper Dai 3 years ago
parent
commit
b3bd16b1f0

+ 24 - 22
src/api/calendar.js

@@ -74,32 +74,34 @@ function checkSchedule (type, events) {
     })
     return false
   }
-  if (type === ScheduleType.CALENDAR) {
-    const now = Date.now()
-    if (!events.some(({ endDateTime }) => new Date(endDateTime) > now)) {
+  switch (type) {
+    case ScheduleType.RECUR:
+      if (events.length < 2) {
+        Message({
+          type: 'warning',
+          message: '请至少添加两个节目'
+        })
+        return false
+      }
+      break
+    case ScheduleType.COMPLEX:
+      if (type === ScheduleType.COMPLEX) {
+        const now = Date.now()
+        if (!events.some(({ until }) => !until || new Date(until) > now)) {
+          Message({
+            type: 'warning',
+            message: '无有效节目,请添加节目'
+          })
+          return false
+        }
+      }
+      break
+    default:
       Message({
         type: 'warning',
-        message: '无有效节目,请添加节目'
+        message: '不支持的类型'
       })
       return false
-    }
-  }
-  if (type === ScheduleType.RECUR && events.length < 2) {
-    Message({
-      type: 'warning',
-      message: '请至少添加两个节目'
-    })
-    return false
-  }
-  if (type === ScheduleType.COMPLEX) {
-    const now = Date.now()
-    if (!events.some(({ until }) => !until || new Date(until) > now)) {
-      Message({
-        type: 'warning',
-        message: '无有效节目,请添加节目'
-      })
-      return false
-    }
   }
   return true
 }

+ 244 - 27
src/components/EventPicker/index.vue

@@ -3,11 +3,21 @@
     class="c-grid-form medium c-event"
     :class="{ row: !vertical, col: vertical }"
   >
+    <div class="c-grid-form__label required">播放内容</div>
+    <div class="c-event__option c-event__program u-pointer">
+      <div
+        class="c-event__name has-padding--h"
+        @click="onChoose"
+      >
+        <div class="u-ellipsis">{{ msg }}</div>
+      </div>
+    </div>
     <div class="c-grid-form__label">播放方式</div>
     <div>
       <el-select
         v-model="eventOptions.freq"
         class="c-event__option"
+        @change="onEventTypeChanged"
       >
         <el-option
           v-for="option in freqOptions"
@@ -36,7 +46,6 @@
         :disabled="!eventOptions.start"
         placeholder="请选择失效时间"
         value-format="yyyy-MM-dd HH:mm:ss"
-        :default-value="eventOptions.start"
         :picker-options="endPickerOptions"
         @change="onDateTimeChange('until')"
       />
@@ -93,24 +102,62 @@
         :disabled="!eventOptions.start"
         placeholder="请选择失效日期"
         value-format="yyyy-MM-dd HH:mm:ss"
-        :default-value="eventOptions.start"
         :picker-options="endPickerOptions"
         @change="onDateTimeChange('until')"
       />
     </template>
+    <template v-if="isCount">
+      <div class="c-grid-form__label">次数</div>
+      <el-input-number
+        v-model="eventOptions.count"
+        :min="1"
+        :max="99999999"
+        :step="1"
+        step-strictly
+      />
+      <div class="c-grid-form__label required">开始时间</div>
+      <el-date-picker
+        v-model="eventOptions.start"
+        class="c-event__option"
+        type="datetime"
+        placeholder="请选择生效时间"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        :picker-options="pickerOptions"
+      />
+      <template v-if="untilDate">
+        <div class="c-grid-form__label required">结束时间</div>
+        <el-date-picker
+          :value="untilDate"
+          class="c-event__option"
+          type="datetime"
+          disabled
+        />
+      </template>
+    </template>
+    <event-target-dialog
+      ref="eventTargetDialog"
+      v-bind="$attrs"
+      @choosen="onChoosen"
+    />
   </div>
 </template>
 
 <script>
 import {
   EventPriority,
-  EventFreq
+  EventFreq,
+  EventTarget
 } from '@/constant'
+import { getSchedule } from '@/api/calendar'
+import { getProgram } from '@/api/program'
 import {
   isMoreThanOneWeek,
   getNearestHitDate,
   isOverDay
 } from '@/utils/event'
+import { parseTime } from '@/utils'
+
+const EventFreqCount = 'COUNT'
 
 export default {
   name: 'EventPicker',
@@ -132,7 +179,8 @@ export default {
     return {
       freqOptions: [
         { value: EventFreq.ONCE, label: '时段播放' },
-        { value: EventFreq.WEEKLY, label: '重复播放' }
+        { value: EventFreq.WEEKLY, label: '重复播放' },
+        { value: EventFreqCount, label: '按次播放' }
       ],
       weeks: [
         { value: '0', label: '日' },
@@ -143,15 +191,19 @@ export default {
         { value: '5', label: '五' },
         { value: '6', label: '六' }
       ],
-      eventOptions: null
+      eventOptions: null,
+      eventTarget: null
     }
   },
   computed: {
     isOnce () {
-      return this.eventOptions?.freq === EventFreq.ONCE
+      return this.eventOptions.freq === EventFreq.ONCE
     },
     isWeekly () {
-      return this.eventOptions?.freq === EventFreq.WEEKLY
+      return this.eventOptions.freq === EventFreq.WEEKLY
+    },
+    isCount () {
+      return this.eventOptions.freq === EventFreqCount
     },
     pickerOptions () {
       return {
@@ -167,8 +219,18 @@ export default {
       const now = new Date()
       return new Date(now.getFullYear(), now.getMonth(), now.getDate())
     },
-    startDate () {
-      return this.eventOptions?.start && new Date(this.eventOptions.start.replace(/\d{2}:\d{2}:\d{2}$/, '00:00:00'))
+    msg () {
+      return this.eventTarget ? this.eventTarget.name : '点击选择播放内容'
+    },
+    untilDate () {
+      if (this.isCount && this.eventTarget) {
+        const { start, count } = this.eventOptions
+        const { duration } = this.eventTarget
+        if (start && duration) {
+          return new Date(start).getTime() + duration * count * 1000
+        }
+      }
+      return null
     }
   },
   watch: {
@@ -184,10 +246,19 @@ export default {
       return date < this.minDate
     },
     isDisableEndDate (date) {
-      return date < this.minDate || this.eventOptions.start && date < new Date(this.eventOptions.start)
+      if (date < this.minDate) {
+        return true
+      }
+      if (this.eventOptions.start) {
+        if (this.isWeekly) {
+          return date <= new Date(this.eventOptions.start)
+        }
+        return date < new Date(this.eventOptions.start)
+      }
+      return false
     },
     init () {
-      const { byDay, ...options } = {
+      const { freq, byDay, target, ...options } = {
         freq: EventFreq.ONCE,
         start: null,
         until: null,
@@ -195,10 +266,25 @@ export default {
         endTime: '00:00:00',
         ...this.event
       }
-      this.eventOptions = {
-        ...options,
-        byDay: byDay ? byDay.split(',') : []
+
+      if (freq === EventFreq.ONCE && target?.duration) {
+        const { start, until } = options
+        this.eventOptions = {
+          ...options,
+          freq: EventFreqCount,
+          count: (new Date(until) - new Date(start)) / target.duration / 1000,
+          byDay: byDay ? byDay.split(',') : []
+        }
+      } else {
+        this.eventOptions = {
+          ...options,
+          freq,
+          count: 1,
+          byDay: byDay ? byDay.split(',') : []
+        }
       }
+
+      this.eventTarget = target
     },
     onDateTimeChange (type) {
       const { start, until } = this.eventOptions
@@ -210,18 +296,86 @@ export default {
         }
       }
     },
-    _onError (message) {
+    onChoose () {
+      this.$refs.eventTargetDialog.show()
+    },
+    onChoosen ({ value, done }) {
+      done()
+      this.eventTarget = value
+      this.onCheckCount()
+      this.$emit('choosen', value)
+    },
+    onEventTypeChanged () {
+      this.onCheckCount()
+      if (this.isCount) {
+        this.eventOptions.until = null
+      }
+    },
+    onCheckCount () {
+      if (this.eventTarget && this.isCount) {
+        this.getDuration(this.eventTarget).then(
+          duration => {
+            this.eventTarget = {
+              ...this.eventTarget,
+              duration
+            }
+          },
+          ({ isCancel }) => {
+            if (!isCancel) {
+              this.$message({
+                type: 'wanring',
+                message: '暂无节目时长,将使用默认值(1分钟)'
+              })
+              this.eventTarget = {
+                ...this.eventTarget,
+                duration: 60
+              }
+            }
+          }
+        )
+      }
+    },
+    getDuration ({ duration, type, id }) {
+      if (duration === 0) {
+        return Promise.reject()
+      }
+      if (!duration) {
+        const loading = this.$showLoading()
+        switch (type) {
+          case EventTarget.RECUR:
+            return getSchedule(id)
+              .then(({ events }) => events.reduce((total, { spend }) => total + spend, 0))
+              .finally(() => {
+                this.$closeLoading(loading)
+              })
+          default:
+            return getProgram(id)
+              .then(({ data }) => Number(data.duration))
+              .finally(() => {
+                this.$closeLoading(loading)
+              })
+        }
+      }
+      return Promise.resolve(duration)
+    },
+    isEventTargetMatchEventFreq () {
+      if (this.eventTarget && this.isCount) {
+        return this.eventTarget.type === ''
+      }
+      return true
+    },
+    onError (message) {
       this.$message({
         type: 'warning',
         message
       })
       return null
     },
-    _isOnceSame () {
+    isOnceSame () {
       const { freq, byDay, startTime, endTime } = this.eventOptions
       return freq === EventFreq.WEEKLY && byDay.length === 7 && startTime === '00:00:00' && endTime === '00:00:00'
     },
-    _getOnce () {
+    createOnceEvent () {
       const { start, until } = this.eventOptions
       return {
         priority: this.priority,
@@ -229,7 +383,7 @@ export default {
         start, until
       }
     },
-    _getWeekly () {
+    createWeeklyEvent () {
       const { start, until, byDay, startTime, endTime } = this.eventOptions
       const byDayValue = byDay.join(',')
       const isOver = isOverDay({ startTime, endTime })
@@ -246,39 +400,79 @@ export default {
         endTime
       }
       if (untilDate && !isMoreThanOneWeek(new Date(untilDate) - new Date(startDate)) && !getNearestHitDate(event, startDate, untilDate)) {
-        return this._onError('有效日期内无法触发')
+        return this.onError('有效日期内无法触发')
       }
       return event
     },
+    createCountEvent () {
+      const { start, count } = this.eventOptions
+      const { duration } = this.eventTarget
+      return {
+        priority: this.priority,
+        freq: EventFreq.ONCE,
+        start,
+        until: parseTime(new Date(start).getTime() + duration * count * 1000, '{y}-{m}-{d} {h}:{i}:{s}')
+      }
+    },
+    createEventTarget () {
+      const { duration, ...eventTarget } = this.eventTarget
+      if (this.isCount) {
+        return { ...eventTarget, duration }
+      }
+      return { ...eventTarget }
+    },
+    createEvent () {
+      if (this.isWeekly && !this.isOnceSame()) {
+        return this.createWeeklyEvent()
+      }
+      if (this.isCount) {
+        return this.createCountEvent()
+      }
+      return this.createOnceEvent()
+    },
     getValue () {
+      if (!this.eventTarget) {
+        return this.onError('请选择播放内容')
+      }
       const { start, until } = this.eventOptions
       if (this.isOnce) {
         if (!start) {
-          return this._onError('请选择生效时间')
+          return this.onError('请选择生效时间')
         }
         if (start === until) {
-          return this._onError('生效时间与失效时间不能一样')
+          return this.onError('生效时间与失效时间不能一样')
         }
       }
       if (this.isWeekly) {
         const { byDay, startTime, endTime } = this.eventOptions
         if (!byDay.length) {
-          return this._onError('请选择生效星期')
+          return this.onError('请选择生效星期')
         }
         if (!startTime) {
-          return this._onError('请选择开始时间')
+          return this.onError('请选择开始时间')
         }
         if (!endTime) {
-          return this._onError('请选择结束时间')
+          return this.onError('请选择结束时间')
         }
         if (!start) {
-          return this._onError('请选择生效日期')
+          return this.onError('请选择生效日期')
         }
         if (until && start.split(' ')[0] === until.split(' ')[0]) {
-          return this._onError('生效日期与失效日期不能一样')
+          return this.onError('生效日期与失效日期不能一样')
+        }
+      }
+      if (this.isCount) {
+        if (!start) {
+          return this.onError('请选择生效时间')
         }
       }
-      return this.isOnce || this._isOnceSame() ? this._getOnce() : this._getWeekly()
+      if (until && new Date(until) <= Date.now()) {
+        return this.onError('结束时间小于当前时间,请配置有效的生效时间')
+      }
+      return {
+        ...this.createEvent(),
+        target: this.createEventTarget()
+      }
     }
   }
 }
@@ -289,5 +483,28 @@ export default {
   &.row &__option {
     width: 100%;
   }
+
+  &__program {
+    position: relative;
+    height: 40px;
+    color: $blue;
+    font-size: 14px;
+    line-height: 1;
+    border-radius: $radius--mini;
+    border: 1px solid #dcdfe6;
+
+    &:hover {
+      border-color: #c0c4cc;
+    }
+  }
+
+  &__name {
+    display: inline-flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+  }
 }
 </style>

+ 0 - 102
src/components/Schedule/ScheduleCalendar/EventEdit.vue

@@ -1,102 +0,0 @@
-<template>
-  <div>
-    <event-picker
-      ref="picker"
-      :event="event"
-    />
-    <div class="o-program u-pointer">
-      <div
-        class="o-program__name has-padding--h"
-        @click="onChoose"
-      >
-        <div class="u-ellipsis">{{ msg }}</div>
-      </div>
-    </div>
-    <event-target-dialog
-      ref="eventTargetDialog"
-      v-bind="$attrs"
-      @choosen="onChoosen"
-    />
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'EventEdit',
-  props: {
-    event: {
-      type: Object,
-      default: null
-    }
-  },
-  data () {
-    return {
-      eventTarget: null
-    }
-  },
-  computed: {
-    msg () {
-      return this.eventTarget ? this.eventTarget.name : '点击选择播放内容'
-    }
-  },
-  watch: {
-    event: {
-      handler () {
-        this.init()
-      },
-      immediate: true
-    }
-  },
-  methods: {
-    init () {
-      console.log(this.event)
-      const target = this.event?.target
-      this.eventTarget = target ? { ...target } : null
-    },
-    onChoose () {
-      this.$refs.eventTargetDialog.show()
-    },
-    onChoosen ({ value, done }) {
-      done()
-      this.eventTarget = value
-    },
-    getValue () {
-      const event = this.$refs.picker.getValue()
-      if (event) {
-        if (!this.eventTarget) {
-          this.$message({
-            type: 'warning',
-            message: '请选择播放节目'
-          })
-          return null
-        }
-        event.target = this.eventTarget
-        return event
-      }
-      return null
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.o-program {
-  position: relative;
-  height: 48px;
-  margin-top: 10px;
-  color: $blue;
-  font-size: 16px;
-  line-height: 1;
-  border-radius: $radius--mini;
-  border: 1px solid $gray;
-
-  &__name {
-    display: inline-flex;
-    justify-content: center;
-    align-items: center;
-    position: absolute;
-    width: 100%;
-    height: 100%;
-  }
-}
-</style>

+ 7 - 19
src/components/Schedule/ScheduleCalendar/index.vue

@@ -62,8 +62,9 @@
       ref="editDialog"
       title="事件设置"
       @confirm="onSaveEvent"
+      @cancel="onCloseEditDialog"
     >
-      <event-edit
+      <event-picker
         v-if="event"
         ref="editor"
         class="u-align-self--center"
@@ -91,21 +92,18 @@
 </template>
 
 <script>
-import { ScheduleType } from '@/constant'
 import { toDate } from '@/utils/event'
 import calendarMixin from '../mixins/calendar'
 import ScheduleWrapper from '../components/ScheduleWrapper'
 import ScheduleCalendarWeek from './ScheduleCalendarWeek'
 import ScheduleCalendarMonth from './ScheduleCalendarMonth'
-import EventEdit from './EventEdit'
 
 export default {
   name: 'ScheduleCalendar',
   components: {
     ScheduleWrapper,
     ScheduleCalendarWeek,
-    ScheduleCalendarMonth,
-    EventEdit
+    ScheduleCalendarMonth
   },
   mixins: [calendarMixin],
   methods: {
@@ -113,20 +111,10 @@ export default {
       return events.sort((a, b) => toDate(a.start) - toDate(b.start)).map(this.createEventProxy)
     },
     getEvents () {
-      switch (this.scheduleOptions.type) {
-        case ScheduleType.CALENDAR:
-          return this.events.map(({ origin: event }) => {
-            const { start, until, target: { id, name } } = event
-            return {
-              programId: id,
-              programName: name,
-              startDateTime: start,
-              endDateTime: until
-            }
-          })
-        default:
-          return this.events.map(({ origin }) => origin)
-      }
+      return this.events.map(({ origin }) => origin)
+    },
+    onCloseEditDialog () {
+      this.event = null
     }
   }
 }

+ 3 - 2
src/components/Schedule/ScheduleSwiper/index.vue

@@ -36,7 +36,7 @@
         <draggable
           v-else
           v-model="scheduleOptions.events"
-          class="l-flex__auto c-schedule-swiper__list"
+          class="l-flex__auto c-schedule-swiper__list has-padding--v"
           :class="{ disabled }"
           handle=".mover"
           animation="300"
@@ -115,6 +115,7 @@ export default {
       record: null,
       programs: [],
       programSchema: {
+        pagination: { small: true, layout: 'prev,pager,next' },
         condition: { status: State.RESOLVED, resolutionRatio: this.ratio, name: '' },
         list: getPrograms,
         filters: [
@@ -250,7 +251,7 @@ export default {
   color: $black;
 
   &__table {
-    width: 300px;
+    width: 318px;
     align-self: flex-start;
     max-height: 100%;
   }

+ 1 - 1
src/components/Schedule/index.vue

@@ -72,7 +72,7 @@ export default {
     }
 
     const { detail } = this.options
-    return h(['ScheduleCalendar', 'ScheduleSwiper', 'ScheduleCalendar'][detail.type - 1], {
+    return h([null, 'ScheduleSwiper', 'ScheduleCalendar'][detail.type - 1], {
       props: {
         detail,
         ...this.$attrs

+ 2 - 1
src/components/dialog/EventTargetDialog/index.vue

@@ -101,7 +101,8 @@ export default {
             type,
             id: item.id,
             name: item.name,
-            programUrl: `${item.buckets}/${item.itemConfigName}`
+            programUrl: `${item.buckets}/${item.itemConfigName}`,
+            duration: Number(item.duration)
           }
         case EventTarget.RECUR:
           return {

+ 0 - 1
src/constant.js

@@ -22,7 +22,6 @@ export const State = {
 }
 
 export const ScheduleType = {
-  CALENDAR: 1,
   RECUR: 2,
   COMPLEX: 3
 }

+ 107 - 205
src/views/schedule/deploy/index.vue

@@ -19,7 +19,6 @@
         align-center
       >
         <el-step title="选择设备" />
-        <el-step title="选择类型" />
         <el-step title="选择内容" />
       </el-steps>
       <button
@@ -52,7 +51,7 @@
       </div>
       <div
         v-if="active === 1"
-        class="c-list fill has-padding u-overflow-y--auto"
+        class="c-list large has-padding u-overflow-y--auto"
       >
         <div class="c-list__item">
           <div class="o-type">发布类型</div>
@@ -69,35 +68,37 @@
           v-if="isEvent"
           ref="picker"
           class="c-list__item"
-          :event="eventOptions.inst"
           :priority="3"
+          :ratio="resolutionRatio"
           vertical
+          @choosen="onChoosenProgram"
         />
-      </div>
-      <div
-        v-if="active > 1"
-        class="c-list small has-padding u-overflow-y--auto"
-        :class="{ large: isEvent, fill: active === 1 }"
-      >
-        <div class="c-list__item u-bold">{{ typeMsg }}</div>
         <div
-          v-for="msg in typeMsgs"
-          :key="msg"
-          class="c-list__item"
+          v-else
+          class="c-list__item c-grid-form medium col"
         >
-          {{ msg }}
+          <div class="c-grid-form__label required">播放内容</div>
+          <div class="o-schedule-button u-pointer">
+            <div
+              class="o-schedule-button__label has-padding--h"
+              @click="onChooseSchedule"
+            >
+              <div class="u-ellipsis">{{ msg }}</div>
+            </div>
+          </div>
         </div>
+        <table-dialog
+          ref="scheduleDialog"
+          title="排期选择"
+          size="medium"
+          :schema="scheduleSchema"
+          @choosen="onChoosenSchedule"
+        />
       </div>
       <div
-        v-if="active >= 2"
+        v-if="active > 0 && eventTarget"
         class="c-list fill has-padding u-overflow-y--auto"
       >
-        <div
-          class="o-choose-button"
-          @click="showChoose"
-        >
-          点击选择内容
-        </div>
         <schedule
           v-if="scheduleId"
           :schedule="scheduleId"
@@ -111,18 +112,6 @@
         </div>
       </div>
     </div>
-    <table-dialog
-      ref="scheduleDialog"
-      title="排期选择"
-      size="medium"
-      :schema="scheduleSchema"
-      @choosen="onChooseSchedule"
-    />
-    <event-target-dialog
-      ref="eventTargetDialog"
-      :ratio="resolutionRatio"
-      @choosen="onChoosenEventTarget"
-    />
   </wrapper>
 </template>
 
@@ -132,7 +121,6 @@ import { publish } from '@/api/platform'
 import {
   State,
   ScheduleType,
-  EventFreq,
   EventTarget,
   PublishType
 } from '@/constant'
@@ -145,20 +133,6 @@ export default {
         { value: PublishType.CALENDAR, label: '排期' },
         { value: PublishType.EVENT, label: '插播' }
       ],
-      freqOptions: [
-        { value: EventFreq.ONCE, label: '单次' },
-        { value: EventFreq.WEEKLY, label: '每周重复' }
-      ],
-      weeks: [
-        { value: '0', label: '日' },
-        { value: '1', label: '一' },
-        { value: '2', label: '二' },
-        { value: '3', label: '三' },
-        { value: '4', label: '四' },
-        { value: '5', label: '五' },
-        { value: '6', label: '六' }
-      ],
-      scheduleOptions: null,
       active: 0,
       selectedDevices: [],
       eventOptions: null,
@@ -166,8 +140,7 @@ export default {
         condition: { type: ScheduleType.COMPLEX, status: State.RESOLVED },
         list: getSchedules,
         cols: [{ prop: 'name', label: '名称', align: 'center' }]
-      },
-      eventTarget: null
+      }
     }
   },
   computed: {
@@ -175,39 +148,17 @@ export default {
       switch (this.active) {
         case 0:
           return this.selectedDevices.length === 0
-        case 2:
+        case 1:
           return !this.eventTarget
         default:
           return false
       }
     },
-    btnMsg () {
-      return this.active < 3 ? '下一步' : '发布'
-    },
-    resolutionRatio () {
-      return this.selectedDevices[0]?.resolutionRatio
-    },
-    typeMsg () {
-      return this.typeOptions[this.eventOptions?.type - 1]?.label
-    },
-    typeMsgs () {
-      const msgs = []
-      if (this.eventOptions?.type === PublishType.EVENT) {
-        const { freq, start, until, byDay, startTime, endTime } = this.eventOptions.inst
-        switch (freq) {
-          case EventFreq.WEEKLY:
-            msgs.push(`自${start.split(' ')[0]}开始`)
-            until && msgs.push(`至${until.split(' ')[0]}前`)
-            msgs.push(`每周${byDay.split(',').map(val => ['日', '一', '二', '三', '四', '五', '六'][val]).join('、')}`)
-            msgs.push(`${startTime} - ${endTime}`)
-            break
-          default:
-            msgs.push(`自${start}开始`)
-            until && msgs.push(`${until}结束`)
-            break
-        }
+    eventTarget () {
+      if (this.isCalendar) {
+        return this.eventOptions.schedule
       }
-      return msgs
+      return this.eventOptions.program
     },
     isCalendar () {
       return this.eventOptions?.type === PublishType.CALENDAR
@@ -215,46 +166,47 @@ export default {
     isEvent () {
       return this.eventOptions?.type === PublishType.EVENT
     },
+    btnMsg () {
+      return this.active < 1 ? '下一步' : '发布'
+    },
+    resolutionRatio () {
+      return this.selectedDevices[0]?.resolutionRatio
+    },
+    typeMsg () {
+      return this.typeOptions[this.eventOptions?.type - 1]?.label
+    },
     scheduleId () {
-      return this.eventOptions?.type === PublishType.CALENDAR || this.eventTarget?.type === EventTarget.RECUR ? this.eventTarget?.id : null
+      return this.eventOptions?.type === PublishType.CALENDAR || this.eventTarget?.type === EventTarget.RECUR ? this.eventTarget.id : null
     },
     programId () {
       return this.eventTarget?.type === EventTarget.PROGRAM ? this.eventTarget.id : null
+    },
+    msg () {
+      return this.eventTarget?.name ?? '点击选择内容'
     }
   },
   methods: {
     onPresent () {
       if (this.active > 0) {
-        if (this.active === 3) {
-          this.active -= 2
-        } else {
-          this.active -= 1
-        }
+        this.active -= 1
       }
     },
     onNext () {
-      let pass = false
       switch (this.active) {
         case 0:
-          pass = this.checkDevices()
-          break
-        case 1:
-          pass = this.checkEventOptions()
-          if (pass && this.checkEventTarget()) {
+          if (this.checkDevices()) {
             this.active += 1
           }
           break
-        case 2:
-          pass = this.checkEventTarget()
-          break
-        case 3:
-          this.publish()
+        case 1:
+          this.publish().then(() => {
+            this.active = 0
+            this.$refs.tree.reset()
+            this.eventOptions = null
+          })
           break
         default:
-          return
-      }
-      if (pass) {
-        this.active += 1
+          break
       }
     },
     onError (message) {
@@ -277,67 +229,26 @@ export default {
       if (devices.some(device => device.resolutionRatio !== resolutionRatio)) {
         return this.onError('选择的设备分辨率不一致')
       }
-      if (this.eventOptions) {
-        if (this.resolutionRatio !== this.eventOptions.ratio) {
-          this.eventTarget = null
-        }
-      } else {
-        this.eventOptions = this.createEventOptions()
-      }
+      this.eventOptions = this.createEventOptions()
       return true
     },
     createEventOptions () {
       return {
         ratio: this.resolutionRatio,
         type: PublishType.CALENDAR,
-        inst: null
+        schedule: null,
+        program: null
       }
     },
-    checkEventOptions () {
-      if (this.eventOptions.type === PublishType.EVENT) {
-        const event = this.$refs.picker.getValue()
-        if (!event) {
-          return false
-        }
-        this.eventOptions.inst = event
-      }
-      return true
-    },
-    showChoose () {
-      switch (this.eventOptions?.type) {
-        case PublishType.CALENDAR:
-          this.$refs.scheduleDialog.show({ resolutionRatio: this.resolutionRatio })
-          break
-        case PublishType.EVENT:
-          this.$refs.eventTargetDialog.show()
-          break
-        default:
-          break
-      }
+    onChoosenProgram (val) {
+      this.eventOptions.program = val
     },
-    onChooseSchedule ({ value: { id, name }, done }) {
-      done()
-      this.eventTarget = { id, name }
-      if (this.active === 2) {
-        this.onNext()
-      }
+    onChooseSchedule () {
+      this.$refs.scheduleDialog.show({ resolutionRatio: this.resolutionRatio })
     },
-    onChoosenEventTarget ({ value, done }) {
+    onChoosenSchedule ({ value: { id, name }, done }) {
       done()
-      this.eventTarget = value
-      if (this.active === 2) {
-        this.onNext()
-      }
-    },
-    checkEventTarget () {
-      if (this.isCalendar && this.eventTarget?.type || this.isEvent && this.eventTarget && !this.eventTarget.type) {
-        this.eventTarget = null
-      }
-      if (!this.eventTarget) {
-        this.showChoose()
-        return false
-      }
-      return true
+      this.eventOptions.schedule = { id, name }
     },
     onViewProgram () {
       window.open(this.$router.resolve({
@@ -346,42 +257,36 @@ export default {
       }).href, '_blank')
     },
     getPublishTarget () {
-      switch (this.eventOptions.type) {
-        case PublishType.CALENDAR:
-          return {
-            type: PublishType.CALENDAR,
-            detail: this.eventTarget.id
-          }
-        case PublishType.EVENT:
-          return {
-            type: PublishType.EVENT,
-            detail: {
-              ...this.eventOptions.inst,
-              target: this.eventTarget
-            }
-          }
-        default:
-          return null
+      if (this.eventOptions.type === PublishType.CALENDAR) {
+        return Promise.resolve({
+          type: PublishType.CALENDAR,
+          detail: this.eventTarget.id
+        })
+      }
+      const event = this.$refs.picker.getValue()
+      if (event) {
+        return Promise.resolve({
+          type: PublishType.EVENT,
+          detail: { ...event }
+        })
       }
+      return Promise.reject()
     },
     publish () {
-      const devices = this.selectedDevices
-      this.$confirm(
-        `对设备 ${devices.map(device => device.name)}`,
-        `发布 ${this.typeMsg} ${this.eventTarget.name}`,
-        { type: 'warning' }
-      ).then(() => publish(
-        devices.map(device => device.id),
-        this.getPublishTarget(),
-        {
-          programCalendarName: this.eventTarget.name,
-          resolutionRatio: this.resolutionRatio
-        }
-      )).then(() => {
-        this.active = 0
-        this.$refs.tree.reset()
-        this.eventOptions = null
-        this.eventTarget = null
+      return this.getPublishTarget().then(eventTarget => {
+        const devices = this.selectedDevices
+        return this.$confirm(
+          `对设备 ${devices.map(device => device.name)}`,
+          `发布 ${this.typeMsg} ${this.eventTarget.name}`,
+          { type: 'warning' }
+        ).then(() => publish(
+          devices.map(device => device.id),
+          eventTarget,
+          {
+            programCalendarName: this.eventTarget.name,
+            resolutionRatio: this.resolutionRatio
+          }
+        ))
       })
     }
   }
@@ -398,9 +303,8 @@ export default {
 }
 
 .c-list {
-  flex: 1 0 200px;
+  flex: 0 0 200px;
   min-width: 200px;
-  max-width: 300px;
   padding-right: $spacing;
   color: $black;
 
@@ -408,18 +312,13 @@ export default {
     border-left: 1px solid $gray--light;
   }
 
-  &.small {
-    min-width: 100px;
-    max-width: 200px;
-  }
-
   &.large {
-    min-width: 240px;
-    max-width: 320px;
+    min-width: 336px;
+    max-width: 336px;
   }
 
   &.fill {
-    max-width: initial;
+    flex: 1 0 200px;
   }
 
   &__item {
@@ -442,24 +341,27 @@ export default {
   line-height: 1;
 }
 
-.o-choose-button {
-  padding: 20px 0;
-  margin-bottom: $spacing;
-  font-size: 20px;
-  text-align: center;
-  border: 1px solid $gray--light;
-  color: $black;
-  cursor: pointer;
+.o-schedule-button {
+  position: relative;
+  width: 217px;
+  height: 40px;
+  color: $blue;
+  font-size: 14px;
+  line-height: 1;
+  border-radius: $radius--mini;
+  border: 1px solid #dcdfe6;
 
   &:hover {
-    color: #409eff;
-    border-color: #c6e2ff;
-    background-color: $blue--light;
+    border-color: #c0c4cc;
   }
 
-  &:active {
-    color: #3a8ee6;
-    border-color: #3a8ee6;
+  &__label {
+    display: inline-flex;
+    justify-content: center;
+    align-items: center;
+    position: absolute;
+    width: 100%;
+    height: 100%;
   }
 }