Browse Source

feat(deploy): content can be imported from the submitted workflow

Casper Dai 2 năm trước cách đây
mục cha
commit
8d4bc1e022

+ 9 - 0
src/api/workflow.js

@@ -6,6 +6,7 @@ import {
 } from '@/constant'
 import request from '@/utils/request'
 import {
+  send,
   addTenant,
   addUser
 } from '@/api/base'
@@ -54,3 +55,11 @@ export function getWorkflowsByUser (query, options) {
     ...options
   })
 }
+
+export function getWorkflowDetail (workflowId, options) {
+  return (options?.loading ? send : request)({
+    url: '/workflow/getBussinessData',
+    method: 'POST',
+    params: { workflowId }
+  })
+}

+ 21 - 2
src/components/service/EventTargetPicker/index.vue

@@ -268,6 +268,7 @@ export default {
             detail: target,
             assets: []
           }
+          this.$datasetName = ''
           break
         default:
           this.currentTarget = {
@@ -286,6 +287,7 @@ export default {
               }
             })
           }
+          this.$datasetName = target?.name
           break
       }
     },
@@ -353,7 +355,8 @@ export default {
       if (this.$datasetAssets) {
         return Promise.resolve({ data: this.$datasetAssets })
       }
-      return getCommonDataset(this.$datasetId).then(({ data: { mediaList } }) => {
+      return getCommonDataset(this.$datasetId).then(({ data: { name, mediaList } }) => {
+        this.$datasetName = name
         this.$datasetAssets = mediaList.map(this.transformDatasetAsset)
         return { data: this.$datasetAssets }
       })
@@ -367,6 +370,7 @@ export default {
       if (this.$datasetId !== value.id) {
         this.$datasetId = value.id
         this.$datasetAssets = null
+        this.$datasetName = ''
       }
       this.getAssets().then(({ data }) => {
         data.forEach(item => {
@@ -423,7 +427,22 @@ export default {
           }
     },
     getSnapshot () {
-      return this.currentTarget
+      const { type, detail } = this.currentTarget
+      switch (type) {
+        case EventTarget.PROGRAM:
+        case EventTarget.RECUR:
+          return {
+            type,
+            detail
+          }
+        default:
+          return {
+            type,
+            detail: {
+              name: this.$datasetName
+            }
+          }
+      }
     },
     getValue () {
       const eventTarget = this.createEventTarget()

+ 69 - 18
src/components/tree/DeviceTree/index.vue

@@ -29,7 +29,15 @@
       />
       <template v-else>
         <div class="l-flex__none l-flex--row c-sibling-item--v u-overflow--hidden">
-          <div class="l-flex__auto" />
+          <div class="l-flex__auto">
+            <div
+              v-if="canReset"
+              class="o-button"
+              @click="onReset"
+            >
+              重置
+            </div>
+          </div>
           <search-input
             v-model.trim="deviceName"
             class="l-flex__none"
@@ -95,6 +103,9 @@ export default {
     },
     iconClass () {
       return this.shrinkState ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'
+    },
+    canReset () {
+      return this.checkbox && !this.rootOption.disabled
     }
   },
   watch: {
@@ -115,7 +126,7 @@ export default {
       this.getDevicesByActive().then(
         data => {
           this.$nodes = data
-          this.onSearch()
+          this.initData()
         },
         () => {
           this.error = true
@@ -232,29 +243,49 @@ export default {
         map[value].push(device)
       }
     },
-    onSearch () {
-      const regx = this.deviceName ? new RegExp(this.deviceName) : null
+    initData () {
       const rootOption = this.createRootNode()
-      this.setNodes(rootOption, this.onFilter(rootOption, this.$nodes, regx))
+      this.setNodes(rootOption, this.onFilter(rootOption, this.$nodes, this.deviceName ? new RegExp(this.deviceName) : null))
       this.rootOption = rootOption
+      this.customExpand = false
+    },
+    onSearch () {
+      this.filterNode(this.rootOption, this.deviceName ? new RegExp(this.deviceName) : null)
       this.customExpand = !!this.deviceName
     },
-    onFilter (parent, nodes, regx) {
-      if (regx) {
-        nodes = nodes.filter(({ children, name }) => {
-          if (children) {
-            return true
+    filterNode (node, regx) {
+      const { disbaled, checked, indeterminate } = node
+      let change = false
+      node.children.forEach(subnode => {
+        if (subnode.children) {
+          change = this.filterNode(subnode, regx) || change
+        } else {
+          const isDisabled = this.isDisabledNode(subnode, regx)
+          if (subnode.disabled !== isDisabled) {
+            change = true
+            subnode.disabled = isDisabled
+            this.addOrRemoveNodeCache(subnode)
           }
-          return regx.test(name)
-        })
+        }
+      })
+      if (change) {
+        this.checkNode(node)
+        return disbaled !== node.disbaled || checked !== node.checked || indeterminate !== node.indeterminate
       }
+      return change
+    },
+    onFilter (parent, nodes, regx) {
       const childNodes = nodes.map(({ children, ...node }) => {
         if (!children) {
-          const leafNode = this.createLeafNode(parent, node)
-          if (this.filter) {
-            leafNode.disabled = !this.filter(node)
+          const checked = this.$presetNodeMap ? this.$presetNodeMap[node.id] : false
+          if (checked) {
+            delete this.$presetNodeMap[node.id]
           }
-          return leafNode
+          return this.createLeafNode(parent, {
+            ...node,
+            checked,
+            disabled: this.isDisabledNode(node)
+          })
         }
         const subNode = this.createNode(node, parent)
         this.setNodes(subNode, this.onFilter(subNode, children, regx))
@@ -262,8 +293,28 @@ export default {
       })
       return childNodes
     },
-    reset () {
-      this.onSearch()
+    isDisabledNode (node, regx) {
+      return (regx ? !regx.test(node.name) : false) || (this.filter ? !this.filter(node) : false)
+    },
+    onReset () {
+      this.deviceName = ''
+      this.initData()
+    },
+    reset (deviceIds) {
+      if (deviceIds?.length) {
+        const map = {}
+        deviceIds.forEach(id => {
+          map[id] = true
+        })
+        this.$presetNodeMap = map
+        this.deviceName = ''
+        this.initData()
+        const diff = Object.keys(this.$presetNodeMap)
+        this.$presetNodeMap = null
+        return diff.length
+      }
+      this.initData()
+      return 0
     }
   }
 }

+ 20 - 3
src/components/tree/Tree/TreeNode/index.vue

@@ -1,5 +1,8 @@
 <template>
-  <div class="l-flex__none l-flex--col c-tree-node">
+  <div
+    v-if="!hide"
+    class="l-flex__none l-flex--col c-tree-node"
+  >
     <div
       v-if="isInView"
       class="l-flex--row c-tree-node__content u-pointer"
@@ -59,6 +62,9 @@ export default {
     }
   },
   computed: {
+    hide () {
+      return this.tree.hideDisabled && this.node.disabled
+    },
     isLeaf () {
       return !this.node.children
     },
@@ -83,13 +89,24 @@ export default {
   },
   watch: {
     'tree.expandOnInit' (val) {
-      if (!this.isLeaf && val) {
+      if (!this.isLeaf) {
         this.expand = val
       }
+    },
+    hide (val) {
+      if (val) {
+        this.$observer?.disconnect()
+      } else {
+        this.$nextTick(() => {
+          this.observe()
+        })
+      }
     }
   },
   mounted () {
-    this.observe(this.$el)
+    if (!this.hide) {
+      this.observe()
+    }
   },
   beforeDestroy () {
     this.$observer?.disconnect()

+ 1 - 1
src/components/tree/Tree/index.vue

@@ -41,7 +41,7 @@ export default {
   },
   computed: {
     isEmpty () {
-      return !this.rootOption.children.length
+      return this.rootOption.disabled
     }
   },
   methods: {

+ 33 - 54
src/components/tree/tree.js

@@ -28,6 +28,10 @@ export default {
     defaultCheckedNodes: {
       type: Array,
       default: null
+    },
+    includeDisabledLeaf: {
+      type: [Boolean, String],
+      default: true
     }
   },
   data () {
@@ -56,42 +60,29 @@ export default {
     }
   },
   methods: {
-    initDefaultCheckedNodesCache () {
-      if (this.checkbox && this.defaultCheckedNodes && this.defaultCheckedNodes.length) {
-        const map = {}
-        this.defaultCheckedNodes.forEach(item => {
-          map[item.id] = item
-        })
-        console.log('selected nodes', map)
-        return map
-      }
-      return null
-    },
     emitChange () {
       if (!this.checkbox) {
         return
       }
-      if (this.$nodeCache) {
-        const diff = Object.values(this.$nodeCache)
-        console.log('node diff', diff)
-        if (diff.length) {
-          this.$emit(
-            'change',
-            [
-              ...this.flatNode(this.rootOption, []),
-              ...diff
-            ]
-          )
-          return
-        }
-      }
       this.$emit(
         'change',
-        this.flatNode(this.rootOption, [])
+        this.$nodeCache
+          ? [...this.$nodeCache].map(this.getSimpleNode)
+          : []
       )
     },
+    getSimpleNode ({ disabled, checked, indeterminate, children, parent, ...info }) {
+      return info
+    },
+    addOrRemoveNodeCache (node) {
+      if (node.checked) {
+        this.$nodeCache.add(node)
+      } else {
+        this.$nodeCache.delete(node)
+      }
+    },
     createRootNode () {
-      this.$nodeCache = this.initDefaultCheckedNodesCache()
+      this.$nodeCache = new Set()
       return {
         disabled: false,
         checked: false,
@@ -110,39 +101,26 @@ export default {
       }
     },
     createLeafNode (parent, item) {
-      let checked = false
-      if (this.$nodeCache) {
-        const id = item.id
-        checked = !!this.$nodeCache[id]
-        if (checked) {
-          delete this.$nodeCache[id]
-        }
-      }
-      return {
+      const node = {
         parent,
         disabled: false,
-        checked,
+        checked: false,
         ...item
       }
+      if (node.checked && (!node.disabled || this.includeDisabledLeaf)) {
+        this.addOrRemoveNodeCache(node)
+      }
+      return node
     },
     setNodes (parent, nodes) {
-      parent.children = this.hideDisabled ? nodes.filter(({ disabled }) => !disabled) : nodes
-      parent.disabled = !nodes.some(({ disabled }) => !disabled)
-      if (this.$nodeCache) {
-        parent.checked = !parent.children.some(({ disabled, checked }) => !disabled && !checked)
-        parent.indeterminate = !parent.checked && parent.children.some(({ disabled, checked, indeterminate }) => !disabled && (checked || indeterminate))
-      }
+      parent.children = nodes
+      this.checkNode(parent)
     },
-    flatNode (node, arr) {
-      const { disabled, checked, indeterminate, children, parent, ...info } = node
-      if (!children && checked) {
-        arr.push(info)
-      } else if (children && (checked || indeterminate)) {
-        children.forEach(sub => {
-          this.flatNode(sub, arr)
-        })
-      }
-      return arr
+    checkNode (node) {
+      const { children } = node
+      node.disabled = !children.some(({ disabled }) => !disabled)
+      node.checked = !children.some(({ disabled, checked }) => !disabled && !checked)
+      node.indeterminate = !node.checked && children.some(({ disabled, checked, indeterminate }) => !disabled && (checked || indeterminate))
     },
     reStatue (node, bool) {
       if (!this.checkbox || node.disabled) {
@@ -158,11 +136,12 @@ export default {
       this.emitChange()
     },
     setNodeChekced (node, bool) {
-      if (node.disabled) {
+      if (node.disabled || node.checked === bool) {
         return
       }
       node.checked = bool
       if (!node.children) {
+        this.addOrRemoveNodeCache(node)
         return
       }
       node.indeterminate = false

+ 4 - 4
src/router/index.js

@@ -177,7 +177,7 @@ export const asyncRoutes = [
       {
         name: 'workflow-mine',
         path: 'mine',
-        component: () => import('@/views/screen/review/workflow/mine/index'),
+        component: () => import('@/views/screen/deploy/workflow/mine/index'),
         access: Access.MANAGE_CALENDAR,
         meta: { title: '我的流程' }
       },
@@ -194,21 +194,21 @@ export const asyncRoutes = [
           {
             name: 'workflow-list',
             path: '',
-            component: () => import('@/views/screen/review/workflow/index'),
+            component: () => import('@/views/screen/deploy/workflow/index'),
             meta: { cache: 'WorkflowList' }
           },
           {
             hidden: true,
             name: 'workflow-audit',
             path: ':id',
-            component: () => import('@/views/screen/review/workflow/audit/index'),
+            component: () => import('@/views/screen/deploy/workflow/audit/index'),
             meta: { title: '审核', cache: 'WorkflowList' }
           }
         ]
       },
       {
         path: 'history',
-        component: () => import('@/views/screen/review/workflow/history/index'),
+        component: () => import('@/views/screen/deploy/workflow/history/index'),
         access: [Access.MANAGE_CALENDAR, Access.MANAGE_GROUP],
         meta: { title: '发布历史' }
       }

+ 0 - 1
src/views/device/record/index.vue

@@ -11,7 +11,6 @@
       shrink
       checkbox
       check-on-click-node
-      :default-checked-nodes="devices"
       @loaded="onDeviceLoaded"
       @change="onChange"
     />

+ 93 - 26
src/views/screen/deploy/device/index.vue

@@ -9,10 +9,9 @@
     <div class="l-flex__none l-flex--row c-step__header">
       <button
         class="l-flex__none c-sibling-item o-button"
-        :class="{ hidden: active === 0 }"
         @click="onPresent"
       >
-        上一步
+        {{ presentBtnMsg }}
       </button>
       <el-steps
         :active="active"
@@ -60,7 +59,6 @@
           <el-select
             v-model="eventOptions.type"
             class="c-sibling-item--v nearer u-width--sm"
-            @change="onChangeType"
           >
             <el-option
               v-for="option in typeOptions"
@@ -106,6 +104,13 @@
         />
       </template>
     </div>
+    <radio-table-dialog
+      ref="workflowTableDialog"
+      title="历史编单"
+      :schema="workflowSchema"
+      @confirm="onChooseWorkflow"
+    />
+    <workflow-dialog ref="workflowDialog" />
     <event-frequency-config-dialog
       ref="eventFrequencyConfigDialog"
       @confirm="onConfirmEventFrequency"
@@ -131,8 +136,8 @@
 
 <script>
 import {
+  State,
   EventTarget,
-  EventTargetInfo,
   PublishType,
   PublishTargetType,
   EventPriority,
@@ -145,6 +150,11 @@ import {
   getEventDescription
 } from '@/utils/event'
 import { publish } from '@/api/platform'
+import {
+  getWorkflowsByUser,
+  getWorkflowDetail
+} from '@/api/workflow'
+import WorkflowDialog from '../workflow/components/WorkflowDialog.vue'
 import EventFrequencyConfigDialog from '../components/EventFrequencyConfigDialog.vue'
 
 const DEFAULT_PLAYBACK = 'DEFAULT_PLAYBACK'
@@ -152,6 +162,7 @@ const DEFAULT_PLAYBACK = 'DEFAULT_PLAYBACK'
 export default {
   name: 'DeployDevice',
   components: {
+    WorkflowDialog,
     EventFrequencyConfigDialog
   },
   data () {
@@ -166,7 +177,7 @@ export default {
         { value: EventPriority.EMBEDDED, label: EventPriorityInfo[EventPriority.EMBEDDED] },
         { value: EventPriority.EMERGENT, label: EventPriorityInfo[EventPriority.EMERGENT] }
       ],
-      eventOptions: null,
+      eventOptions: { type: PublishTargetType.EVENT },
       typeOptions: [
         { value: PublishTargetType.EVENT, label: '事件' },
         { value: DEFAULT_PLAYBACK, label: '默认播放' }
@@ -186,7 +197,31 @@ export default {
       },
       events: [],
       eventTarget: this.createEventTarget(),
-      conflicts: []
+      conflicts: [],
+      workflowSchema: {
+        list: getWorkflowsByUser,
+        filters: [
+          { key: 'status', type: 'select', placeholder: '流程状态', options: [
+            { value: State.SUBMITTED, label: '待审核' },
+            { value: State.RESOLVED, label: '通过' },
+            { value: State.REJECTED, label: '驳回' }
+          ] }
+        ],
+        cols: [
+          { prop: 'flowDesc', label: '优先级', width: 100, align: 'center' },
+          { prop: 'flowName', label: '上播内容' },
+          { label: '状态', type: 'tag', render: ({ status }) => {
+            return {
+              type: ['', 'primary', 'success', 'danger'][status],
+              label: ['', '待审核', '通过', '驳回'][status]
+            }
+          } },
+          { prop: 'createTime', label: '提交时间', width: 160, align: 'center' },
+          { type: 'invoke', render: [
+            { label: '详情', on: this.onViewWorkflow }
+          ] }
+        ]
+      }
     }
   },
   computed: {
@@ -199,15 +234,23 @@ export default {
       }
     },
     isDefaultPlayback () {
-      return this.eventOptions?.type === DEFAULT_PLAYBACK
+      return this.eventOptions.type === DEFAULT_PLAYBACK
+    },
+    presentBtnMsg () {
+      return this.active === 0 ? '导入历史编单' : '上一步'
     },
     btnMsg () {
       return this.active < 1 ? '下一步' : '发布'
     }
   },
   methods: {
+    createEventTarget (options) {
+      return { type: EventTarget.ASSETS, ...options }
+    },
     onPresent () {
-      if (this.active > 0) {
+      if (this.active === 0) {
+        this.$refs.workflowTableDialog.show()
+      } else {
         this.active = 0
       }
     },
@@ -223,9 +266,6 @@ export default {
           this.publish().then(() => {
             this.active = 0
             this.$refs.tree.reset()
-            this.eventOptions = null
-            this.priority = EventPriority.INSERTED
-            this.events = []
             this.eventTarget = this.createEventTarget()
           })
           break
@@ -236,12 +276,6 @@ export default {
     onChange (devices) {
       this.selectedDevices = devices
     },
-    createEventOptions (type) {
-      return {
-        type,
-        target: null
-      }
-    },
     checkDevices () {
       const devices = this.selectedDevices
       const length = devices.length
@@ -260,25 +294,17 @@ export default {
         //   '选择的设备分辨率不一致',
         //   { type: 'warning' }
         // ).then(() => {
-        //   this.eventOptions = this.createEventOptions(PublishTargetType.EVENT)
         //   this.active += 1
         // })
         // return false
       } else {
         this.ratio = ratio
       }
-      this.eventOptions = this.createEventOptions(PublishTargetType.EVENT)
       return true
     },
-    onChangeType (type) {
-      this.eventOptions = this.createEventOptions(type)
-    },
     onView ({ id }) {
       this.$refs.materialDialog.showSchedule(id)
     },
-    createEventTarget () {
-      return { type: EventTarget.ASSETS }
-    },
     getEvents () {
       return Promise.resolve(({ data: this.events }))
     },
@@ -379,7 +405,7 @@ export default {
               }
             }
           }),
-        name: detail?.name || EventTargetInfo[eventTarget.type],
+        name: detail?.name || '',
         resolutionRatio: detail?.resolutionRatio
       })
     },
@@ -411,6 +437,47 @@ export default {
           )
         )
       )
+    },
+    onViewWorkflow ({ id }) {
+      this.$refs.workflowDialog.show(id)
+    },
+    onChooseWorkflow ({ value: { id }, done }) {
+      getWorkflowDetail(id, { loading: true }).then(({ data }) => {
+        const { programCalendarName, calendarReleaseDeviceList, targetList } = data
+        const targets = targetList.map(JSON.parse)
+        this.events = targets.map(({ detail: { priority, target, ...event } }) => {
+          return {
+            key: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
+            origin: event,
+            time: getEventDescription(event)
+          }
+        })
+        const { detail: { priority, target } } = targets[0]
+        this.priority = priority
+        this.eventOptions.type = priority === DEFAULT_PLAYBACK ? DEFAULT_PLAYBACK : PublishTargetType.EVENT
+        this.eventTarget = target.type === EventTarget.ASSETS
+          ? {
+            name: programCalendarName,
+            ...this.createEventTarget(target)
+          }
+          : this.createEventTarget(target)
+        const deviceCount = calendarReleaseDeviceList.length
+        const validDevices = calendarReleaseDeviceList.filter(({ deviceName }) => !!deviceName)
+        const validDeviceCount = validDevices.length
+        const diffDeviceCount = this.$refs.tree.reset(validDevices.map(({ deviceId }) => deviceId))
+        console.log('deploy import', diffDeviceCount, validDeviceCount, deviceCount)
+        if (diffDeviceCount > 0 || validDeviceCount !== deviceCount) {
+          this.$message({
+            type: 'warning',
+            message: '部分设备无操作权限或已移除,请确认设备'
+          })
+        } else {
+          this.$nextTick(() => {
+            this.onNext()
+          })
+        }
+        done()
+      })
     }
   }
 }

+ 0 - 8
src/views/screen/review/workflow/api.js → src/views/screen/deploy/workflow/api.js

@@ -54,14 +54,6 @@ export function getWorkflow (workflowId, options) {
   })
 }
 
-export function getWorkflowDetail (workflowId, options) {
-  return (options?.loading ? send : request)({
-    url: '/workflow/getBussinessData',
-    method: 'POST',
-    params: { workflowId }
-  })
-}
-
 export function getWorkflowHistory (workflowId) {
   return request({
     url: '/workflow/getById',

+ 0 - 0
src/views/screen/review/workflow/audit/index.vue → src/views/screen/deploy/workflow/audit/index.vue


+ 0 - 0
src/views/screen/review/workflow/components/PublishDetailDialog.vue → src/views/screen/deploy/workflow/components/PublishDetailDialog.vue


+ 0 - 0
src/views/screen/review/workflow/components/WorkflowDialog.vue → src/views/screen/deploy/workflow/components/WorkflowDialog.vue


+ 0 - 0
src/views/screen/review/workflow/history/index.vue → src/views/screen/deploy/workflow/history/index.vue


+ 0 - 0
src/views/screen/review/workflow/index.vue → src/views/screen/deploy/workflow/index.vue


+ 0 - 0
src/views/screen/review/workflow/mine/index.vue → src/views/screen/deploy/workflow/mine/index.vue


+ 0 - 0
src/views/screen/review/workflow/utils.js → src/views/screen/deploy/workflow/utils.js