Casper Dai 2 роки тому
батько
коміт
55e119533a

+ 14 - 1
src/router/index.js

@@ -359,25 +359,38 @@ export const asyncRoutes = [
   {
     path: '/d',
     component: Layout,
-    access: Access.MANAGE_TENANT,
     meta: { icon: 'logger', title: '数据统计' },
     children: [
       {
         path: 'asset',
         component: () => import('@/views/dashboard/statistic/index'),
+        access: Access.MANAGE_TENANT,
         meta: { title: '资源播放' }
       },
       {
         path: 'logger',
         component: () => import('@/views/realm/logger/index'),
+        access: Access.MANAGE_TENANT,
         meta: { title: '操作日志' }
       },
+      {
+        path: 'report',
+        component: () => import('@/views/realm/report/index'),
+        access: [
+          Access.MANAGE_TENANT,
+          Access.MANAGE_GROUP,
+          Access.MANAGE_CALENDAR
+        ],
+        meta: { title: '报表导出' }
+      },
       {
         path: 'internal:dashboard/v0',
+        access: Access.MANAGE_TENANT,
         meta: { title: '大数据V0', internal: true }
       },
       {
         path: 'internal:dashboard/v1',
+        access: Access.MANAGE_TENANT,
         meta: { title: '大数据V1', internal: true }
       }
     ]

+ 89 - 0
src/views/realm/report/api.js

@@ -0,0 +1,89 @@
+import { parseTime } from '@/utils'
+import { downloadRequest } from '@/utils/request'
+import {
+  send,
+  addTenant
+} from '@/api/base'
+
+function download ({ data, headers }, fileName) {
+  const blob = new Blob([data], { type: headers['content-type'] })
+  const url = window.URL.createObjectURL(blob)
+  const dom = document.createElement('a')
+  dom.href = url
+  dom.download = decodeURI(fileName)
+  dom.style.display = 'none'
+  document.body.appendChild(dom)
+  dom.click()
+  dom.parentNode.removeChild(dom)
+  window.URL.revokeObjectURL(url)
+}
+
+export function getDeviceExcel () {
+  return send({
+    url: '/device/tenant/export/excel',
+    method: 'GET',
+    params: addTenant()
+  }, downloadRequest).then(response => {
+    download(response, `设备报表${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`)
+  })
+}
+
+export function getMaterialExcel (params, fileName = '') {
+  return send({
+    url: '/minio-data/export',
+    method: 'GET',
+    params: addTenant(params)
+  }, downloadRequest).then(response => {
+    download(response, `资源报表${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}_${fileName}.xlsx`)
+  })
+}
+
+export function getContentExcel (params, fileName = '') {
+  return send({
+    url: '/minio-data/usage/degree/export',
+    method: 'GET',
+    params: addTenant(params)
+  }, downloadRequest).then(response => {
+    download(response, `内容播放${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}_${fileName}.xlsx`)
+  })
+}
+
+export function getAccountExcel (params, fileName = '') {
+  return send({
+    url: '/admin/department/user/export',
+    method: 'GET',
+    params: addTenant(params)
+  }, downloadRequest).then(response => {
+    download(response, `账号报表${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}_${fileName}.xlsx`)
+  })
+}
+
+export function getAuditExcel (params, fileName = '') {
+  return send({
+    url: '/orchestration/calendarReleaseHis/export',
+    method: 'GET',
+    params
+  }, downloadRequest).then(response => {
+    download(response, `三审报表${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}_${fileName}.xls`)
+  })
+}
+
+export function getProgramGuideExcel (params, fileName = '') {
+  return send({
+    url: '/ad/tenant/scheduling/export',
+    method: 'GET',
+    params
+  }, downloadRequest).then(response => {
+    download(response, `节目单${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}_${fileName}.xlsx`)
+  })
+}
+
+export function getDeviceAdExcel ({ deviceId, date }, fileName = '') {
+  return send({
+    url: `/minio-data/device/${deviceId}/usage/statistic/export`,
+    method: 'GET',
+    params: { date }
+  }, downloadRequest).then(response => {
+    download(response, `广告播放${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}_${fileName}.xlsx`)
+  })
+}

+ 52 - 0
src/views/realm/report/components/AccountDialog.vue

@@ -0,0 +1,52 @@
+<template>
+  <confirm-dialog
+    ref="dialog"
+    size="lg fixed"
+    title="部门账号报表"
+    @confirm="onConfirm"
+  >
+    <template #default>
+      <div class="l-flex__fill l-flex">
+        <department-tree
+          class="c-sibling-item c-sidebar u-width--xl"
+          @change="onGroupChanged"
+        />
+        <div class="c-sibling-item far">
+          <el-checkbox v-model="recursive">
+            级联
+          </el-checkbox>
+        </div>
+      </div>
+    </template>
+  </confirm-dialog>
+</template>
+
+<script>
+import { getAccountExcel } from '../api'
+
+export default {
+  name: 'AccountDialog',
+  data () {
+    return {
+      recursive: false
+    }
+  },
+  methods: {
+    show () {
+      this.recursive = false
+      this.$refs.dialog.show()
+    },
+    onGroupChanged (group) {
+      this.$group = group
+    },
+    onConfirm (done) {
+      getAccountExcel({
+        departmentId: this.$group.id,
+        recursive: this.recursive ? 1 : 0
+      }, this.recursive
+        ? `${this.$group.name}_全量`
+        : `${this.$group.name}`).then(done)
+    }
+  }
+}
+</script>

+ 72 - 0
src/views/realm/report/components/AuditDialog.vue

@@ -0,0 +1,72 @@
+<template>
+  <confirm-dialog
+    ref="dialog"
+    size="lg fixed"
+    title="部门三审报表"
+    @confirm="onConfirm"
+  >
+    <template #default>
+      <div class="l-flex__fill l-flex">
+        <department-tree
+          class="c-sibling-item c-sidebar u-width--xl"
+          @change="onGroupChanged"
+        />
+        <div class="c-sibling-item far">
+          <div class="c-sibling-item--v u-required">日期范围</div>
+          <el-date-picker
+            v-model="dateRange"
+            class="c-sibling-item--v"
+            type="daterange"
+            range-separator="至"
+            value-format="yyyy-MM-dd"
+            :picker-options="pickerOptions"
+            :editable="false"
+            :clearable="false"
+          />
+        </div>
+      </div>
+    </template>
+  </confirm-dialog>
+</template>
+
+<script>
+import { parseTime } from '@/utils'
+import { getAuditExcel } from '../api'
+
+export default {
+  name: 'AuditDialog',
+  data () {
+    return {
+      dateRange: []
+    }
+  },
+  computed: {
+    pickerOptions () {
+      return {
+        disabledDate: this.isDisableDate
+      }
+    }
+  },
+  methods: {
+    show () {
+      const date = parseTime(new Date(), '{y}-{m}-{d}')
+      this.dateRange = [date, date]
+      this.$refs.dialog.show()
+    },
+    onGroupChanged (group) {
+      this.$group = group
+    },
+    isDisableDate (date) {
+      return date > Date.now()
+    },
+    onConfirm (done) {
+      const time = this.dateRange[0] === this.dateRange[1] ? this.dateRange[0] : `${this.dateRange[0]}~${this.dateRange[1]}`
+      getAuditExcel({
+        org: this.$group.path,
+        beginCreateTime: `${this.dateRange[0]} 00:00:00`,
+        endCreateTime: `${this.dateRange[1]} 23:59:59`
+      }, `${this.$group.name}_${time}`).then(done)
+    }
+  }
+}
+</script>

+ 63 - 0
src/views/realm/report/components/ContentDialog.vue

@@ -0,0 +1,63 @@
+<template>
+  <confirm-dialog
+    ref="dialog"
+    title="平台内容播放"
+    @confirm="onConfirm"
+  >
+    <template #default>
+      <div class="u-align-self--center">
+        <div class="c-sibling-item--v u-required">日期范围</div>
+        <el-date-picker
+          v-model="dateRange"
+          class="c-sibling-item--v"
+          type="daterange"
+          range-separator="至"
+          value-format="yyyy-MM-dd"
+          :picker-options="pickerOptions"
+          :editable="false"
+          :clearable="false"
+        />
+      </div>
+    </template>
+  </confirm-dialog>
+</template>
+
+<script>
+import { parseTime } from '@/utils'
+import { getContentExcel } from '../api'
+
+export default {
+  name: 'ContentDialog',
+  data () {
+    return {
+      dateRange: []
+    }
+  },
+  computed: {
+    pickerOptions () {
+      return {
+        disabledDate: this.isDisableDate
+      }
+    }
+  },
+  methods: {
+    show () {
+      const date = parseTime(new Date(), '{y}-{m}-{d}')
+      this.dateRange = [date, date]
+      this.$refs.dialog.show()
+    },
+    isDisableDate (date) {
+      return date > Date.now()
+    },
+    onConfirm (done) {
+      const time = this.dateRange[0] === this.dateRange[1] ? this.dateRange[0] : `${this.dateRange[0]}~${this.dateRange[1]}`
+      const endDate = new Date(this.dateRange[1].replace(/-/g, '/'))
+      endDate.setDate(endDate.getDate() + 1)
+      getContentExcel({
+        startDate: this.dateRange[0],
+        endDate: parseTime(endDate, '{y}-{m}-{d}')
+      }, time).then(done)
+    }
+  }
+}
+</script>

+ 76 - 0
src/views/realm/report/components/DeviceAdDialog.vue

@@ -0,0 +1,76 @@
+<template>
+  <confirm-dialog
+    ref="dialog"
+    size="lg fixed"
+    title="设备广告播放"
+    @confirm="onConfirm"
+  >
+    <template #default>
+      <div class="l-flex__fill l-flex">
+        <device-tree-single
+          class="c-sibling-item c-sidebar u-font-size--sm u-width--xl"
+          @change="onDeviceChange"
+        />
+        <div class="c-sibling-item far">
+          <div class="c-sibling-item--v u-required">日期</div>
+          <el-date-picker
+            v-model="date"
+            class="c-sibling-item--v"
+            type="date"
+            value-format="yyyy-MM-dd"
+            :picker-options="pickerOptions"
+            :editable="false"
+            :clearable="false"
+          />
+        </div>
+      </div>
+    </template>
+  </confirm-dialog>
+</template>
+
+<script>
+import { parseTime } from '@/utils'
+import { getDeviceAdExcel } from '../api'
+
+export default {
+  name: 'DeviceAdDialog',
+  data () {
+    return {
+      date: null
+    }
+  },
+  computed: {
+    pickerOptions () {
+      return {
+        disabledDate: this.isDisableDate
+      }
+    }
+  },
+  methods: {
+    show () {
+      this.$device = null
+      this.date = parseTime(new Date(), '{y}-{m}-{d}')
+      this.$refs.dialog.show()
+    },
+    onDeviceChange (device) {
+      this.$device = device
+    },
+    isDisableDate (date) {
+      return date > Date.now()
+    },
+    onConfirm (done) {
+      if (!this.$device) {
+        this.$message({
+          type: 'warning',
+          message: '请选择设备'
+        })
+        return
+      }
+      getDeviceAdExcel({
+        deviceId: this.$device.id,
+        date: this.date
+      }, `${this.$device.name}_${this.date}`).then(done)
+    }
+  }
+}
+</script>

+ 52 - 0
src/views/realm/report/components/MaterialDialog.vue

@@ -0,0 +1,52 @@
+<template>
+  <confirm-dialog
+    ref="dialog"
+    size="lg fixed"
+    title="部门资源报表"
+    @confirm="onConfirm"
+  >
+    <template #default>
+      <div class="l-flex__fill l-flex">
+        <department-tree
+          class="c-sibling-item c-sidebar u-width--xl"
+          @change="onGroupChanged"
+        />
+        <div class="c-sibling-item far">
+          <el-checkbox v-model="recursive">
+            级联
+          </el-checkbox>
+        </div>
+      </div>
+    </template>
+  </confirm-dialog>
+</template>
+
+<script>
+import { getMaterialExcel } from '../api'
+
+export default {
+  name: 'MaterialDialog',
+  data () {
+    return {
+      recursive: false
+    }
+  },
+  methods: {
+    show () {
+      this.recursive = false
+      this.$refs.dialog.show()
+    },
+    onGroupChanged (group) {
+      this.$group = group
+    },
+    onConfirm (done) {
+      getMaterialExcel({
+        departmentId: this.$group.id,
+        deptRecursive: this.recursive
+      }, this.recursive
+        ? `${this.$group.name}_全量`
+        : `${this.$group.name}`).then(done)
+    }
+  }
+}
+</script>

+ 76 - 0
src/views/realm/report/components/ProgramGuideDialog.vue

@@ -0,0 +1,76 @@
+<template>
+  <confirm-dialog
+    ref="dialog"
+    size="lg fixed"
+    title="设备节目单"
+    @confirm="onConfirm"
+  >
+    <template #default>
+      <div class="l-flex__fill l-flex">
+        <device-tree-single
+          class="c-sibling-item c-sidebar u-font-size--sm u-width--xl"
+          @change="onDeviceChange"
+        />
+        <div class="c-sibling-item far">
+          <div class="c-sibling-item--v u-required">日期</div>
+          <el-date-picker
+            v-model="date"
+            class="c-sibling-item--v"
+            type="date"
+            value-format="yyyy-MM-dd"
+            :picker-options="pickerOptions"
+            :editable="false"
+            :clearable="false"
+          />
+        </div>
+      </div>
+    </template>
+  </confirm-dialog>
+</template>
+
+<script>
+import { parseTime } from '@/utils'
+import { getProgramGuideExcel } from '../api'
+
+export default {
+  name: 'ProgramGuideDialog',
+  data () {
+    return {
+      date: null
+    }
+  },
+  computed: {
+    pickerOptions () {
+      return {
+        disabledDate: this.isDisableDate
+      }
+    }
+  },
+  methods: {
+    show () {
+      this.$device = null
+      this.date = parseTime(new Date(), '{y}-{m}-{d}')
+      this.$refs.dialog.show()
+    },
+    onDeviceChange (device) {
+      this.$device = device
+    },
+    isDisableDate (date) {
+      return date > Date.now()
+    },
+    onConfirm (done) {
+      if (!this.$device) {
+        this.$message({
+          type: 'warning',
+          message: '请选择设备'
+        })
+        return
+      }
+      getProgramGuideExcel({
+        deviceId: this.$device.id,
+        startDate: this.date
+      }, `${this.$device.name}_${this.date}`).then(done)
+    }
+  }
+}
+</script>

+ 126 - 0
src/views/realm/report/index.vue

@@ -0,0 +1,126 @@
+<template>
+  <wrapper
+    fill
+    margin
+    padding
+    background
+  >
+    <template v-if="isTopGroupAdmin">
+      <span class="c-sibling-item--v u-font-size--sm u-bold">平台数据</span>
+      <div class="c-sibling-item--v l-grid--info mini">
+        <button
+          v-if="isTopGroupAdmin"
+          class="o-button"
+          @click="getDeviceExcel"
+        >
+          设备报表
+        </button>
+        <button
+          v-if="isTopGroupAdmin"
+          class="o-button"
+          @click="getContentExcel"
+        >
+          内容播放报表
+        </button>
+      </div>
+    </template>
+    <template v-if="isGroupAdmin">
+      <span class="c-sibling-item--v u-font-size--sm u-bold">部门数据</span>
+      <div class="c-sibling-item--v l-grid--info mini">
+        <button
+          class="o-button"
+          @click="getMaterialExcel"
+        >
+          资源报表
+        </button>
+        <button
+          class="o-button"
+          @click="getAccountExcel"
+        >
+          账号报表
+        </button>
+        <button
+          class="o-button"
+          @click="getAuditExcel"
+        >
+          三审报表
+        </button>
+      </div>
+    </template>
+    <span class="c-sibling-item--v u-font-size--sm u-bold">设备数据</span>
+    <div class="c-sibling-item--v l-grid--info mini">
+      <button
+        class="o-button"
+        @click="getProgramGuideExcel"
+      >
+        节目单
+      </button>
+      <button
+        class="o-button"
+        @click="getDeviceAdExcel"
+      >
+        广告播放
+      </button>
+    </div>
+    <material-dialog ref="materialDialog" />
+    <account-dialog ref="accountDialog" />
+    <audit-dialog ref="auditDialog" />
+    <content-dialog ref="contentDialog" />
+    <program-guide-dialog ref="programGuideDialog" />
+    <device-ad-dialog ref="deviceAdDialog" />
+  </wrapper>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { getDeviceExcel } from './api'
+import MaterialDialog from './components/MaterialDialog.vue'
+import AccountDialog from './components/AccountDialog.vue'
+import AuditDialog from './components/AuditDialog.vue'
+import ContentDialog from './components/ContentDialog.vue'
+import ProgramGuideDialog from './components/ProgramGuideDialog.vue'
+import DeviceAdDialog from './components/DeviceAdDialog.vue'
+
+export default {
+  name: 'Report',
+  components: {
+    MaterialDialog,
+    AccountDialog,
+    AuditDialog,
+    ContentDialog,
+    ProgramGuideDialog,
+    DeviceAdDialog
+  },
+  computed: {
+    ...mapGetters(['isTopGroupAdmin', 'isGroupAdmin'])
+  },
+  watch: {
+    deviceId () {
+      this.$refs.table?.pageTo(1)
+    }
+  },
+  methods: {
+    getDeviceExcel () {
+      getDeviceExcel()
+    },
+    getMaterialExcel () {
+      this.$refs.materialDialog.show()
+    },
+    getAccountExcel () {
+      this.$refs.accountDialog.show()
+    },
+    getAuditExcel () {
+      this.$refs.auditDialog.show()
+    },
+    getContentExcel () {
+      this.$refs.contentDialog.show()
+    },
+    getProgramGuideExcel () {
+      this.$refs.programGuideDialog.show()
+    },
+    getDeviceAdExcel () {
+      this.$refs.deviceAdDialog.show()
+    }
+  }
+}
+</script>