Browse Source

广播完成

wuyunfeng 4 năm trước cách đây
mục cha
commit
3aa770b869

+ 2 - 0
package.json

@@ -15,6 +15,7 @@
     "test:ci": "npm run lint && npm run test:unit"
   },
   "dependencies": {
+    "@moefe/vue-aplayer": "^2.0.0-beta.5",
     "ag-grid-community": "^25.0.0",
     "ag-grid-vue": "^25.0.0",
     "axios": "0.18.1",
@@ -45,6 +46,7 @@
     "vue": "2.6.10",
     "vue-class-component": "^7.2.6",
     "vue-count-to": "1.0.13",
+    "vue-draggable-resizable": "^2.3.0",
     "vue-property-decorator": "^9.1.2",
     "vue-router": "3.0.2",
     "vue-seamless-scroll": "^1.1.23",

+ 0 - 1
src/api/calling-broadcast.js

@@ -41,7 +41,6 @@ export function update(id, params) {
   return request({
     url: `/ncs/broadcast/${id}`,
     method: 'put',
-    headers: { 'Content-Type': 'application/json' },
     data: params
   })
 }

+ 65 - 0
src/api/calling-broadcastFiles.js

@@ -0,0 +1,65 @@
+import request from '@/utils/request'
+
+/**
+ * 广播文件设置相关接口
+ * @param params
+ * @returns {*|Promise|Promise<unknown>}
+ */
+export function getList(params) {
+  return request({
+    url: '/ncs/broadcastfiles/page',
+    method: 'POST',
+    loading: true,
+    data: params,
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
+  })
+}
+
+/** 新增广播文件 */
+export function add(params) {
+  return request({
+    url: '/ncs/broadcastfiles',
+    method: 'POST',
+    loading: true,
+    data: params
+  })
+}
+
+/** 删除广播文件 */
+export function remove(params) {
+  const ids = params.toString()
+  return request({
+    url: `/ncs/broadcastfiles/${ids}`,
+    method: 'DELETE',
+    loading: true,
+    data: params
+  })
+}
+
+/** 更新广播文件 */
+export function update(id, params) {
+  return request({
+    url: `/ncs/broadcastfiles/${id}`,
+    method: 'put',
+    data: params
+  })
+}
+
+/** 查询单个广播文件 */
+export function get(id, params) {
+  return request({
+    url: `/ncs/broadcastfiles/${id}`,
+    method: 'get',
+    loading: false,
+    params
+  })
+}
+
+export function getAllFileByBroadcastId(broadcastId) {
+  return request({
+    url: `/ncs/broadcastfiles/all/${broadcastId}`,
+    method: 'get',
+    loading: true
+  })
+}
+

+ 7 - 0
src/api/ncs_frameGroup.js

@@ -55,6 +55,13 @@ export function get(id, params) {
     params
   })
 }
+/** 查询科室的所有区域 */
+export function getAll(partId) {
+  return request({
+    url: `/ncs/frame_group/getFrameGroupByPartId/${partId}`,
+    method: 'GET'
+  })
+}
 
 /** 查询某科室下的房间结构 */
 export function getframestruct(part_id, frame_type) {

+ 8 - 1
src/router/index.js

@@ -243,7 +243,7 @@ export const partRoutes = [
         meta: { title: '区域管理', icon: 'area', noCache: true }
       },
       {
-        path: 'edit/:id?',
+        path: '/frameGroup/edit/:id?',
         component: () => import('@/views/hospitalFrame/frameGroupEdit'),
         name: 'frameGroupEdit',
         meta: { title: '编辑区域信息', icon: 'area', noCache: true },
@@ -261,6 +261,13 @@ export const partRoutes = [
         component: () => import('@/views/ncs-broadcast/index'),
         name: 'Broadcast',
         meta: { title: '广播设置', icon: 'el-icon-headset', noCache: true }
+      },
+      {
+        path: '/broadcast/edit/:id?',
+        component: () => import('@/views/ncs-broadcast/broadcastEdit'),
+        name: 'broadcastEdit',
+        meta: { title: '编辑广播信息', icon: 'area', noCache: true },
+        hidden: true
       }
     ]
   },

+ 3 - 1
src/store/modules/user.js

@@ -11,10 +11,12 @@ const _user = Storage.getItem('calling_user')
 const _shop = Storage.getItem('calling_shop')
 const _partid = Storage.getItem('part_id')
 const _uid = Storage.getItem('uid')
+const uuid = Storage.getItem('calling_uuid')
 const state = {
   userInfo: _user ? JSON.parse(_user) : '',
   organization: _shop ? JSON.parse(_shop) : '',
-  partId: _partid
+  partId: _partid,
+  uuid: uuid
 }
 
 const mutations = {

+ 34 - 0
src/views/ncs-broadcast/broadcastEdit.vue

@@ -0,0 +1,34 @@
+<template>
+  <div class="tab-container">
+    <el-tabs v-model="activeName" style="margin:15px;" type="border-card">
+      <el-tab-pane name="broadInfo" label="广播信息">
+        <keep-alive>
+          <broadcast-info  :broadcast-id="broadcastId" />
+        </keep-alive>
+      </el-tab-pane>
+      <el-tab-pane name="broadFileList"  label="广播文件">
+        <keep-alive>
+          <broadcast-file-list :broadcast-id="broadcastId" /></keep-alive>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import BroadcastInfo from './components/broadcastInfo'
+import BroadcastFileList from './components/broadcastFileList'
+export default {
+  name: 'BroadcastEdit',
+  components: { BroadcastFileList, BroadcastInfo },
+  data() {
+    return {
+      broadcastId: Number(this.$route.params.id),
+      activeName: 'broadInfo'
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 449 - 0
src/views/ncs-broadcast/components/broadcastFileList.vue

@@ -0,0 +1,449 @@
+<template>
+  <div>
+    <ag-grid-layout
+      toolbar
+      :table-height="tableHeight"
+      theme="ag-theme-alpine"
+      :column-defs="columnDefs"
+      :row-data="rowData"
+      :locale-text="localeText"
+      :grid-options="gridOptions"
+      :default-col-def="defaultColDef"
+      :animate-rows="true"
+      :row-selection="rowSelection"
+      @filterChanged="filterModifed"
+      @sortChanged="gridSortChange"
+    >
+      <!--        @rowDoubleClicked="getList"-->
+      <div slot="toolbar" class="inner-toolbar">
+        <div class="toolbar-search">
+          <en-table-search placeholder="请输入搜索关键字" @search="handlerSearch" />
+        </div>
+        <div class="toolbar-btns">
+          <el-button type="primary" size="mini" @click="uploadFile">上传文件</el-button>
+        </div>
+      </div>
+      <el-pagination
+        v-if="pageData"
+        slot="pagination"
+        :current-page="pageData.page_no"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="pageData.page_size"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="pageData.data_total"
+        @size-change="handlePageSizeChange"
+        @current-change="handlePageCurrentChange"
+      />
+    </ag-grid-layout>
+    <el-dialog title="文件上传" :visible.sync="formshow" width="40%">
+      <div>
+        <el-form ref="editform" :rules="rules" label-width="120px" :model="formmodel">
+
+          <el-row>
+            <el-col :span="24" align="center">
+              <el-upload
+                class="upload-demo"
+                drag
+                :action="`${uploadServer}/ncs/upload/uploadFile?scene=broadcast`"
+                :show-file-list="false"
+                accept=".mp3"
+                :on-success="uploaded"
+                :before-upload="handleUploadBefore"
+              >
+                <i class="el-icon-upload" />
+                <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+                <div slot="tip" class="el-upload__tip">只能上传mp3文件</div>
+              </el-upload>
+            </el-col>
+          </el-row>
+          <el-form-item label="文件名称" prop="file_name">
+            <el-input v-model="formmodel.file_name" clearable :maxlength="100" placeholder="文件名称" />
+          </el-form-item>
+          <el-form-item label="文件路径" prop="file_url">
+            <el-input v-model="formmodel.file_url" clearable readonly placeholder="文件路径" />
+          </el-form-item>
+          <el-form-item label="播放顺序" prop="play_index">
+            <el-input-number v-model="formmodel.play_index" :min="1" label="play_index" />
+          </el-form-item>
+        </el-form>
+
+      </div>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="formshow = false">取 消</el-button>
+        <el-button type="primary" @click="handlerFormSubmit('editform')">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { AG_GRID_LOCALE_CN } from '@/utils/AgGridVueLocaleCn'
+import ButtonCellRender from '@/components/AgGridCellRender/ButtonCellRender'
+import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
+import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+import { unix2Date } from '@/utils/Foundation'
+import * as API_BroadcastFiles from '@/api/calling-broadcastFiles'
+import { serverUrl } from '@/utils/domain'
+export default {
+  name: 'BroadcastFileList',
+  components: { ButtonCellRender, ListFilter, RadioFilter },
+  props: {
+    broadcastId: {
+      type: Number,
+      default: null
+    }
+  },
+  data() {
+    return {
+      tableData: [],
+      /** 列表参数 */
+      params: {
+        page_size: 20,
+        page_no: 1,
+        fixedCondition: ' part_id = ' + this.$store.getters.partId + ' and broadcast_id=' + this.broadcastId
+      },
+      formshow: false,
+      formmodel: {},
+      /** 上传接口 **/
+      uploadServer: serverUrl,
+      /** ag-grid参数 **/
+      pageData: [],
+      loading: false,
+      columnDefs: null,
+      rowData: null,
+      defaultColDef: null,
+      gridOptions: null,
+      gridApi: null,
+      columnApi: null,
+      localeText: AG_GRID_LOCALE_CN,
+      filterState: null,
+      rowSelection: null,
+      frameworkComponents: null,
+      rules: {
+        file_name: [
+          { required: true, message: '文件名称不能为空', trigger: 'blur' }
+        ],
+        file_url: [
+          { required: true, message: '文件路径不能为空,请上传文件', trigger: 'blur' }
+        ],
+        play_index: [
+          { required: true, message: '播放顺序必填', trigger: 'blur' }
+        ]
+      }
+    }
+  },
+  computed: {
+    tableHeight() {
+      return this.mainAreaHeight - 190
+    }
+
+  },
+  beforeMount() {
+    this.gridOptions = {}
+    this.columnDefs = [
+      {
+        headerName: '#',
+        headerCheckboxSelection: true,
+        headerCheckboxSelectionFilteredOnly: true,
+        checkboxSelection: true,
+        sortable: false, filter: false,
+        width: 100,
+        resizable: false,
+        valueGetter: this.hashValueGetter
+      },
+      { headerName: 'ID', field: 'id', sortable: true, filter: 'agNumberColumnFilter', width: 130 },
+      {
+        headerName: '播放顺序', field: 'play_index', sortable: true, filter: 'agNumberColumnFilter', width: 150
+      },
+      {
+        headerName: '文件名称', field: 'file_name', sortable: true, filter: 'agTextColumnFilter', width: 150
+      },
+      {
+        headerName: '文件路径', field: 'file_url', sortable: true, filterFramework: 'agTextColumnFilter', flex: 1
+      },
+      {
+        headerName: '上传者', field: 'file_author', sortable: true, filter: 'agTextColumnFilter', width: 150
+      },
+      // lockPosition 锁定位置,会在第一列
+      // lockPinned = true 不能拖动然后固定
+      // resizeable 单元个大小是否可以调整
+      {
+        headerName: '上传日期',
+        field: 'create_time',
+        sortable: true,
+        filter: 'agNumberColumnFilter',
+        width: 200,
+        valueFormatter: this.unixDateFormatter
+      },
+      {
+        headerName: '更新时间',
+        field: 'update_time',
+        sortable: true,
+        filter: 'agNumberColumnFilter',
+        width: 200,
+        valueFormatter: this.unixDateFormatter
+      },
+      {
+        headerName: '编辑', field: 'shop_id',
+        cellRendererFramework: 'ButtonCellRender',
+        cellRendererParams: {
+          onClick: this.handEdit,
+          label: '编辑',
+          buttonType: 'primary',
+          buttonSize: 'mini'
+        },
+        filter: false,
+        pinned: 'right',
+        lockPinned: true,
+        width: 100,
+        resizable: false,
+        sortable: false
+      },
+      {
+        headerName: '删除', field: 'shop_id',
+        cellRendererFramework: 'ButtonCellRender',
+        cellRendererParams: param => {
+          return {
+            onClick: this.deleteSingle,
+            label: '删除',
+            buttonType: 'danger',
+            buttonSize: 'mini'
+          }
+        },
+        pinned: 'right',
+        lockPinned: true,
+        width: 100,
+        resizable: false,
+        filter: false,
+        sortable: false
+      }
+    ]
+    this.defaultColDef = {
+      sortable: true,
+      resizable: true,
+      comparator: this.dateCustomComparator,
+      filterParams: {
+        debounceMs: 200,
+        newRowsAction: 'keep',
+        textCustomComparator: this.textCustomComparator,
+        comparator: this.dateCustomComparator
+      }
+    }
+    this.rowSelection = 'multiple'
+  },
+  mounted() {
+    window.onresize = this.windowResize
+    this.gridApi = this.gridOptions.api
+    this.gridColumnApi = this.gridOptions.columnApi
+    // 设置默认排序字段,应用列状态之后会触发 gridSortChange 函数,会调用getlist,后面不需要再调用this.getlist
+    this.gridColumnApi.applyColumnState({
+      state: [
+        {
+          colId: 'play_index',
+          sort: 'asc'
+        }
+      ]
+    })
+  },
+  methods: {
+    windowResize() {
+      this.$set(this, 'mainAreaHeight', Number(document.documentElement.clientHeight) - 84)
+    },
+    handlerDelete(ids) {
+      this.$confirm('删除操作后数据不可复原,您确定要删除此数据?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        API_BroadcastFiles.remove(ids).then(
+          response => {
+            this.getList()
+            this.$message({
+              type: 'success',
+              message: '删除成功!'
+            })
+          }
+        ).catch(response => {
+          this.$message({
+            type: 'info',
+            message: response.message
+          })
+        })
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        })
+      })
+    },
+    deleteSingle(row) {
+      this.handlerDelete(row.id)
+    },
+    /**
+       * 格式化unix时间戳
+       **/
+    unixDateFormatter(param) {
+      if (!param.value) return ''
+      return unix2Date(param.value * 1000)
+    },
+    /** 分页大小发生改变 */
+    handlePageSizeChange(size) {
+      this.params.page_size = size
+      this.getList()
+    },
+
+    /** 分页页数发生改变 */
+    handlePageCurrentChange(page) {
+      this.params.page_no = page
+      this.getList()
+    },
+    /** 加载列表数据 */
+    getList() {
+      this.loading = true
+      const param = this.MixinClone(this.params)
+      this.gridApi.showLoadingOverlay()
+      API_BroadcastFiles.getList(param).then(response => {
+        this.loading = false
+        // this.tableData = [...response.data]
+        this.pageData = {
+          page_no: response.page_no,
+          page_size: response.page_size,
+          data_total: response.data_total
+        }
+        this.rowData = [...response.data]
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    /** 处理搜索 */
+    handlerSearch(keywords) {
+      this.params.query = keywords
+      this.getList()
+    },
+    /** 处理字段排序 */
+    tableSort(column) {
+      if (column.order !== null) {
+        this.params.sort = column.prop
+        this.params.dir = column.order === 'ascending' ? 'asc' : 'desc'
+      } else {
+        this.params.sort = null
+        this.params.dir = null
+      }
+      this.getList()
+    },
+    gridSortChange(param) {
+      const columnState = param.columnApi.getColumnState()
+      // 排序状态
+      const sortState = columnState.filter(function(s) {
+        return s.sort != null
+      }).map(function(s) {
+        return {
+          colId: s.colId,
+          sort: s.sort,
+          sortIndex: s.sortIndex
+        }
+      }).sort(function(a, b) {
+        return a.sortIndex - b.sortIndex
+      })
+      if (sortState.length > 0) {
+        if (sortState.length === 1) {
+          this.params.sort = sortState[0].colId
+          this.params.dir = sortState[0].sort
+        } else {
+          let sortstring = ''
+          sortState.forEach(function(item) {
+            sortstring += item.colId + ' ' + item.sort + ','
+          })
+          this.params.sort = sortstring.substring(0, sortstring.length - 1)
+          this.params.dir = ' '
+        }
+      } else {
+        delete this.params.sort
+        delete this.params.dir
+      }
+
+      this.getList()
+      console.log(sortState)
+    },
+    filterModifed(param) { // todo 通过转换后的数值过滤,需要转回原始数值
+      console.log(param)
+      var model = param.api.getFilterModel()
+      console.log('model', JSON.stringify(model))
+      this.params.filter = JSON.stringify(model)
+      this.getList()
+    },
+    /** 打开上传文件窗口 **/
+    uploadFile() {
+      this.formshow = true
+      this.formmodel = {
+        file_url: '',
+        file_name: '',
+        part_id: this.$store.getters.partId,
+        broadcast_id: this.broadcastId,
+        play_index: 1
+      }
+    },
+    /** 上传成功后的钩子 更新form */
+    uploaded(response) {
+      console.log('file', response)
+      this.formmodel.file_url = response
+    },
+    /** 附件上传之前校验 */
+    handleUploadBefore(file) {
+      return new Promise((resolve, reject) => {
+        const filename = file.name
+        const index = filename.lastIndexOf('.')
+        const extension = filename.substring(index + 1)
+        const isMp3 = extension.toLowerCase() === 'mp3'
+        const isLt5M = file.size / 1024 / 1024 < 5
+        if (!isMp3) {
+          this.$message.error('只能上传mp3文件!')
+          reject()
+        }
+        if (!isLt5M) {
+          this.$message.error('上传附件大小不能超过 5MB!')
+          reject()
+        }
+        this.formmodel.file_name = filename
+        resolve()
+      })
+    },
+    handlerFormSubmit(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          /** 新增 */
+          if (this.formmodel.id) {
+            API_BroadcastFiles.update(this.formmodel.id, this.formmodel).then(res => {
+              this.formshow = false
+              this.$message.success('保存成功!')
+              this.getList()
+            }).catch(err => {
+              this.$message.error(err)
+            })
+          } else {
+            this.formmodel.part_id = this.$store.getters.partId
+            API_BroadcastFiles.add(this.formmodel).then(res => {
+              this.formshow = false
+              this.$message.success('保存成功!')
+              this.getList()
+            }).catch(err => {
+              this.$message.error(err)
+            })
+          }
+        } else {
+          this.$message.error('表单填写错误,请检查!')
+        }
+      })
+    },
+    handEdit(row) {
+      this.formshow = true
+      this.formmodel = {
+        ...row
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 407 - 0
src/views/ncs-broadcast/components/broadcastInfo.vue

@@ -0,0 +1,407 @@
+<template>
+  <div>
+    <el-form ref="editform" :rules="rules" label-width="120px" :model="formmodel">
+
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="广播名称" prop="title">
+            <el-input
+              v-model="formmodel.title"
+              clearable
+              :maxlength="100"
+              placeholder="请输入广播名称"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="广播模式" prop="broadcast_mode">
+            <el-radio-group v-model="formmodel.broadcast_mode" @change="broadcastModeChanage">
+              <!--                  <el-radio :label="1">手动模式</el-radio>-->
+              <!--                  <el-radio :label="2">自动模式</el-radio>-->
+              <el-radio v-for="(item,index) in broadcastMode" :key="index" :label="item.value">{{ item.key }}</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="播放模式(自动)" prop="play_mode">
+            <el-radio-group v-model="formmodel.play_mode" @change="playModeChange">
+              <!--                  <el-radio :label="1">定时播放</el-radio>-->
+              <!--                  <el-radio :label="2">定次播放</el-radio>-->
+              <el-radio v-for="(item,index) in playMode" :key="index" :disabled="formmodel.broadcast_mode===1" :label="item.value">{{ item.key }}</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="自动播放开始" prop="play_start">
+            <el-time-picker
+              v-model="formmodel.play_start"
+              :disabled="formmodel.broadcast_mode===1"
+              value-format="HH:mm:ss"
+              placeholder="任意时间点"
+              @change="playStartChange"
+            />
+          </el-form-item>
+        </el-col>
+
+      </el-row>
+
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="定时播放结束" prop="play_end">
+            <el-time-picker
+              v-model="formmodel.play_end"
+              :disabled="formmodel.broadcast_mode===1||formmodel.play_mode===2"
+              value-format="HH:mm:ss"
+              :picker-options="{
+                selectableRange: (this.formmodel.play_start ? this.formmodel.play_start : '00:00:00') + ' - 23:59:59',
+                format: 'HH:mm:ss'
+              }"
+              placeholder="任意时间点"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="定次播放次数" prop="repeat_times">
+            <el-input-number v-model="formmodel.repeat_times" :min="0" :disabled="formmodel.broadcast_mode===1 || formmodel.play_mode === 1" :max="100" label="repeat_times" />
+          </el-form-item>
+        </el-col>
+
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="是否启用" prop="status">
+            <el-checkbox v-model="formmodel.status" :true-label="1" :false-label="0">开启此广播</el-checkbox>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="开启日">
+            <el-checkbox-group v-model="formmodel.repeat_day" :disabled="formmodel.broadcast_mode===1">
+              <el-checkbox label="1">周一</el-checkbox>
+              <el-checkbox label="2">周二</el-checkbox>
+              <el-checkbox label="3">周三</el-checkbox>
+              <el-checkbox label="4">周四</el-checkbox>
+              <el-checkbox label="5">周五</el-checkbox>
+              <el-checkbox label="6">周六</el-checkbox>
+              <el-checkbox label="0">周日</el-checkbox>
+            </el-checkbox-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="广播区域">
+            <el-checkbox-group v-model="formmodel.group_ids">
+              <el-checkbox v-for="(item,index) in frameGroups" :key="index" :label="item.id+''">{{ item.group_name }}</el-checkbox>
+            </el-checkbox-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <fieldset>
+        <legend>其他广播位置</legend>
+        <el-row :gutter="20">
+          <el-col v-for="(item,index) in rooms" :key="index" gut :xs="8" :sm="6" :md="4" :lg="4" :xl="4">
+            <el-card class="box-card">
+              <div slot="header" class="clearfix">
+                <el-checkbox v-model="item.checked" @change="handleCheckboxChanged(item)"><svg-icon icon-class="sickroom" style="color: #9aaabf;margin-right: 5px" /><span>{{ item.name }}</span></el-checkbox>
+                <el-checkbox v-model="item.allCkeck" style="float: right;" :indeterminate="item.indeterminate" @change="(checked)=>{handleCheckAll(checked,item)}">全选</el-checkbox>
+              </div>
+              <div v-for="(bed,_index) in item.children" :key="_index" class="text item">
+                <el-checkbox v-model="bed.checked" @change="handleCheckboxChanged(item)"><svg-icon icon-class="bed" style="color: #9aaabf;margin-right: 5px" />{{ bed.name }}</el-checkbox>
+              </div>
+            </el-card>
+          </el-col>
+        </el-row>
+      </fieldset>
+      <el-form-item align="center" class="margin-top-sm">
+        <el-button type="success" @click="onSubmit('editform')">保存设置</el-button>
+      </el-form-item>
+    </el-form>
+
+  </div>
+</template>
+
+<script>
+import * as API_FrameGroup from '@/api/ncs_frameGroup'
+import * as API_Broadcast from '@/api/calling-broadcast'
+
+export default {
+  name: 'BroadcastInfo',
+  props: {
+    broadcastId: {
+      type: Number,
+      default: null
+    }
+  },
+  data() {
+    return {
+      formmodel: {
+        repeat_times: 1,
+        group_ids: [],
+        repeat_day: []
+      },
+      frameGroups: [],
+      /** 空间结构房间 **/
+      rooms: [],
+      rules: {
+        title: [
+          { required: true, message: '广播名称不能为空', trigger: 'blur' }
+        ],
+        broadcast_mode: [
+          { required: true, message: '广播模式必须选择', trigger: 'blur' }
+        ],
+        play_mode: [
+          { required: true, message: '播放模式必须选择', trigger: 'blur' }
+        ],
+        repeat_times: [
+          { required: true, message: '播放次数必须填写', trigger: 'blur' }
+        ],
+        play_start: [
+          { required: true, message: '自动模式开始时间必须选择', trigger: 'blur' }
+        ],
+        play_end: [
+          { required: true, message: '定时播放结束时间必须选择', trigger: 'blur' }
+        ]
+      },
+      timeRange: [new Date(2020, 11, 11, 7, 0), new Date(2020, 11, 11, 8, 0)],
+      checkWeek: [],
+      broadcastStatus: [
+        { key: '启用', value: true, color: 'green' },
+        { key: '关闭', value: false, color: 'red' }
+      ],
+      broadcastMode: [
+        { key: '手动模式', value: 1, color: 'green' },
+        { key: '自动模式', value: 2, color: 'red' }
+      ],
+      playMode: [
+        { key: '定时播放', value: 1 },
+        { key: '定次播放', value: 2 }
+      ]
+    }
+  },
+  mounted() {
+    this.getBroadInfo().then(() => {
+      this.getRoomStructs()
+    })
+    this.getAllFrameGroups()
+  },
+  methods: {
+
+    getBroadInfo() {
+      return new Promise((resolve, reject) => {
+        API_Broadcast.get(this.broadcastId).then(res => {
+          this.formmodel = {
+            ...res,
+            group_ids: res.group_ids ? res.group_ids.split(',') : [],
+            frame_ids: res.frame_ids ? res.frame_ids.split(',') : [],
+            repeat_day: res.repeat_day ? res.repeat_day.split(',') : []
+          }
+          this.broadcastModeChanage(this.formmodel.broadcast_mode)
+          if (this.formmodel.play_mode) {
+            this.playModeChange(this.formmodel.play_mode)
+          }
+
+          resolve()
+        }).catch(err => {
+          reject(err)
+        })
+      })
+    },
+
+    broadcastModeChanage(val) { // 变换广播模式,手动模式可以不选择播放模式,自动模式需要选择播放模式,定时播放或者定次播放
+      if (val === 1) { // 手动模式
+        this.rules.play_mode[0].required = false
+        this.rules.repeat_times[0].required = false
+        this.rules.play_end[0].required = false
+        this.rules.play_start[0].required = false
+        this.$set(this.formmodel, 'play_mode', null)
+        this.$set(this.formmodel, 'play_start', null)
+        this.$set(this.formmodel, 'play_end', null)
+        this.$set(this.formmodel, 'repeat_times', null)
+      } else {
+        this.rules.play_mode[0].required = true
+        this.rules.repeat_times[0].required = false
+        this.rules.play_end[0].required = false
+        this.rules.play_start[0].required = false
+        if (this.formmodel.play_mode === 1) {
+          this.rules.play_start[0].required = true
+          this.rules.play_end[0].required = true
+          this.rules.repeat_times[0].required = false
+        } else {
+          this.rules.play_start[0].required = true
+          this.rules.play_end[0].required = false
+          this.rules.repeat_times[0].required = true
+        }
+      }
+    },
+    playModeChange(val) {
+      if (val === 1) {
+        this.rules.play_start[0].required = true
+        this.rules.play_end[0].required = true
+        this.rules.repeat_times[0].required = false
+      } else {
+        this.rules.play_start[0].required = true
+        this.rules.play_end[0].required = false
+        this.rules.repeat_times[0].required = true
+      }
+    },
+    playStartChange(val) {
+      this.$set(this.formmodel, 'play_end', null)
+    },
+    getAllFrameGroups() {
+      API_FrameGroup.getAll(this.$store.getters.partId).then(res => {
+        this.frameGroups = [...res]
+      })
+    },
+    /** 获取科室房间结构 **/
+    getRoomStructs() {
+      API_FrameGroup.getframestruct(this.$store.getters.partId, 4).then(res => {
+        this.rooms = this.filterGroupFrames(res, this.formmodel.frame_ids)
+      }).catch(err => {
+        this.$message.error(err)
+      })
+    },
+    /** 递归筛选被选空间节点 */
+    filterGroupFrames(frames, frameIds) {
+      const _frames = []
+      frames.forEach(item => {
+        const result = frameIds.find(p => Number(p) === item.id)
+        if (result !== undefined && result !== null) {
+          item.checked = true
+        } else {
+          item.checked = false
+        }
+        if (item.children) {
+          this.$set(item, 'children', this.filterGroupFrames(item.children, frameIds))
+        }
+        _frames.push(item)
+      })
+      _frames.forEach(item => {
+        this.countAllFrame(item)
+      })
+      return _frames
+    },
+    /** 获取所有frame的长度、被选中的长度 */
+    countAllFrame(frame) {
+      const _list = []
+      if (!Array.isArray(frame)) {
+        _list.push(frame)
+        if (frame.children) _list.push(...this.countAllFrame(frame.children))
+      } else {
+        frame.forEach(item => {
+          _list.push(item)
+          if (item.children) _list.push(...this.countAllFrame(item.children))
+        })
+      }
+      const length = _list.length
+      const length_checked = _list.filter(_item => _item.checked).length
+      this.$set(frame, 'allCkeck', length === _list.filter(_item => _item.checked).length)
+      this.$set(frame, 'indeterminate', (length_checked !== 0) && (length !== length_checked))
+      return _list
+    },
+    /** 设置选择状态 */
+    setFrameCheck(item, checked) {
+      const perm = this.MixinClone(item)
+      if (!Array.isArray(perm)) {
+        this.$set(item, 'checked', checked)
+        if (item.children && item.children.length) {
+          this.$set(item, 'children', this.setFrameCheck(item.children, checked))
+        }
+      } else {
+        perm.map(item => {
+          item.checked = checked
+          this.$set(item, 'checked', checked)
+          if (item.children && item.children.length) {
+            this.$set(item, 'children', this.setFrameCheck(item.children, checked))
+          }
+        })
+      }
+      return perm
+    },
+    /** 选择 */
+    handleCheckboxChanged(item) {
+      this.countAllFrame(item)
+    },
+    handleCheckAll(checked, item) {
+      this.$set(item, 'indeterminate', false)
+      this.setFrameCheck(item, checked)
+    },
+    /** 递归获取被选中的房间床号FrameId */
+    getCheckedIds(frames) {
+      let _ids = []
+      if (!Array.isArray(frames)) {
+        if (frames.checked) {
+          _ids = [..._ids, frames.id]
+        }
+        if (frames.children && frames.children.length) {
+          _ids = [..._ids, ...this.getCheckedIds(frames.children)]
+        }
+      } else {
+        frames.forEach(item => {
+          _ids = [..._ids, ...this.getCheckedIds(item)]
+          // _ids.push(this.getCheckedIds(item))
+        })
+      }
+      return _ids
+    },
+
+    /**
+       * 提交编辑
+       * @param formName
+       */
+    onSubmit(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          const ids = this.getCheckedIds(this.rooms)
+          this.formmodel.frame_ids = ids
+          /** 新增 */
+          // const starttime = new Date(this.timeRange[0]).toLocaleTimeString('chinese', { hour12: false })
+          // const endtime = new Date(this.timeRange[1]).toLocaleTimeString('chinese', { hour12: false })
+          // this.formmodel.play_start = starttime
+          // this.formmodel.play_end = endtime
+
+          API_Broadcast.update(this.formmodel.id, this.formmodel).then(() => {
+            this.formshow = false
+            this.$message.success('保存成功!')
+            this.getBroadInfo().then(() => {
+              this.getRoomStructs()
+            })
+          }).catch(err => {
+            this.$message.error(err)
+          })
+        } else {
+          this.$message.error('表单填写错误,请检查!')
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+    /deep/ .el-checkbox__input.is-checked + .el-checkbox__label svg{
+        color: #1890ff !important;
+    }
+    .item {
+        margin-bottom: 10px;
+    }
+    .box-card{margin-bottom: 10px}
+    /deep/ .el-card__body {
+        min-height: 130px;
+    }
+    /deep/ .el-card__header{
+        padding:10px 20px;
+    }
+    .margin-top-sm{
+        margin-top: 20px;
+    }
+    fieldset{
+        border:1px solid #DCDFE6;
+        border-radius: 5px;
+    }
+
+</style>

+ 215 - 43
src/views/ncs-broadcast/index.vue

@@ -11,6 +11,7 @@
       :default-col-def="defaultColDef"
       :animate-rows="true"
       :row-selection="rowSelection"
+      :enable-cell-change-flash="true"
       @filterChanged="filterModifed"
       @sortChanged="gridSortChange"
     >
@@ -54,8 +55,9 @@
             <el-col :span="12">
               <el-form-item label="广播模式" prop="broadcast_mode">
                 <el-radio-group v-model="formmodel.broadcast_mode" @change="broadcastModeChanage">
-                  <el-radio :label="1">手动模式</el-radio>
-                  <el-radio :label="2">自动模式</el-radio>
+                  <!--                  <el-radio :label="1">手动模式</el-radio>-->
+                  <!--                  <el-radio :label="2">自动模式</el-radio>-->
+                  <el-radio v-for="(item,index) in broadcastMode" :key="index" :label="item.value">{{ item.key }}</el-radio>
                 </el-radio-group>
               </el-form-item>
             </el-col>
@@ -64,8 +66,9 @@
             <el-col :span="12">
               <el-form-item label="播放模式(自动)" prop="play_mode">
                 <el-radio-group v-model="formmodel.play_mode" @change="playModeChange">
-                  <el-radio :label="1">定时播放</el-radio>
-                  <el-radio :label="2">定次播放</el-radio>
+                  <!--                  <el-radio :label="1">定时播放</el-radio>-->
+                  <!--                  <el-radio :label="2">定次播放</el-radio>-->
+                  <el-radio v-for="(item,index) in playMode" :key="index" :disabled="formmodel.broadcast_mode===1" :label="item.value">{{ item.key }}</el-radio>
                 </el-radio-group>
               </el-form-item>
             </el-col>
@@ -73,7 +76,11 @@
               <el-form-item label="自动播放开始" prop="play_start">
                 <el-time-picker
                   v-model="formmodel.play_start"
+                  :disabled="formmodel.broadcast_mode===1"
+                  arrow-control
+                  value-format="HH:mm:ss"
                   placeholder="任意时间点"
+                  @change="playStartChange"
                 />
               </el-form-item>
             </el-col>
@@ -85,13 +92,20 @@
               <el-form-item label="定时播放结束" prop="play_end">
                 <el-time-picker
                   v-model="formmodel.play_end"
+                  :disabled="formmodel.broadcast_mode===1||formmodel.play_mode===2"
+                  arrow-control
+                  value-format="HH:mm:ss"
+                  :picker-options="{
+                    selectableRange: (this.formmodel.play_start ? this.formmodel.play_start : '00:00:00') + ' - 23:59:59',
+                    format: 'HH:mm:ss'
+                  }"
                   placeholder="任意时间点"
                 />
               </el-form-item>
             </el-col>
             <el-col :span="12">
               <el-form-item label="定次播放次数" prop="repeat_times">
-                <el-input-number v-model="formmodel.repeat_times" :min="1" :max="100" label="repeat_times" />
+                <el-input-number v-model="formmodel.repeat_times" :min="0" :disabled="formmodel.broadcast_mode===1 || formmodel.play_mode === 1" :max="100" label="repeat_times" />
               </el-form-item>
             </el-col>
 
@@ -106,7 +120,7 @@
           <el-row>
             <el-col :span="24">
               <el-form-item label="开启日">
-                <el-checkbox-group v-model="formmodel.repeat_day">
+                <el-checkbox-group v-model="formmodel.repeat_day" :disabled="formmodel.broadcast_mode===1">
                   <el-checkbox label="1">周一</el-checkbox>
                   <el-checkbox label="2">周二</el-checkbox>
                   <el-checkbox label="3">周三</el-checkbox>
@@ -118,7 +132,15 @@
               </el-form-item>
             </el-col>
           </el-row>
-
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="广播区域">
+                <el-checkbox-group v-model="formmodel.group_ids">
+                  <el-checkbox v-for="(item,index) in frameGroups" :key="index" :label="item.id">{{ item.group_name }}</el-checkbox>
+                </el-checkbox-group>
+              </el-form-item>
+            </el-col>
+          </el-row>
         </el-form>
 
       </div>
@@ -127,7 +149,11 @@
         <el-button type="primary" @click="handlerFormSubmit('editform')">确 定</el-button>
       </div>
     </el-dialog>
-    <!-- 新建广播弹窗结束 -->
+    <!--    &lt;!&ndash; 新建广播弹窗结束 &ndash;&gt;-->
+    <!--    <vue-draggable-resizable :w="400" style="position: absolute;right:0;bottom: 80px">-->
+    <!--      <aplayer :audio="music" :lrc-type="0" fixed />-->
+    <!--    </vue-draggable-resizable>-->
+    <!--    <a-player  :music="music[0]" :list="music" float fixed   :showLrc="3" listFolded />-->
   </div>
 </template>
 
@@ -137,9 +163,14 @@ import { AG_GRID_LOCALE_CN } from '@/utils/AgGridVueLocaleCn'
 import ButtonCellRender from '../../components/AgGridCellRender/ButtonCellRender'
 import * as API_Broadcast from '@/api/calling-broadcast'
 import { unix2Date } from '@/utils/Foundation'
+import * as API_FrameGroup from '@/api/ncs_frameGroup'
+import ListFilter from '@/components/AgGridCustomFilter/ListFilter'
+import RadioFilter from '@/components/AgGridCustomFilter/RadioFilter'
+import * as API_BroadcastFiles from '@/api/calling-broadcastFiles'
+import { serverUrl, DeviceUrl } from '@/utils/domain'
 export default {
   name: 'Index',
-  components: { ButtonCellRender },
+  components: { ButtonCellRender, ListFilter, RadioFilter },
   data() {
     return {
       tableData: [],
@@ -153,8 +184,10 @@ export default {
       formtitle: '新建广播',
       formshow: false,
       formmodel: {
-        repeat_times: 1
+        repeat_times: 1,
+        group_ids: []
       },
+      frameGroups: [],
       rules: {
         title: [
           { required: true, message: '广播名称不能为空', trigger: 'blur' }
@@ -191,13 +224,28 @@ export default {
       localeText: AG_GRID_LOCALE_CN,
       filterState: null,
       rowSelection: null,
-      frameworkComponents: null
+      frameworkComponents: null,
+      broadcastStatus: [
+        { key: '启用', value: true, color: 'green' },
+        { key: '关闭', value: false, color: 'red' }
+      ],
+      broadcastMode: [
+        { key: '手动模式', value: 1, color: 'green' },
+        { key: '自动模式', value: 2, color: 'red' }
+      ],
+      playMode: [
+        { key: '定时播放', value: 1 },
+        { key: '定次播放', value: 2 }
+      ],
+      websocket: null,
+      scheduledIds: []
     }
   },
   computed: {
     tableHeight() {
       return this.mainAreaHeight - 130
     }
+
   },
   beforeMount() {
     this.gridOptions = {}
@@ -217,15 +265,21 @@ export default {
         headerName: '广播名称', field: 'title', sortable: true, filter: 'agTextColumnFilter', width: 150
       },
       {
-        headerName: '开启状态', field: 'status', sortable: true, filter: 'agListColumnFilter', width: 150, cellRenderer: this.statusFormatter
+        headerName: '开启状态', field: 'status', sortable: true, filterFramework: 'RadioFilter', filterParams: {
+          listData: this.broadcastStatus
+        }, width: 150, cellRenderer: this.statusFormatter
       },
       {
-        headerName: '广播模式', field: 'broadcast_mode', sortable: true, filter: 'agTextColumnFilter', width: 150,
-        valueFormatter: this.broadcastFormatter
+        headerName: '广播模式', field: 'broadcast_mode', sortable: true, filterFramework: 'RadioFilter', filterParams: {
+          listData: this.broadcastMode
+        }, width: 150,
+        cellRenderer: (para) => { return this.radioFilterFormatter(para, this.broadcastMode) }
       },
       {
-        headerName: '播放模式', field: 'play_mode', sortable: true, filter: 'agTextColumnFilter', width: 150,
-        valueFormatter: this.playmodeFormatter
+        headerName: '播放模式', field: 'play_mode', sortable: true, filterFramework: 'RadioFilter', filterParams: {
+          listData: this.playMode
+        }, width: 150,
+        cellRenderer: (para) => { return this.radioFilterFormatter(para, this.playMode) }
       },
       {
         headerName: '开始时间', field: 'play_start', sortable: true, filter: 'agTextColumnFilter', width: 150
@@ -292,10 +346,28 @@ export default {
         resizable: false,
         filter: false,
         sortable: false
+      },
+      {
+        headerName: '播放', field: 'id',
+        cellRendererFramework: 'ButtonCellRender',
+        cellRendererParams: param => {
+          return {
+            onClick: () => { param.data.playing ? this.stopBroadcast(param.data) : this.playBroadcast(param.data) },
+            label: param.data.playing ? '停止' : '播放',
+            buttonType: param.data.playing ? 'danger' : 'primary',
+            buttonSize: 'mini',
+            disabled: param.data.broadcast_mode !== 1 || !param.data.status
+          }
+        },
+        pinned: 'right',
+        lockPinned: true,
+        width: 100,
+        resizable: false,
+        filter: false,
+        sortable: false
       }
     ]
     this.defaultColDef = {
-      filter: 'agTextColumnFilter',
       sortable: true,
       resizable: true,
       comparator: this.dateCustomComparator,
@@ -321,7 +393,15 @@ export default {
         }
       ]
     })
-    // this.getList()
+    this.getAllFrameGroups()
+    this.initWebSocket()
+    const _this = this
+    // 每分钟检查一次websocket状态,如果掉线,则重连
+    setInterval(function() {
+      if (_this.websock === null || _this.websock.readyState !== 1) {
+        _this.initWebSocket()
+      }
+    }, 60000)
   },
   methods: {
     windowResize() {
@@ -357,15 +437,41 @@ export default {
     deleteSingle(row) {
       this.handlerDelete(row.id)
     },
+
+    playBroadcast(row) {
+      // API_BroadcastFiles.getAllFileByBroadcastId(row.id).then(res => {
+      //   if (Array.isArray(res)) {
+      //     this.music = res.map(p => { return { name: p.file_name, url: serverUrl + '/' + p.file_url, artist: p.file_author } })
+      //     console.log(this.music)
+      //   }
+      // })
+      this.websock.send(JSON.stringify({ 'command': 'play', 'broadcast_id': row.id }))
+      // API_Broadcast.playBroadcast(row.id).then(() => {
+      //   row.playing = true
+      //   // this.gridApi.redrawRows()
+      //   this.gridApi.redrawRows()
+      // })
+    },
+
+    stopBroadcast(row) {
+      this.websock.send(JSON.stringify({ 'command': 'stop', 'broadcast_id': row.id }))
+      // API_Broadcast.stopBroadcast(row.id).then(() => {
+      //   row.playing = false
+      //   this.gridApi.redrawRows()
+      // })
+    },
+
     /**
-             * 创建组织
+             * 创建广播
              */
     createBroadcast() {
       this.formshow = true
       this.formmodel = {
         title: '',
         repeat_times: 1,
-        repeat_day: []
+        status: false,
+        repeat_day: [],
+        group_ids: []
       }
     },
     /** 分页大小发生改变 */
@@ -393,6 +499,7 @@ export default {
           data_total: response.data_total
         }
         this.rowData = [...response.data]
+        this.refreshPlayStatus()
       }).catch(() => {
         this.loading = false
       })
@@ -421,28 +528,26 @@ export default {
       return unix2Date(param.value * 1000)
     },
 
-    broadcastFormatter(param) {
-      if (!param.value) return ''
-      if (param.value === 1) {
-        return '手动模式'
+    radioFilterFormatter(params, array) {
+      const item = array.filter(p => p.value === params.value)[0]
+      if (item) {
+        if (item.color) {
+          return '<span style="color:' + item.color + '">' + item.key + '</span>'
+        } else {
+          return item.key
+        }
       } else {
-        return '自动模式'
+        return ''
       }
     },
 
-    playmodeFormatter(param) {
-      if (!param.value) return ''
-      if (param.value === 1) {
-        return '定时播放'
-      } else {
-        return '定次播放'
-      }
-    },
-    statusFormatter(param) {
-      if (param.value) {
-        return '<span style="color: green">开启</span>'
+    statusFormatter(params) {
+      console.log('para', params)
+      const item = this.broadcastStatus.filter(p => (p.value === (!params.value ? false : params.value)))[0] // params.value === null 时,要返回false的记录
+      if (item) {
+        return '<span style="color:' + item.color + '">' + item.key + '</span>'
       } else {
-        return '<span style="color: red">关闭</span>'
+        return ''
       }
     },
     weekdayFormatter(param) {
@@ -496,7 +601,7 @@ export default {
       this.getList()
     },
     handEdit(row) {
-      this.$router.push({ name: 'frameGroupEdit', params: { id: row.id, callback: this.getList() }})
+      this.$router.push({ name: 'broadcastEdit', params: { id: row.id, callback: this.getList() }})
     },
     /**
              * 提交新增表单
@@ -508,10 +613,10 @@ export default {
           /** 新增 */
           delete this.formmodel.id
           this.formmodel.part_id = this.$store.getters.partId
-          const starttime = new Date(this.timeRange[0]).toLocaleTimeString('chinese', { hour12: false })
-          const endtime = new Date(this.timeRange[1]).toLocaleTimeString('chinese', { hour12: false })
-          this.formmodel.play_start = starttime
-          this.formmodel.play_end = endtime
+          // const starttime = new Date(this.timeRange[0]).toLocaleTimeString('chinese', { hour12: false })
+          // const endtime = new Date(this.timeRange[1]).toLocaleTimeString('chinese', { hour12: false })
+          // this.formmodel.play_start = starttime
+          // this.formmodel.play_end = endtime
 
           API_Broadcast.add(this.formmodel).then(() => {
             this.formshow = false
@@ -520,6 +625,8 @@ export default {
           }).catch(err => {
             this.$message.error(err)
           })
+        } else {
+          this.$message.error('表单填写错误,请检查!')
         }
       })
     },
@@ -529,20 +636,85 @@ export default {
         this.rules.repeat_times[0].required = false
         this.rules.play_end[0].required = false
         this.rules.play_start[0].required = false
+        this.$set(this.formmodel, 'play_mode', null)
+        this.$set(this.formmodel, 'play_start', null)
+        this.$set(this.formmodel, 'play_end', null)
+        this.$set(this.formmodel, 'repeat_times', null)
       } else {
         this.rules.play_mode[0].required = true
         this.rules.repeat_times[0].required = false
         this.rules.play_end[0].required = false
         this.rules.play_start[0].required = false
+        if (this.formmodel.play_mode === 1) {
+          this.rules.play_start[0].required = true
+          this.rules.play_end[0].required = true
+          this.rules.repeat_times[0].required = false
+        } else {
+          this.rules.play_start[0].required = true
+          this.rules.play_end[0].required = false
+          this.rules.repeat_times[0].required = true
+        }
       }
     },
     playModeChange(val) {
       if (val === 1) {
+        this.rules.play_start[0].required = true
+        this.rules.play_end[0].required = true
+        this.rules.repeat_times[0].required = false
+      } else {
+        this.rules.play_start[0].required = true
+        this.rules.play_end[0].required = false
+        this.rules.repeat_times[0].required = true
+      }
+    },
+    playStartChange(val) {
+      console.log('start', val)
+      this.$set(this.formmodel, 'play_end', null)
+    },
+    getAllFrameGroups() {
+      API_FrameGroup.getAll(this.$store.getters.partId).then(res => {
+        this.frameGroups = [...res]
+        console.log('groups', this.frameGroups)
+      })
+    },
+
+    initWebSocket: function() {
+      const stockbase = DeviceUrl.replace('http', 'ws')
+      this.websock = new WebSocket(stockbase + '/broadcast/status/' + this.$store.getters.uuid)
+      this.websock.onopen = this.websocketonopen
+      this.websock.onerror = this.websocketonerror
+      this.websock.onmessage = this.websocketonmessage
+      this.websock.onclose = this.websocketclose
+    },
+    websocketonopen: function() {
+      console.log('WebSocket连接成功')
+    },
+    websocketonerror: function(e) {
+      console.log('WebSocket连接发生错误')
+    },
+    websocketonmessage: function(e) {
+      this.scheduledIds = JSON.parse(e.data)
+      this.refreshPlayStatus()
+    },
 
-      }else{
+    websocketclose: function(e) {
+      console.log('connection closed (' + e.code + ')')
+    },
 
+    refreshPlayStatus() {
+      if (this.rowData !== null) {
+        this.rowData.forEach(item => {
+          console.log(this.scheduledIds.indexOf(item.id))
+          if (this.scheduledIds.indexOf(item.id) > -1 && item.broadcast_mode === 1) {
+            item.playing = true
+          } else {
+            item.playing = false
+          }
+        })
+        this.gridApi.redrawRows()
       }
     }
+
   }
 }
 </script>