Explorar o código

feat: liveroomStats

furffico hai 1 ano
pai
achega
e0dae4fa81

+ 3 - 1
package.json

@@ -14,6 +14,7 @@
     "core-js": "3.6.5",
     "crypto-js": "^4.1.1",
     "dayjs": "^1.11.9",
+    "echarts": "^5.4.3",
     "element-ui": "2.15.7",
     "lodash": "4.17.20",
     "qs": "^6.11.2",
@@ -23,6 +24,7 @@
     "vue": "2.7.14",
     "vue-class-component": "^7.2.3",
     "vue-cookie": "1.1.4",
+    "vue-echarts": "^6.6.0",
     "vue-property-decorator": "^9.1.2",
     "vue-router": "3.5.2",
     "vuex": "3.6.2"
@@ -63,4 +65,4 @@
     "last 2 versions",
     "not dead"
   ]
-}
+}

+ 73 - 0
pnpm-lock.yaml

@@ -16,6 +16,9 @@ dependencies:
   dayjs:
     specifier: ^1.11.9
     version: registry.npmmirror.com/dayjs@1.11.9
+  echarts:
+    specifier: ^5.4.3
+    version: registry.npmmirror.com/echarts@5.4.3
   element-ui:
     specifier: 2.15.7
     version: registry.npmmirror.com/element-ui@2.15.7(vue@2.7.14)
@@ -43,6 +46,9 @@ dependencies:
   vue-cookie:
     specifier: 1.1.4
     version: registry.npmmirror.com/vue-cookie@1.1.4
+  vue-echarts:
+    specifier: ^6.6.0
+    version: registry.npmmirror.com/vue-echarts@6.6.0(echarts@5.4.3)(vue@2.7.14)
   vue-property-decorator:
     specifier: ^9.1.2
     version: registry.npmmirror.com/vue-property-decorator@9.1.2(vue-class-component@7.2.3)(vue@2.7.14)
@@ -6251,6 +6257,15 @@ packages:
       safer-buffer: registry.npmmirror.com/safer-buffer@2.1.2
     dev: true
 
+  registry.npmmirror.com/echarts@5.4.3:
+    resolution: {integrity: sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz}
+    name: echarts
+    version: 5.4.3
+    dependencies:
+      tslib: registry.npmmirror.com/tslib@2.3.0
+      zrender: registry.npmmirror.com/zrender@5.4.4
+    dev: false
+
   registry.npmmirror.com/editorconfig@0.15.3:
     resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/editorconfig/-/editorconfig-0.15.3.tgz}
     name: editorconfig
@@ -12511,6 +12526,12 @@ packages:
     version: 1.0.0
     dev: true
 
+  registry.npmmirror.com/resize-detector@0.3.0:
+    resolution: {integrity: sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz}
+    name: resize-detector
+    version: 0.3.0
+    dev: false
+
   registry.npmmirror.com/resize-observer-polyfill@1.5.1:
     resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz}
     name: resize-observer-polyfill
@@ -14150,6 +14171,12 @@ packages:
     version: 1.14.1
     dev: true
 
+  registry.npmmirror.com/tslib@2.3.0:
+    resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz}
+    name: tslib
+    version: 2.3.0
+    dev: false
+
   registry.npmmirror.com/tslint@5.20.1(typescript@4.5.5):
     resolution: {integrity: sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tslint/-/tslint-5.20.1.tgz}
     id: registry.npmmirror.com/tslint/5.20.1
@@ -14661,6 +14688,44 @@ packages:
     version: 0.5.11
     dev: false
 
+  registry.npmmirror.com/vue-demi@0.13.11(vue@2.7.14):
+    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz}
+    id: registry.npmmirror.com/vue-demi/0.13.11
+    name: vue-demi
+    version: 0.13.11
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      vue: registry.npmmirror.com/vue@2.7.14
+    dev: false
+
+  registry.npmmirror.com/vue-echarts@6.6.0(echarts@5.4.3)(vue@2.7.14):
+    resolution: {integrity: sha512-PpXDe1wKKzOJTLM8RM4GJxpRi+9eiC0YUJR7i44/AAa0oDatvNctJcZlfq/bUV4KV+HDsCeQzaB6JocrXEy9LA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-echarts/-/vue-echarts-6.6.0.tgz}
+    id: registry.npmmirror.com/vue-echarts/6.6.0
+    name: vue-echarts
+    version: 6.6.0
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.5
+      echarts: ^5.4.1
+      vue: ^2.6.12 || ^3.1.1
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      echarts: registry.npmmirror.com/echarts@5.4.3
+      resize-detector: registry.npmmirror.com/resize-detector@0.3.0
+      vue: registry.npmmirror.com/vue@2.7.14
+      vue-demi: registry.npmmirror.com/vue-demi@0.13.11(vue@2.7.14)
+    dev: false
+
   registry.npmmirror.com/vue-hot-reload-api@2.3.4:
     resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz}
     name: vue-hot-reload-api
@@ -15500,6 +15565,14 @@ packages:
       strip-indent: registry.npmmirror.com/strip-indent@2.0.0
     dev: true
 
+  registry.npmmirror.com/zrender@5.4.4:
+    resolution: {integrity: sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz}
+    name: zrender
+    version: 5.4.4
+    dependencies:
+      tslib: registry.npmmirror.com/tslib@2.3.0
+    dev: false
+
 settings:
   autoInstallPeers: true
   excludeLinksFromLockfile: false

+ 4 - 33
src/avue/crud/liveroomConsult.ts

@@ -1,32 +1,3 @@
-import dayjs, { Dayjs } from "dayjs";
-
-const getShortcut = (label: string, start: Dayjs, end?: Dayjs) => {
-  const start_d = start.toDate()
-  const end_d = end?.toDate() || new Date()
-  return {
-    text: label,
-    onClick: (picker: any) => {
-      picker.$emit('pick', [start_d, end_d]);
-    }
-  }
-}
-
-const getShortcutDays = (label: string, days: number) =>
-  getShortcut(label, dayjs().subtract(days, 'day'))
-
-export const pickerOption = {
-  shortcuts: [
-    getShortcut('今天', dayjs().startOf('day')),
-    getShortcut('本月', dayjs().startOf('month')),
-    getShortcutDays('最近三天', 3),
-    getShortcutDays('最近一周', 7),
-    getShortcutDays('最近两周', 14),
-    getShortcutDays('最近一个月', 30),
-    getShortcutDays('最近两个月', 60),
-    getShortcutDays('最近三个月', 90),
-    getShortcutDays('最近一年', 365),
-  ]
-};
 
 export const tableOption = {
   border: false,
@@ -50,14 +21,14 @@ export const tableOption = {
     label: '客户手机号',
     prop: 'phone',
     width: 150,
-  }, {
-    label: '咨询内容',
-    prop: 'content',
-    align: 'left',
   }, {
     label: '提交时间',
     prop: 'createdAt',
     width: 150,
+  }, {
+    label: '咨询内容',
+    prop: 'content',
+    align: 'left',
   }]
 }
 

+ 4 - 2
src/components/base-table/config.js

@@ -1,3 +1,5 @@
+import { datetime_format } from '@/utils/formatters'
+
 export const order = {
   columns: [
     {
@@ -58,7 +60,7 @@ export const order = {
     },
   ],
 }
-import dayjs from 'dayjs'
+
 export const liveroomProductJumpRecord = {
   columns: [
     {
@@ -77,7 +79,7 @@ export const liveroomProductJumpRecord = {
       title: "点击时间",
       dataIndex: "createAt",
       scopedSlots: {
-        customRender: (label, row, index) => `${dayjs(row.createAt).format("YYYY-MM-DD HH:mm:ss")}`
+        customRender: (label, row, index) => datetime_format(row.createAt)
       },
     },
   ],

+ 83 - 0
src/components/datetime-picker/index.vue

@@ -0,0 +1,83 @@
+
+<template>
+  <el-date-picker v-model="daterange" type="datetimerange" :picker-options="pickerOption" range-separator="至"
+    start-placeholder="开始日期" end-placeholder="结束日期" @change="onChange" :default-time="['00:00:00', '23:59:59']">
+  </el-date-picker>
+</template>
+
+<script lang="ts">
+import Vue from 'vue'
+import dayjs, { Dayjs } from "dayjs";
+
+const getShortcut = (label: string, start: Dayjs, end?: Dayjs) => {
+  const start_d = start.startOf('day').toDate()
+  const end_d = end?.toDate() || new Date()
+  return {
+    text: label,
+    onClick: (picker: any) => {
+      picker.$emit('pick', [start_d, end_d]);
+    }
+  }
+}
+
+const getShortcutDays = (label: string, days: number) =>
+  getShortcut(label, dayjs().subtract(days, 'day'))
+
+const end_of_today = dayjs().endOf('day')
+
+const shortcuts = [
+  getShortcut('今天', dayjs().startOf('day')),
+  getShortcut('本周', dayjs().startOf('week')),
+  getShortcut('本月', dayjs().startOf('month')),
+  getShortcutDays('最近三天', 3),
+  getShortcutDays('最近一周', 7),
+  getShortcutDays('最近两周', 14),
+  getShortcutDays('最近一个月', 30),
+  getShortcutDays('最近两个月', 60),
+  getShortcutDays('最近三个月', 90),
+  getShortcutDays('最近一年', 365),
+]
+
+export default Vue.extend({
+  props: {
+    default: {
+      type: Array,
+      default: () => [
+        // 默认从上个月开始,这样打开选择框时展示的就是上个月和这个月,
+        // 否则(从这个月开始的话)会展示这个月和全都不可选的下个月
+        dayjs().subtract(1, 'month').toDate(),
+        new Date(),
+      ]
+    },
+    lowerlimit: {
+      type: Date,
+      default: () => dayjs('2020-01-01').toDate(),
+    }
+  },
+  data() {
+    return {
+      daterange: this.$props.default,
+    }
+  },
+  mounted() {
+    // 加载后触发父组件更新
+    this.$nextTick(this.onChange)
+  },
+  computed: {
+    pickerOption() {
+      return {
+        shortcuts,
+        disabledDate: (date: Date) => {
+          // 禁止选择未来日期
+          return dayjs(date).isAfter(end_of_today) || dayjs(date).isBefore(this.$props.lowerlimit);
+        }
+      }
+    }
+  },
+  methods: {
+    onChange() {
+      this.$emit("change", this.daterange)
+    },
+  }
+})
+</script>

+ 5 - 0
src/router/index.ts

@@ -86,6 +86,11 @@ export const pages = [
     component: _import("modules/liveroom/liveroomConsults"),
     name: "liveroomConsults",
     meta: { title: "直播间客户咨询", sidebar: false, icon: "", isTab: false }
+  }, {
+    path: "/liveroom/statistics/:id",
+    component: _import("modules/liveroom/liveroomStats"),
+    name: "liveroomStats",
+    meta: { title: "直播间观看统计", sidebar: false, icon: "", isTab: false }
   },
 ]
 

+ 1 - 1
src/utils/currency.ts

@@ -1,4 +1,4 @@
-import { toInteger } from "lodash"
+import toInteger from "lodash/toInteger"
 
 export type DatabaseCurrency = number
 // 因为数据库表内价格是以分为单位的整数,因此在展示与请求时需要转换

+ 14 - 1
src/utils/formatters.ts

@@ -1,4 +1,17 @@
 import { database2real } from "./currency"
+import dayjs, { Dayjs } from 'dayjs'
 
 export const currencyFormatter = (v: number) => "¥" + database2real(v).toFixed(2)
-export const row_currencyFormatter = (_: any, v: number | null) => v ? ("¥" + database2real(v).toFixed(2)) : "-"
+export const row_currencyFormatter = (_: any, v: number | null) => v ? ("¥" + database2real(v).toFixed(2)) : "-"
+
+export function datetime_format(v: Dayjs | Date | string, format: string = 'YYYY-MM-DD HH:mm:ss'): string {
+    if (v instanceof Date || typeof v === 'string') {
+        return dayjs(v).format(format)
+    } else {
+        return v.format(format)
+    }
+}
+
+export function datetime_format_short(v: (Parameters<typeof datetime_format>)[0]): string {
+    return datetime_format(v, "M-DD HH:mm")
+}

+ 1 - 1
src/utils/httpRequest.js

@@ -28,7 +28,7 @@ http.interceptors.request.use(config => {
  * 响应拦截
  */
 http.interceptors.response.use(response => {
-   // blob 格式处理
+  // blob 格式处理
   if (response.request.responseType === 'blob') {
     return response
   }

+ 1 - 1
src/utils/httpx.ts

@@ -38,7 +38,7 @@ httpx.interceptors.response.use(
       response.statusText = response.data.msg;
       response.data = response.data.data;
       if ((response.data?.retCode || "0000") !== "0000") {
-        //! 如果后端能更规范一些就好了
+        // 如果后端能更规范一些就好了
         Message({
           message: response.data.retMsg,
           type: 'error',

+ 21 - 17
src/views/modules/liveroom/liveroomConsults.vue

@@ -2,9 +2,7 @@
   <div class="avue-noheader">
     <el-form :inline="true" size="small">
       <el-form-item label="提交时间范围" size="small">
-        <el-date-picker v-model="daterange" type="datetimerange" :picker-options="pickerOption" range-separator="至"
-          start-placeholder="开始日期" end-placeholder="结束日期" @change="getDataList">
-        </el-date-picker>
+        <datetime-picker @change="onDateChange" />
       </el-form-item>
     </el-form>
     <avue-crud ref="crud" :page="page" :data="dataList" :option="tableOption" @on-load="getDataList" />
@@ -13,18 +11,17 @@
 
 <script lang="ts">
 import Vue from 'vue'
-import dayjs from 'dayjs'
 import { IPage } from '@/utils/vo';
 import httpx from '@/utils/httpx';
-import { tableOption, pickerOption } from "@/avue/crud/liveroomConsult"
-import { isInteger } from 'lodash';
+import tableOption from "@/avue/crud/liveroomConsult"
+import isInteger from 'lodash/isInteger';
+import DatetimePicker from "@/components/datetime-picker/index.vue";
+import { datetime_format } from '@/utils/formatters';
 
 export default Vue.extend({
   data() {
     return {
-      pickerOption,
-      tableOption,
-      daterange: [dayjs().startOf("month").toDate(), dayjs().toDate()],
+      daterange: [] as Date[],
       dataListLoading: false,
       page: {
         total: 0,
@@ -36,7 +33,11 @@ export default Vue.extend({
     }
   },
 
-  mounted() {
+  components: {
+    DatetimePicker
+  },
+
+  created() {
     const id_str = this.$route.params.id
     const id = parseInt(id_str)
     if (isNaN(id) || !isInteger(id)) {
@@ -44,7 +45,6 @@ export default Vue.extend({
       this.$shortcut.notFound()
     } else {
       this.roomId = id
-      this.getDataList()
     }
   },
 
@@ -69,21 +69,25 @@ export default Vue.extend({
         if (done) {
           done()
         }
+        this.$message.success({ message: "加载成功", duration: 700 })
       })
     },
-    onSubmitQuery() { },
-    onResetQuery() { },
+    onDateChange(daterange: Date[]) {
+      this.daterange = daterange
+      this.$nextTick(() => this.getDataList())
+    }
   },
 
   computed: {
     formatted_daterange() {
       //@ts-ignore
       const [start, end] = this.daterange
-      const formatted_start = dayjs(start).format('YYYY-MM-DD HH:mm:ss')
-      const formatted_end = dayjs(end).format('YYYY-MM-DD HH:mm:ss')
+      const formatted_start = datetime_format(start)
+      const formatted_end = datetime_format(end)
       return [formatted_start, formatted_end]
-    }
-  }
+    },
+    tableOption() { return tableOption }
+  },
 })
 
 </script>

+ 4 - 4
src/views/modules/liveroom/liveroomEdit.vue

@@ -5,9 +5,9 @@
 <script lang="ts">
 import Vue from 'vue'
 import formOption from "@/avue/form/liveroominfo"
-import { isInteger } from 'lodash'
+import isInteger from 'lodash/isInteger';
 import httpx from '@/utils/httpx'
-import dayjs from 'dayjs'
+import { datetime_format } from "@/utils/formatters"
 
 export default Vue.extend({
   data() {
@@ -33,7 +33,7 @@ export default Vue.extend({
     }
   },
 
-  mounted() {
+  created() {
     const id_str = this.$route.params.id
     const id = parseInt(id_str)
     if (isNaN(id) || !isInteger(id)) {
@@ -93,7 +93,7 @@ export default Vue.extend({
         title: form.title,
         imageShare: form.imageShare[0] || "",
         imageCover: form.imageCover[0] || "",
-        startTime: dayjs(form.startTime).format("YYYY-MM-DD HH:mm:ss"),
+        startTime: datetime_format(form.startTime),
         livePushUrl: form.livePushUrl,
         livePullHlsUrl: form.livePullHlsUrl,
         livePullRtmpUrl: form.livePullRtmpUrl,

+ 3 - 3
src/views/modules/liveroom/liveroomList-opbar.vue

@@ -10,7 +10,7 @@
           <icon-svg name="playback" /></el-button>
       </el-tooltip-->
       <el-tooltip effect="dark" content="观看统计" placement="top" :enterable="false">
-        <el-button type="info" size="small">
+        <el-button type="info" size="small" @click="navigateTo('liveroomStats')">
           <icon-svg name="chartline" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="商品库" placement="top" :enterable="false">
@@ -66,8 +66,8 @@ export default Vue.extend({
     }
   },
   methods: {
-    navigateTo(name: string, query?: Record<string, string>) {
-      this.$router.push({ name, params: { id: this.id.toString() }, query: query })
+    navigateTo(name: string, query?: Record<string, string>, params?: Record<string, string>) {
+      this.$router.push({ name, params: { id: this.id.toString(), ...params }, query: query })
     }
   },
 })

+ 5 - 1
src/views/modules/liveroom/liveroomList.vue

@@ -42,7 +42,6 @@ export default Vue.extend({
         currentPage: 1,
         pageSize: 10,
       },
-      tableOption,
       dataList: [] as any[],
       dataListLoading: true,
       item: null,
@@ -52,6 +51,11 @@ export default Vue.extend({
     Operations,
     LiveroomInfo
   },
+
+  computed: {
+    tableOption() { return tableOption }
+  },
+
   methods: {
     getDataList(page?: IPage, title: string = "", done?: Function) {
       this.dataListLoading = true

+ 5 - 2
src/views/modules/liveroom/liveroomProd-select.vue

@@ -27,7 +27,6 @@ export default Vue.extend({
   },
   data() {
     return {
-      tableOption,
       dataList: [],
       page: {
         total: 0, // 总页数
@@ -39,10 +38,14 @@ export default Vue.extend({
     }
   },
 
-  mounted() {
+  created() {
     this.getRoomGoodsIds()
   },
 
+  computed: {
+    tableOption() { return tableOption }
+  },
+
   methods: {
     getDataList(page?: IPage, params?: { name: string }, done?: Function) {
       this.dataListLoading = true

+ 1 - 1
src/views/modules/liveroom/liveroomProd.vue

@@ -46,7 +46,7 @@ export default Vue.extend({
     ProductSelect
   },
 
-  mounted() {
+  created() {
     const id_str = this.$route.params.id
     const id = parseInt(id_str)
     if (isNaN(id) || !isInteger(id)) {

+ 2 - 2
src/views/modules/liveroom/liveroomProductJumpRecord.vue

@@ -14,7 +14,7 @@
 <script>
 import * as tableConfig from '@/components/base-table/config.js'
 import BaseTable from "@/components/base-table/base-table.vue"
-import dayjs from "dayjs";
+import { datetime_format } from '@/utils/formatters';
 export default {
     data() {
         return {
@@ -80,7 +80,7 @@ export default {
         exportToCSVFile(list) {
             let header = '用户,商品名称,商品链接,点击时间\n';
             let datas = list.map(ele => {
-                return `${ele.phone},${ele.name},${ele.linkInfo},${dayjs(ele.createAt).format("YYYY-MM-DD HH:mm:ss")}\n`;
+                return `${ele.phone},${ele.name},${ele.linkInfo},${datetime_format(ele.createAt)}\n`;
             });
             let dataStrs = [header, ...datas].join('');
             // 创建一个 Blob 对象

+ 135 - 0
src/views/modules/liveroom/liveroomStats.vue

@@ -0,0 +1,135 @@
+<template>
+  <div>
+    <el-form :inline="true" size="small">
+      <el-form-item label="查询时间范围" size="small">
+        <datetime-picker @change="onDateChange" :default="daterange_default" :lowerlimit="lastmonth" />
+      </el-form-item>
+    </el-form>
+    <v-chart class="chart" :option="option" v-show="data.time.length > 0" />
+    <div v-if="data.time.length == 0">暂无数据</div>
+  </div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue'
+
+import VChart from "vue-echarts";
+import { use as echarts_use } from "echarts/core";
+import { CanvasRenderer } from "echarts/renderers";
+import { LineChart } from "echarts/charts";
+import { GridComponent, DatasetComponent, TooltipComponent, LegendComponent } from "echarts/components";
+
+
+import DatetimePicker from "@/components/datetime-picker/index.vue";
+import dayjs from 'dayjs';
+import toSafeInteger from 'lodash/toSafeInteger';
+import httpx from '@/utils/httpx';
+import { datetime_format } from '@/utils/formatters';
+import { range } from 'lodash';
+
+
+echarts_use([
+  CanvasRenderer,
+  LineChart,
+  GridComponent,
+  DatasetComponent,
+  TooltipComponent,
+  LegendComponent
+])
+
+function transpose_matrix<T>(matrix: T[][]): T[][] {
+  // each array should have same length
+  return matrix[0].map((_, i) => {
+    return matrix.map(arr => arr[i])
+  })
+
+}
+
+export default Vue.extend({
+  components: {
+    DatetimePicker,
+    VChart
+  },
+  provide: {
+  },
+  data() {
+    return {
+      data: {
+        time: range(1, 10).map((v => dayjs().add(v, 'hour').toDate())),
+        wx: range(1, 10),
+        zy: range(1, 10).map(v => -2 * v + 10),
+      } as {
+        time: Date[],
+        wx: number[],
+        zy: number[]
+      },
+      roomId: 0,
+      daterange: [] as Date[],
+      daterange_default: [dayjs().startOf('day').toDate(), dayjs().toDate()],
+    };
+  },
+
+  created() {
+    const id = toSafeInteger(this.$route.params.id)
+    if (id <= 0) {
+      //@ts-ignore
+      this.$shortcut.notFound()
+    } else {
+      this.roomId = id
+    }
+  },
+
+  methods: {
+    getData(daterange?: Date[]) {
+      daterange = daterange || this.daterange
+      const startTime = datetime_format(daterange[0])
+      const endTime = datetime_format(daterange[1])
+      httpx.post(
+        httpx.makeurl('/rooms/queryStatisticResult'),
+        { startTime, endTime, roomId: this.roomId }
+      ).then(({ data: result }: {
+        data: {
+          statResultTime: string[],
+          statResultWx: number[],
+          statResultZy: number[]
+        }
+      }) => {
+        this.data = {
+          time: result.statResultTime.map((v => dayjs(v).toDate())),
+          wx: result.statResultWx,
+          zy: result.statResultZy,
+        }
+      })
+    },
+    onDateChange(range: Date[]) {
+      this.daterange = range
+      this.getData(range)
+    }
+  },
+  computed: {
+    option() {
+      return {
+        xAxis: { type: 'time' },
+        yAxis: { type: 'value' },
+        tooltip: { trigger: 'axis' },
+        legend: { data: ['微信', '掌银'] },
+        series: [
+          //@ts-ignore
+          { type: 'line', name: "微信", data: transpose_matrix([this.data.time, this.data.wx]) },
+          //@ts-ignore
+          { type: 'line', name: "掌银", data: transpose_matrix([this.data.time, this.data.zy]) },
+        ],
+      }
+    },
+    lastmonth() {
+      return dayjs().subtract(1, 'month').toDate()
+    }
+  }
+})
+</script>
+
+<style scoped>
+.chart {
+  height: 400px;
+}
+</style>

+ 2 - 2
src/views/modules/order/showDialog.vue

@@ -32,7 +32,7 @@
 
 <script>
 import BaseTable from "./order-BaseTable.vue"
-import dayjs from 'dayjs'
+import { datetime_format } from "@/utils/formatters";
 export default {
     name: 'showDialog',
     components: { BaseTable, },
@@ -95,7 +95,7 @@ export default {
                     Object.keys(this.form).forEach(key => {
                         this.form[key] = data[key]
                     })
-                    this.form.paidAt = dayjs(this.form.paidAt).format("YYYY-MM-DD HH:mm:ss")
+                    this.form.paidAt = datetime_format(this.form.paidAt)
                     this.loading = false
                 }
             )

+ 7 - 2
src/views/modules/prod/category.vue

@@ -9,7 +9,8 @@
 import Vue from 'vue'
 import { tableOption } from '@/avue/crud/category'
 import { httpx, AxiosPromise, AxiosResponse } from "@/utils/httpx"
-import { ceil, clone } from 'lodash'
+import ceil from 'lodash/ceil'
+import clone from 'lodash/clone'
 import { ICategory, IPage } from '@/utils/vo'
 
 export default Vue.extend({
@@ -20,7 +21,6 @@ export default Vue.extend({
       dataListLoading: false,
       addOrUpdateVisible: false,
       resourcesUrl: process.env.VUE_APP_RESOURCES_URL,
-      tableOption,
       page: {
         total: 1,
         currentPage: 1,
@@ -28,6 +28,11 @@ export default Vue.extend({
       } as IPage
     }
   },
+
+  computed: {
+    tableOption() { return tableOption }
+  },
+
   methods: {
     // 获取数据列表
     getDataList(page?: IPage, params: Partial<ICategory> = { name: "" }, done?: Function) {

+ 3 - 2
src/views/modules/prod/prodInfo.vue

@@ -12,7 +12,7 @@ import Vue from 'vue'
 import httpx, { AxiosResponse } from '@/utils/httpx'
 import formOption from "@/avue/form/prodinfo"
 import { ICategory, IProdInfo } from '@/utils/vo'
-import { isInteger } from 'lodash'
+import isInteger from 'lodash/isInteger';
 import { database2real, real2database } from "@/utils/currency"
 
 interface ISelection {
@@ -52,7 +52,8 @@ export default Vue.extend({
       disabled: false,
     }
   },
-  mounted() {
+
+  created() {
     this.getCategoryList("")
     const id_str = this.$route.params.id
     const id = parseInt(id_str)

+ 5 - 1
src/views/modules/prod/prodList.vue

@@ -39,9 +39,13 @@ export default Vue.extend({
       },
       dataListSelections: [],
       dataListLoading: false,
-      tableOption: tableOption,
     }
   },
+
+  computed: {
+    tableOption() { return tableOption }
+  },
+
   methods: {
     // 获取数据列表
     getDataList(page?: IPage, params?: { name: string }, done?: Function) {

+ 5 - 1
src/views/modules/user/user.vue

@@ -19,7 +19,6 @@ export default Vue.extend({
       dataListLoading: false,
       // dataListSelections: [],
       // addOrUpdateVisible: false,
-      tableOption: tableOption,
       page: {
         total: 1, // 总页数
         currentPage: 1, // 当前页数
@@ -27,6 +26,11 @@ export default Vue.extend({
       } as IPage,
     }
   },
+
+  computed: {
+    tableOption() { return tableOption }
+  },
+
   methods: {
     // 获取数据列表
     getDataList(page?: IPage, params?: Partial<IUser>, done?: Function) {