Browse Source

feat(liveroomConsults): 直播间客户咨询列表

furffico 1 năm trước cách đây
mục cha
commit
f711f75734

+ 52 - 14
src/assets/scss/custom.scss

@@ -4,27 +4,65 @@
   border-radius: 10px;
 }
 
-.site-navbar__brand {
-  padding: 0 20px;
+.icon-svg {
+  width: 1em;
+  height: 1em;
+  fill: currentColor;
+  overflow: hidden;
 }
 
-.site-sidebar--fold {
+.site-navbar {
   .site-navbar__brand {
-    padding: 0;
+    padding: 0 20px;
+  }
+
+  .navbar-fold-icon {
+    &.folded {
+      rotate: 180deg;
+    }
+
+    transition: rotate .3s ease-in-out;
+    height: 18px;
+    width: 18px;
+  }
+
+  .site-navbar__float-center {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    line-height: 2ex !important;
+    border-bottom: none !important;
+
+    &.header {
+      padding-left: 10px;
+      border-left: 1px #DCDFE6 solid;
+    }
   }
 }
 
+.site-sidebar {
+  .site-sidebar--fold {
+    .site-navbar__brand {
+      padding: 0;
+    }
+  }
+}
+
+.avue-noheader{
+  .avue-crud__menu,
+  .avue-crud__header {
+    display: none !important;
+  }
 
-.icon-svg {
-  width: 1em;
-  height: 1em;
-  fill: currentColor;
-  overflow: hidden;
 }
 
-.navbar-fold-icon{
-  &.folded{
-    rotate: 180deg;
+.liveroomprod-select {
+  .el-form {
+    height: 60vh;
+    overflow-y: auto;
   }
-  transition: rotate .3s ease-in-out;
-}
+
+  .el-dialog__body {
+    padding: 10px 20px;
+  }
+}

+ 64 - 0
src/avue/crud/liveroomConsult.ts

@@ -0,0 +1,64 @@
+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,
+  selection: false,
+  stripe: true,
+  align: 'center',
+  searchShow: false,
+  searchBtn: false,
+  refreshBtn: false,
+  columnBtn: false,
+  menu: false,
+  searchShowBtn: false,
+  addBtn: false,
+  rowKey: 'id',
+  reserveSelection: true,
+  column: [{
+    label: '#',
+    prop: 'id',
+    width: 50,
+  }, {
+    label: '客户手机号',
+    prop: 'phone',
+    width: 150,
+  }, {
+    label: '咨询内容',
+    prop: 'content',
+    align: 'left',
+  }, {
+    label: '提交时间',
+    prop: 'createdAt',
+    width: 150,
+  }]
+}
+
+export default tableOption;

+ 0 - 24
src/avue/form/liveroominfo.ts

@@ -77,30 +77,6 @@ export default (isnew: boolean = false) => {
         row: true,
         display: isnew,
         tip: "单位为小时",
-      }, {
-        label: "直播推流地址",
-        prop: "livePushUrl",
-        span: 16,
-        row: true,
-        display: !isnew,
-      }, {
-        label: "livePullRtmpUrl",
-        prop: "livePullRtmpUrl",
-        span: 16,
-        row: true,
-        display: !isnew,
-      }, {
-        label: "livePullHlsUrl",
-        prop: "livePullHlsUrl",
-        span: 16,
-        row: true,
-        display: !isnew,
-      }, {
-        label: "回放视频地址",
-        prop: "replayVideoUrl",
-        span: 16,
-        row: true,
-        display: !isnew,
       }, {
         label: '描述',
         prop: 'desc',

+ 2 - 0
src/element-ui.ts

@@ -81,6 +81,7 @@ import {
   Descriptions,
   DescriptionsItem,
   Empty,
+  PageHeader,
 } from 'element-ui'
 import { ElMessageComponent, ElMessageOptions } from 'element-ui/types/message';
 
@@ -172,6 +173,7 @@ export default {
     Vue.use(DescriptionsItem)
     Vue.use(Loading.directive)
     Vue.use(Empty)
+    Vue.use(PageHeader)
 
     Vue.prototype.$loading = Loading.service
     Vue.prototype.$msgbox = MessageBox

+ 7 - 2
src/router/index.ts

@@ -75,12 +75,17 @@ export const pages = [
     path: "/liveroom/liveroom-product-jump-record/:id",
     component: _import("modules/liveroom/liveroomProductJumpRecord"),
     name: "liveroomProductJumpRecord",
-    meta: { title: "商品跳转记录", sidebar: false, icon: "", isTab: false }
+    meta: { title: "直播间商品跳转记录", sidebar: false, icon: "", isTab: false }
   }, {
     path: "/liveroom/product/:id",
     component: _import("modules/liveroom/liveroomProd"),
     name: "liveroomProd",
-    meta: { title: "直播间产品", sidebar: false, icon: "", isTab: false }
+    meta: { title: "直播间商品库", sidebar: false, icon: "", isTab: false }
+  }, {
+    path: "/liveroom/consults/:id",
+    component: _import("modules/liveroom/liveroomConsults"),
+    name: "liveroomConsults",
+    meta: { title: "直播间客户咨询", sidebar: false, icon: "", isTab: false }
   },
 ]
 

+ 0 - 110
src/views/main-navbar-update-password.vue

@@ -1,110 +0,0 @@
-<template>
-  <el-dialog
-    title="修改密码"
-    :visible.sync="visible"
-    :append-to-body="true">
-    <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
-      <el-form-item label="账号">
-        <span>{{ userName }}</span>
-      </el-form-item>
-      <el-form-item label="原密码" prop="password">
-        <el-input type="password" v-model="dataForm.password"></el-input>
-      </el-form-item>
-      <el-form-item label="新密码" prop="newPassword">
-        <el-input type="password" v-model="dataForm.newPassword"></el-input>
-      </el-form-item>
-      <el-form-item label="确认密码" prop="confirmPassword">
-        <el-input type="password" v-model="dataForm.confirmPassword"></el-input>
-      </el-form-item>
-    </el-form>
-    <span slot="footer" class="dialog-footer">
-      <el-button @click="visible = false">取消</el-button>
-      <el-button type="primary" @click="dataFormSubmit()">确定</el-button>
-    </span>
-  </el-dialog>
-</template>
-
-<script>
-  import { clearLoginInfo } from '@/utils'
-  import { Debounce } from '@/utils/debounce'
-  import { encrypt } from '@/utils/crypto'
-  export default {
-    data () {
-      var validateConfirmPassword = (rule, value, callback) => {
-        if (this.dataForm.newPassword !== value) {
-          callback(new Error('确认密码与新密码不一致'))
-        } else {
-          callback()
-        }
-      }
-      return {
-        visible: false,
-        dataForm: {
-          password: '',
-          newPassword: '',
-          confirmPassword: ''
-        },
-        dataRule: {
-          password: [
-            { required: true, message: '原密码不能为空', trigger: 'blur' }
-          ],
-          newPassword: [
-            { required: true, message: '新密码不能为空', trigger: 'blur' }
-          ],
-          confirmPassword: [
-            { required: true, message: '确认密码不能为空', trigger: 'blur' },
-            { validator: validateConfirmPassword, trigger: 'blur' }
-          ]
-        }
-      }
-    },
-    computed: {
-      userName: {
-        get () { return this.$store.state.user.name }
-      },
-      mainTabs: {
-        get () { return this.$store.state.common.mainTabs },
-        set (val) { this.$store.commit('common/updateMainTabs', val) }
-      }
-    },
-    methods: {
-      // 初始化
-      init () {
-        this.visible = true
-        this.$nextTick(() => {
-          this.$refs['dataForm'].resetFields()
-        })
-      },
-      // 表单提交
-      dataFormSubmit: Debounce(function () {
-        this.$refs['dataForm'].validate((valid) => {
-          if (valid) {
-            this.$http({
-              url: this.$http.adornUrl('/sys/user/password'),
-              method: 'post',
-              data: this.$http.adornData({
-                'password': encrypt(this.dataForm.password),
-                'newPassword': encrypt(this.dataForm.newPassword)
-              })
-            }).then(({data}) => {
-              this.$message({
-                message: '操作成功',
-                type: 'success',
-                duration: 1500,
-                onClose: () => {
-                  this.visible = false
-                  this.$nextTick(() => {
-                    this.mainTabs = []
-                    clearLoginInfo()
-                    this.$router.replace({ name: 'login' })
-                  })
-                }
-              })
-            })
-          }
-        })
-      })
-    }
-  }
-</script>
-

+ 9 - 16
src/views/main-navbar.vue

@@ -12,16 +12,12 @@
     </div>
     <div class="site-navbar__body clearfix">
       <el-menu class="site-navbar__menu" mode="horizontal">
-        <el-tooltip effect="dark" :content="sidebarFold ? '展开菜单' : '折叠菜单'" placement="bottom" :enterable="false">
-          <el-menu-item class="site-navbar__switch" index="0" @click="sidebarFold = !sidebarFold">
-            <icon-svg :class="'navbar-fold-icon ' + (sidebarFold ? 'folded' : '')" name="anglesleft"></icon-svg>
-          </el-menu-item>
-        </el-tooltip>
-        <el-tooltip effect="dark" content="返回上一页" placement="bottom" :enterable="false">
-          <el-menu-item class="site-navbar__switch" index="1" @click="$router.back()">
-            <icon-svg name="arrowleft"></icon-svg>
-          </el-menu-item>
-        </el-tooltip>
+        <el-menu-item class="site-navbar__float-center" index="0" @click="sidebarFold = !sidebarFold">
+          <icon-svg :class="'navbar-fold-icon ' + (sidebarFold ? 'folded' : '')" name="anglesleft"></icon-svg>
+        </el-menu-item>
+        <el-menu-item class="site-navbar__float-center header">
+          <el-page-header @back="$router.back()" :content="currentTitle" />
+        </el-menu-item>
       </el-menu>
       <el-menu class="site-navbar__menu site-navbar__menu--right" mode="horizontal">
         <el-menu-item class="site-navbar__avatar" index="3">
@@ -37,13 +33,10 @@
         </el-menu-item>
       </el-menu>
     </div>
-    <!-- 弹窗, 修改密码 -->
-    <update-password v-if="updatePassowrdVisible" ref="updatePassowrd"></update-password>
   </nav>
 </template>
 
 <script>
-import UpdatePassword from './main-navbar-update-password'
 import { clearLoginInfo } from '@/utils'
 export default {
   data() {
@@ -51,9 +44,6 @@ export default {
       updatePassowrdVisible: false
     }
   },
-  components: {
-    UpdatePassword
-  },
   computed: {
     navbarLayoutType: {
       get() { return this.$store.state.common.navbarLayoutType }
@@ -68,6 +58,9 @@ export default {
     },
     userName: {
       get() { return this.$store.state.user.name }
+    },
+    currentTitle() {
+      return this.$route.meta.title
     }
   },
   methods: {

+ 89 - 0
src/views/modules/liveroom/liveroomConsults.vue

@@ -0,0 +1,89 @@
+<template>
+  <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>
+      </el-form-item>
+    </el-form>
+    <avue-crud ref="crud" :page="page" :data="dataList" :option="tableOption" @on-load="getDataList" />
+  </div>
+</template>
+
+<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';
+
+export default Vue.extend({
+  data() {
+    return {
+      pickerOption,
+      tableOption,
+      daterange: [dayjs().startOf("month").toDate(), dayjs().toDate()],
+      dataListLoading: false,
+      page: {
+        total: 0,
+        currentPage: 1,
+        pageSize: 10,
+      },
+      dataList: [],
+      roomId: 0,
+    }
+  },
+
+  mounted() {
+    const id_str = this.$route.params.id
+    const id = parseInt(id_str)
+    if (isNaN(id) || !isInteger(id)) {
+      //@ts-ignore
+      this.$shortcut.notFound()
+    } else {
+      this.roomId = id
+      this.getDataList()
+    }
+  },
+
+  methods: {
+    getDataList(page?: IPage, query?: never, done?: Function) {
+      if (this.roomId === 0) {
+        return
+      }
+      const [start, end] = this.formatted_daterange
+
+      this.dataListLoading = true
+      httpx.post(httpx.makeurl('/rooms/queryConsultList'), {
+        start,
+        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.dataListLoading = false
+        if (done) {
+          done()
+        }
+      })
+    },
+    onSubmitQuery() { },
+    onResetQuery() { },
+  },
+
+  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')
+      return [formatted_start, formatted_end]
+    }
+  }
+})
+
+</script>

+ 1 - 2
src/views/modules/liveroom/liveroomList-info.vue

@@ -1,7 +1,6 @@
 <template>
   <div>
-    <el-dialog :title="item ? '直播间信息 #' + item.id : ''" :visible="!!item" class="avue-dialog avue-dialog--top" width="80%"
-      :before-close="handleClose">
+    <el-dialog :title="item ? '直播间信息 #' + item.id : ''" :visible="!!item" width="80%" :before-close="handleClose">
       <div v-if="!!item">
         <el-descriptions :column="3" border>
           <el-descriptions-item label="标题" span="2">{{ item.title }}</el-descriptions-item>

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

@@ -2,38 +2,41 @@
   <div class="icon-button">
     <el-button-group>
       <el-tooltip effect="dark" content="直播间内用户" placement="top" :enterable="false">
-        <el-button type="success" size="small" disabled><icon-svg name="user" /></el-button>
+        <el-button type="success" size="small" disabled>
+          <icon-svg name="user" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="管理回放" placement="top" :enterable="false">
-        <el-button type="warning" size="small" disabled><icon-svg name="playback" /></el-button>
+        <el-button type="warning" size="small" disabled>
+          <icon-svg name="playback" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="商品库" placement="top" :enterable="false">
-        <el-button type="primary" size="small"
-          @click="$router.push({ name: 'liveroomProd', params: { id: id.toString() } })">
+        <el-button type="primary" size="small" @click="navigateTo('liveroomProd')">
           <icon-svg name="product" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="客户咨询" placement="top" :enterable="false">
-        <el-button type="primary" size="small" disabled><icon-svg name="question" /></el-button>
+        <el-button type="primary" size="small" @click='navigateTo("liveroomConsults")'>
+          <icon-svg name="question" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="商品跳转记录" placement="top" :enterable="false">
-        <el-button type="info" size="small"
-          @click="$router.push(`/liveroom/liveroom-product-jump-record/${id}?title=${data.title}`)"><icon-svg
-            name="record" /></el-button>
+        <el-button type="info" size="small" @click='navigateTo("liveroomProductJumpRecord", { title: data.title })'>
+          <icon-svg name="record" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="邀约码统计" placement="top" :enterable="false">
-        <el-button type="info" size="small" disabled><icon-svg name="share" /></el-button>
+        <el-button type="info" size="small" disabled>
+          <icon-svg name="share" /></el-button>
       </el-tooltip>
 
       <el-tooltip effect="dark" content="查看" placement="top" :enterable="false">
-        <el-button type="success" size="small" @click="$emit('display-detail', { id, index })"><icon-svg
-            name="eye" /></el-button>
+        <el-button type="success" size="small" @click="$emit('display-detail', { id, index })">
+          <icon-svg name="eye" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="修改" placement="top" :enterable="false">
-        <el-button type="primary" size="small" @click="handleEdit"><icon-svg name="pen" /></el-button>
+        <el-button type="primary" size="small" @click="navigateTo('liveroomEdit')">
+          <icon-svg name="pen" /></el-button>
       </el-tooltip>
       <el-tooltip effect="dark" content="删除" placement="top" :enterable="false">
-        <el-button type="danger" size="small" @click="$emit('delete-room', { id, index })"><icon-svg
-            name="trashcan" /></el-button>
+        <el-button type="danger" size="small" @click="$emit('delete-room', { id, index })">
+          <icon-svg name="trashcan" /></el-button>
       </el-tooltip>
     </el-button-group>
   </div>
@@ -59,12 +62,9 @@ export default Vue.extend({
     }
   },
   methods: {
-    handleEdit() {
-      this.$router.push({
-        name: 'liveroomEdit',
-        params: { id: this.id.toString() }
-      })
+    navigateTo(name: string, query?: Record<string, string>) {
+      this.$router.push({ name, params: { id: this.id.toString() }, query: query })
     }
-  }
+  },
 })
 </script>

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

@@ -1,5 +1,5 @@
 <template>
-  <div class="liveroomprod-select">
+  <div class="liveroomprod-select avue-noheader">
     <el-dialog title="选择商品" :visible="true" width="85%" top="5vh" :before-close="onCancel" :destroy-on-close="true"
       :close-on-click-modal="true">
       <avue-crud ref="crud" :option="tableOption" :page="page" :data="dataList" :table-loading="dataListLoading"
@@ -91,23 +91,4 @@ export default Vue.extend({
     }
   }
 })
-</script>
-
-<style lang="scss">
-.liveroomprod-select {
-
-  .avue-crud__menu,
-  .avue-crud__header {
-    display: none !important;
-  }
-
-  .el-form {
-    height: 60vh;
-    overflow-y: auto;
-  }
-
-  .el-dialog__body {
-    padding: 10px 20px;
-  }
-}
-</style>
+</script>