Bladeren bron

feat: 邀约码统计 统计结果展示与导出

furffico 1 jaar geleden
bovenliggende
commit
10308a48f5

+ 38 - 0
src/utils/datasheet2csv.ts

@@ -0,0 +1,38 @@
+import { toString } from "lodash"
+
+const escape_string = (v: string): string => {
+    if (/^ |[,"\r\n]| $/.test(v)) {
+        return '"' + v.replaceAll('"', '""') + '"'
+    } else {
+        return v
+    }
+}
+
+const encode = (v: any): string => {
+    return escape_string(toString(v))
+}
+
+export const datasheet2csv = <T>(
+    itemList: T[],  // 对象列表
+    converter: (item: T) => any[], // 从单个对象提取需要的项目
+    headers?: string[],  // 表头
+): Blob => {
+    /* 
+     * 参照 RFC4180: Common Format and MIME Type for Comma-Separated Values (CSV) Files
+     * url: https://datatracker.ietf.org/doc/html/rfc4180 
+     * 然而 Microsoft Excel 不完全遵守 RFC4180,因此还有一些特殊情况要考虑
+     */
+    const eol = '\r\n', sep = ',', bom = '\uFEFF'
+    const csv = [bom]
+    if (headers) {
+        csv.push(headers.map(escape_string).join(sep) + eol)
+    }
+    itemList.forEach((item) => {
+        const strings = converter(item)
+        csv.push(strings.map(encode).join(sep) + eol)
+    })
+    const blob = new Blob(csv, { type: 'text/csv;charset=utf-8' })
+    return blob
+}
+
+export default datasheet2csv

+ 2 - 1
src/utils/formatters.ts

@@ -4,7 +4,8 @@ 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 function datetime_format(v: Dayjs | Date | string, format: string = 'YYYY-MM-DD HH:mm:ss'): string {
+export function datetime_format(v?: Dayjs | Date | string, format: string = 'YYYY-MM-DD HH:mm:ss'): string {
+    v = v || new Date()
     if (v instanceof Date || typeof v === 'string') {
         return dayjs(v).format(format)
     } else {

+ 19 - 2
src/utils/index.ts

@@ -1,7 +1,24 @@
 import Vue from 'vue'
 import router from '@/router'
 import { v4 as uuidv4 } from 'uuid';
-export * from "./treedataconv.js";
+
+export const download_blob = (blob: Blob, filename: string) => {
+  // 创建一个 a 标签
+  const link = document.createElement("a")
+  // 一个 URL,可以是任意协议的,不只是 HTTP 协议
+  // 这里创建了一个 Blob URL
+  link.href = URL.createObjectURL(blob)
+  console.log("Blob URL:", link.href)
+
+  // 去除不可用的字符
+  const filename_escaped = filename.replace(/[\\\/:\*\?"<>\|]/g, "").trim()
+
+  link.download = filename_escaped
+  link.click()
+  // 需要释放 URL
+  URL.revokeObjectURL(link.href)
+  link.remove()
+}
 
 /**
  * 获取uuid
@@ -30,7 +47,7 @@ export function isAuth(key: string): boolean {
 /**
  * 清除登录信息
  */
-export function clearLoginInfo () {
+export function clearLoginInfo() {
   //@ts-ignore
   Vue.cookie.delete('Authorization')
   //@ts-ignore

+ 0 - 72
src/utils/treedataconv.js

@@ -1,72 +0,0 @@
-
-/**
- * 树形数据转换
- * @param {*} data
- * @param {*} id
- * @param {*} pid
- */
-export function treeDataTranslate(
-    data,
-    id= 'id',
-    pid= 'parentId',
-  ) {
-    var res = []
-    let temp = {}
-    for (var i = 0; i < data.length; i++) {
-      temp[data[i][id]] = data[i]
-    }
-    for (var k = 0; k < data.length; k++) {
-      if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
-        if (!temp[data[k][pid]]['children']) {
-          temp[data[k][pid]]['children'] = []
-        }
-        if (!temp[data[k][pid]]['_level']) {
-          temp[data[k][pid]]['_level'] = 1
-        }
-        data[k]['_level'] = temp[data[k][pid]]._level + 1
-        temp[data[k][pid]]['children'].push(data[k])
-      } else {
-        res.push(data[k])
-      }
-    }
-    return res
-  }
-  
-  treeDataTranslate([{
-    id: 1,
-    pid: 2
-  }], "id", "pid")
-  
-  /**
-   * 将数组中的parentId列表取出,倒序排列
-   * @param {*} data
-   * @param {*} id
-   * @param {*} pid
-   */
-  export function idList (data, val, id = 'id', children = 'children') {
-    let res = []
-    idListFromTree(data, val, res, id)
-    return res
-  }
-  
-  /**
-   * @param {*} data
-   * @param {*} id
-   * @param {*} pid
-   */
-  function idListFromTree (data, val, res = [], id = 'id', children = 'children') {
-    for (let i = 0; i < data.length; i++) {
-      const element = data[i]
-      if (element[children]) {
-        if (idListFromTree(element[children], val, res, id, children)) {
-          res.push(element[id])
-          return true
-        }
-      }
-      if (element[id] === val) {
-        res.push(element[id])
-        return true
-      }
-    }
-  }
-  

+ 35 - 14
src/views/modules/liveroom/liveroomInvite-stats.vue

@@ -1,7 +1,7 @@
 <template>
   <avue-crud ref="crud" :page="page" :data="dataList" :option="tableOption" @on-load="getDataList">
     <template slot="menuLeft">
-      <el-button type="success" icon="el-icon-download" size="small">导出结果至Excel</el-button>
+      <el-button type="success" icon="el-icon-download" size="small" @click="exportToCSVFile">导出结果至Excel</el-button>
     </template>
   </avue-crud>
 </template>
@@ -11,6 +11,19 @@ import Vue from 'vue'
 import { IPage } from '@/utils/vo'
 import httpx from '@/utils/httpx'
 import { tableOption } from "@/avue/crud/liveroomInviteStats"
+import datasheet2csv from "@/utils/datasheet2csv"
+import { download_blob } from "@/utils/index"
+import { datetime_format } from '@/utils/formatters'
+
+const getPages = (count: number) => {
+  return {
+    total: count,
+    currentPage: 1,
+    pageSize: count,
+    pageSizes: [count],
+    layout: '', //隐藏分页
+  }
+}
 
 export default Vue.extend({
   props: {
@@ -21,12 +34,11 @@ export default Vue.extend({
   data() {
     return {
       dataListLoading: false,
-      page: {
-        total: 0,
-        currentPage: 1,
-        pageSize: 10,
-      } as IPage,
-      dataList: [],
+      page: getPages(10),
+      dataList: [] as {
+        manager_info: string,
+        count: number,
+      }[],
     }
   },
 
@@ -41,21 +53,30 @@ export default Vue.extend({
       const [start, end] = this.daterange
 
       this.dataListLoading = true
-      httpx.post(httpx.makeurl('/rooms/queryConsultList'), {
-        start,
-        end,
+      httpx.post(httpx.makeurl('/rooms/invitesCountList'), {
+        startTime: start,
+        endTime: end,
         roomId: this.roomId,
-        limit: page?.pageSize || this.page.pageSize,
-        page: page?.currentPage || this.page.currentPage,
       }).then(({ data }) => {
-        this.dataList = data.consultList
-        this.page.total = data.total
+        this.dataList = data.invitesList
         this.dataListLoading = false
+        this.page = getPages(data.invitesList.length)
         if (done) {
           done()
         }
       })
     },
+
+    exportToCSVFile() {
+      const blob = datasheet2csv(
+        this.dataList,
+        (v) => [v.manager_info, v.count],
+        ['客户经理信息', '有效邀约数量']
+      )
+      const start = datetime_format(this.daterange[0] as string, 'YYYY-MM-DD HH-mm-ss')
+      const end = datetime_format(this.daterange[1] as string, 'YYYY-MM-DD HH-mm-ss')
+      download_blob(blob, `客户邀约记录_${this.roomInfo.title}_${start}至${end}.csv`)
+    },
   },
 
   watch: {

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

@@ -7,7 +7,7 @@
       </el-form-item>
     </el-form>
     <el-tabs v-model="activeTab">
-      <el-tab-pane label="统计结果" name="stat">
+      <el-tab-pane label="统计结果" name="stats">
         <keep-alive>
           <invitation-stats :daterange="formatted_daterange" :roomId="roomId" :roomInfo="roominfo" />
         </keep-alive>