6 커밋 97616977bc ... 652388b050

작성자 SHA1 메시지 날짜
  zhengjinyi 652388b050 商品详情用户统计 2 달 전
  xiebaomin 6b7a870928 Merge branch 'developer' of http://git.caimei365.com/caimei365/caimei365-manager-ui into developer 1 년 전
  xiebaomin b33821c094 内容库updated 1 년 전
  zhengjinyi d162d83893 Merge branch 'developer' of http://git.caimei365.com/caimei365/caimei365-manager-ui into developer 1 년 전
  zhengjinyi aa6cb4bda6 数据报表预览分享 1 년 전
  xiebaomin 7865e77e00 内容库updated 1 년 전

+ 14 - 0
src/api/user/record/record.js

@@ -7,6 +7,13 @@ export function fetchRecordList(params) {
     params
     params
   })
   })
 }
 }
+export function getRecordProductCount(params) {
+  return request({
+    url: '/user/behavior/record/product/count',
+    method: 'get',
+    params
+  })
+}
 export function fetchBehaviorList(params) {
 export function fetchBehaviorList(params) {
   return request({
   return request({
     url: '/user/behavior/record/recordList',
     url: '/user/behavior/record/recordList',
@@ -14,6 +21,13 @@ export function fetchBehaviorList(params) {
     params
     params
   })
   })
 }
 }
+export function fetchBehaviorProductDetail(params) {
+  return request({
+    url: '/user/behavior/record/count/detail',
+    method: 'get',
+    params
+  })
+}
 
 
 export function importExportToday(params) {
 export function importExportToday(params) {
   return request({
   return request({

+ 6 - 0
src/router/modules/user.js

@@ -16,6 +16,12 @@ const userRouter = {
       component: () => import('@/views/user/record/list'),
       component: () => import('@/views/user/record/list'),
       meta: { title: '用户行为记录', icon: 'international', activeMenu: '/user/list' }
       meta: { title: '用户行为记录', icon: 'international', activeMenu: '/user/list' }
     },
     },
+    {
+      path: 'record-product-list',
+      name: 'RecordProductList',
+      component: () => import('@/views/user/record/record-product-list'),
+      meta: { title: '商品浏览统计', icon: 'international', activeMenu: '/user/list' }
+    },
     {
     {
       path: 'detail-list',
       path: 'detail-list',
       hidden: true,
       hidden: true,

+ 1 - 1
src/views/dataBase/store-list/index.vue

@@ -59,7 +59,7 @@
       <el-table-column prop="productName" label="商品名称" align="center" />
       <el-table-column prop="productName" label="商品名称" align="center" />
       <el-table-column prop="productImage" label="商品图片" align="center" width="150">
       <el-table-column prop="productImage" label="商品图片" align="center" width="150">
         <template slot-scope="scope">
         <template slot-scope="scope">
-          <el-image :src="scope.row.productImage" />
+          <el-image :src="scope.row.productImage || 'https://www.caimei365.com/img/base/placeholder.png'" />
         </template>
         </template>
       </el-table-column>
       </el-table-column>
       <el-table-column prop="productType" label="商品属性" align="center">
       <el-table-column prop="productType" label="商品属性" align="center">

+ 114 - 0
src/views/user/customer/components/share-dialog.vue

@@ -0,0 +1,114 @@
+<template>
+  <el-dialog title="预览" :visible.sync="visible" width="550px" :close-on-click-modal="false" :show-close="false">
+    <p style="line-height: 24px; margin-bottom: 10px">
+      预览码:
+      <span style="color: red">{{ shareCode }}(24小时内有效)</span>
+    </p>
+    <p style="line-height: 24px; margin-bottom: 10px">
+      预览链接:
+      <span style="color: #999999">{{ shareLink }}</span>
+    </p>
+    <p style="line-height: 24px; margin-bottom: 10px">
+      <el-tag type="info">查看预览链接需要输入预览码,预览码24小时内有效,失效后需要重新进行预览操作获取。</el-tag>
+    </p>
+    <div slot="footer">
+      <el-button type="primary" @click="handleConfirm"> 直接预览 </el-button>
+      <el-button type="primary" @click="handleCopy"> 全部复制 </el-button>
+      <el-button @click="handleCanle"> 取 消 </el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { getCustomerShopList } from '@/api/user/customer/customer'
+
+export default {
+  name: 'ShareDialog',
+  filters: {
+    NumFormat(value) {
+      // 处理金额
+      return Number(value).toFixed(2)
+    }
+  },
+  props: {
+    shareInfo: {
+      type: Object,
+      default: null
+    },
+    shareCode: {
+      type: Number,
+      default: 2345
+    },
+    shareLink: {
+      type: String,
+      default: ''
+    },
+    productName: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      visible: true
+    }
+  },
+  computed: {},
+  created() {},
+  methods: {
+    // 获取所有供应商列表
+    async getCustomerShopList() {
+      try {
+        const res = await getCustomerShopList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 全部复制链接
+    handleCopy() {
+      const truncatedName = this.truncateStringWithEllipsis(this.productName, 18)
+      const clipboardText = `商品${truncatedName}\n【${this.shareInfo.reportDate}】数据报表\n预览链接:${this.shareLink}\n预览码:${this.shareCode}(24小时内有效)`
+      navigator.clipboard
+        .writeText(clipboardText)
+        .then(() => {
+          this.$notify({
+            title: '复制成功!',
+            message: '',
+            type: 'success'
+          })
+        })
+        .catch((err) => {
+          console.error('复制失败:', err)
+        })
+    },
+
+    // 直接预览
+    handleConfirm() {
+      window.open(this.shareLink, '_blank')
+    },
+    handleCanle() {
+      // 取消弹窗
+      this.$emit('cancel')
+    },
+    truncateStringWithEllipsis(str, maxLength) {
+      if (str.length <= maxLength) {
+        return str // 如果字符串长度小于或等于最大长度,直接返回原字符串
+      } else {
+        const startIndex = maxLength - 3 // 从第10个字符开始
+        const endIndex = str.length - 3 // 保留最后3个字符
+        return `${str.slice(0, startIndex)}...${str.slice(endIndex)}` // 在中间插入省略号并返回截取后的字符串
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+::v-deep {
+  thead .el-checkbox {
+    display: none !important;
+  }
+}
+</style>

+ 1 - 1
src/views/user/customer/list.vue

@@ -95,7 +95,7 @@
 import { getCustomerShopList, saveCustomerShop, renewCustomerShop } from '@/api/user/customer/customer'
 import { getCustomerShopList, saveCustomerShop, renewCustomerShop } from '@/api/user/customer/customer'
 import ShopDialog from './components/shop-dialog'
 import ShopDialog from './components/shop-dialog'
 export default {
 export default {
-  name: 'RecordList',
+  name: 'CustomerList',
   components: { ShopDialog },
   components: { ShopDialog },
   filters: {},
   filters: {},
   data() {
   data() {

+ 26 - 3
src/views/user/customer/market-report-list.vue

@@ -63,14 +63,26 @@
       :limit.sync="listQuery.pageSize"
       :limit.sync="listQuery.pageSize"
       @pagination="getMarketReportList"
       @pagination="getMarketReportList"
     />
     />
+    <!--  预览弹窗 -->
+    <share-dialog
+      v-if="dialogVisible"
+      ref="shopDialog"
+      :product-name="productName"
+      :share-code="shareCode"
+      :share-link="shareLink"
+      :share-info="shareInfo"
+      @cancel="handleCancel"
+    />
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
 import { getMarketReportList, updateVisible, deleteMarketReport } from '@/api/user/customer/customer'
 import { getMarketReportList, updateVisible, deleteMarketReport } from '@/api/user/customer/customer'
+import ShareDialog from './components/share-dialog'
 import { downloadWithUrl } from '@/utils'
 import { downloadWithUrl } from '@/utils'
 export default {
 export default {
   name: 'CustomerStatList',
   name: 'CustomerStatList',
+  components: { ShareDialog },
   filters: {},
   filters: {},
   data() {
   data() {
     return {
     return {
@@ -84,7 +96,11 @@ export default {
         pageSize: 100
         pageSize: 100
       },
       },
       list: [],
       list: [],
-      total: 0
+      total: 0,
+      shareCode: 0,
+      shareLink: '',
+      shareInfo: {},
+      dialogVisible: false
     }
     }
   },
   },
   computed: {},
   computed: {},
@@ -154,8 +170,15 @@ export default {
     },
     },
     // 跳转预览
     // 跳转预览
     handlePreview(row) {
     handlePreview(row) {
-      const urls = `${process.env.VUE_APP_CAIMEI_URL}/supplier/charts.html?type=1&shopId=${row.shopId}&marketReportId=${row.id}`
-      window.open(urls, '_blank')
+      const urls = `${process.env.VUE_APP_CAIMEI_URL}/charts-preview.html?type=1&shopId=${row.shopId}&marketReportId=${row.id}`
+      this.shareCode = 4567
+      this.shareLink = urls
+      this.shareInfo = row
+      this.dialogVisible = true
+    },
+    // 取消预览
+    handleCancel() {
+      this.dialogVisible = false
     },
     },
     // 添加/编辑
     // 添加/编辑
     handleUpdate(type, row) {
     handleUpdate(type, row) {

+ 1 - 1
src/views/user/record/components/detail-dialog.vue

@@ -119,7 +119,7 @@ export default {
       listQuery: {
       listQuery: {
         ip: '',
         ip: '',
         accessDate: '',
         accessDate: '',
-        userId: 0,
+        userId: '',
         pageNum: 1,
         pageNum: 1,
         pageSize: 100
         pageSize: 100
       },
       },

+ 137 - 0
src/views/user/record/components/product-detail-dialog.vue

@@ -0,0 +1,137 @@
+<template>
+  <el-dialog
+    title="商品用户详情"
+    :visible.sync="visible"
+    width="1300px"
+    :close-on-click-modal="false"
+    :show-close="false"
+  >
+    <!-- 列表 -->
+    <el-table v-loading="isLoading" :data="list" border height="500px">
+      <el-table-column prop="region" label="地区" align="center">
+        <template slot-scope="{ row }">
+          {{ row.region ? row.region : "---" }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="accessClient" label="访问客户端" align="center" />
+      <el-table-column prop="companyType" label="公司类型" align="center" />
+      <el-table-column prop="corporateName" label="机构名称" align="center">
+        <template slot-scope="{ row }">
+          {{ row.corporateName ? row.corporateName : "---" }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="contacts" label="联系人" align="center">
+        <template slot-scope="{ row }">
+          {{ row.contacts ? row.contacts : "---" }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="phoneNumber" label="手机号" align="center">
+        <template slot-scope="{ row }">
+          {{ row.phoneNumber ? row.phoneNumber : "---" }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="spName" label="所属协销" align="center" width="100">
+        <template slot-scope="{ row }">
+          {{ row.spName ? row.spName : "---" }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="lastAccessTime" label="最后访问时间" align="center" width="150" />
+      <el-table-column prop="accessDuration" label="访问时长" align="center" width="150" />
+    </el-table>
+    <!-- 页码 -->
+    <pagination
+      :total="total"
+      :page-sizes="[10]"
+      :page-size="10"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="fetchBehaviorProductDetail"
+    />
+    <div slot="footer">
+      <el-button type="primary" @click="handleCanle"> 关闭 </el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { fetchBehaviorProductDetail } from '@/api/user/record/record'
+
+export default {
+  name: 'ProductDetailDialog',
+  filters: { },
+  props: {
+    detailQuery: {
+      type: Object,
+      // eslint-disable-next-line vue/require-valid-default-prop
+      default: {}
+    }
+  },
+  data() {
+    return {
+      visible: true,
+      isLoading: true,
+      listQuery: {
+        productId: '', // 商品ID
+        pageNum: 1,
+        pageSize: 10
+      },
+      list: [],
+      total: 0
+    }
+  },
+  computed: {
+
+  },
+  created() {
+    this.listQuery = { ...this.listQuery, ...this.detailQuery }
+    console.log('listQuery', this.listQuery)
+    this.getList()
+  },
+  methods: {
+    // 获取详情列表
+    getList() {
+      this.listQuery.pageNum = 1
+      this.fetchBehaviorProductDetail()
+    },
+    // 获取商品用户详情
+    async fetchBehaviorProductDetail() {
+      try {
+        this.isLoading = true
+        const res = await fetchBehaviorProductDetail(this.listQuery)
+        const data = res.data
+        this.list = data.results
+        this.total = data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    handleRelevanceShop(value) {
+      console.log('va', value.split(','))
+      return value.split(',')
+    },
+    handleCanle() {
+      // 取消弹窗
+      this.$emit('cancel')
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.tags-sms {
+  width: 100%;
+  height: auto;
+  font-size: 14px;
+  color: #666666;
+  line-height: 40px;
+  border-radius: 4px;
+  box-sizing: border-box;
+  padding: 5px;
+  border: 1px solid #e1e1e1;
+}
+.tags-sms .tags-sms-span {
+  font-size: 18px;
+  font-weight: bold;
+  color: #333333;
+}
+</style>

+ 2 - 1
src/views/user/record/detail-list.vue

@@ -93,7 +93,8 @@ export default {
         4: '神马搜索',
         4: '神马搜索',
         5: '头条搜索',
         5: '头条搜索',
         6: '搜狗搜索',
         6: '搜狗搜索',
-        7: '直接访问'
+        7: '直接访问',
+        8: '内容库访问'
       }
       }
       return map[value]
       return map[value]
     }
     }

+ 6 - 6
src/views/user/record/list.vue

@@ -115,7 +115,7 @@
             />
             />
           </el-select>
           </el-select>
         </div>
         </div>
-        <div class="filter-control">
+        <!--<div class="filter-control">
           <span>页面类型:</span>
           <span>页面类型:</span>
           <el-select v-model="listQuery.pageTypes" filterable @change="getList">
           <el-select v-model="listQuery.pageTypes" filterable @change="getList">
             <el-option value="" label="请选择" />
             <el-option value="" label="请选择" />
@@ -123,10 +123,10 @@
               v-for="item in typesOptions"
               v-for="item in typesOptions"
               :key="item.id"
               :key="item.id"
               :label="item.pageType"
               :label="item.pageType"
-              :value="item.pageType"
+              :value="item.id"
             />
             />
           </el-select>
           </el-select>
-        </div>
+        </div>-->
         <div v-if="tabsCurrent === 1" class="filter-control">
         <div v-if="tabsCurrent === 1" class="filter-control">
           <span>访问日期:</span>
           <span>访问日期:</span>
           <el-date-picker
           <el-date-picker
@@ -160,16 +160,16 @@
           <el-select v-model="listQuery.headUserId" placeholder="协销" @change="getList">
           <el-select v-model="listQuery.headUserId" placeholder="协销" @change="getList">
             <el-option
             <el-option
               v-for="item in sellerList"
               v-for="item in sellerList"
-              :key="item.serviceProviderId"
+              :key="item.userId"
               :label="item.linkMan"
               :label="item.linkMan"
-              :value="item.serviceProviderId"
+              :value="item.userId"
             />
             />
           </el-select>
           </el-select>
         </div>
         </div>
         <div class="filter-control">
         <div class="filter-control">
           <span>内容库ID:</span>
           <span>内容库ID:</span>
           <el-input
           <el-input
-            v-model="listQuery.spName"
+            v-model="listQuery.productArchiveId"
             placeholder="内容库"
             placeholder="内容库"
             clearable
             clearable
             style="width: 120px"
             style="width: 120px"

+ 145 - 0
src/views/user/record/record-product-list.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>商品ID:</span>
+        <el-input
+          v-model="listQuery.productId"
+          placeholder="商品ID"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>统计时间:</span>
+        <el-date-picker
+          v-model="time"
+          type="daterange"
+          unlink-panels
+          value-format="yyyy-MM-dd"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :picker-options="pickerOptions"
+          @change="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList"> 查询 </el-button>
+      </div>
+    </div>
+    <!-- 列表 -->
+    <el-table v-loading="isLoading" :data="list" border style="width: 100%">
+      <el-table-column prop="productId" label="商品ID" align="center" width="200" />
+      <el-table-column prop="productImage" label="商品图片" align="center">
+        <template slot-scope="{ row }">
+          <el-popover v-if="row.productImage" placement="top-start" title="" width="100" trigger="hover">
+            <img :src="row.productImage" alt="" style="width: 80px; height: 80px" />
+            <img slot="reference" :src="row.productImage" alt="" style="width: 80px; height: 80px" />
+          </el-popover>
+          <span v-else>---</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="productName" label="商品名称" align="center" />
+      <el-table-column prop="productCount" label="商城总浏览次数" align="center">
+        <template slot-scope="{ row }">
+          <el-link type="primary" :underline="false">【{{ row.productCount }}】次</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column fixed="right" label="操作" align="center" width="200">
+        <template slot-scope="{ row }">
+          <el-button type="primary" size="mini" style="margin: 2px" @click="handleOperate(row)"> 查看用户详情 </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 页码 -->
+    <pagination
+      :total="total"
+      :page-sizes="[10, 20, 30, 100]"
+      :page-size="20"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="getRecordProductCount"
+    />
+    <!--  详情弹窗 -->
+    <product-detail-dialog
+      v-if="dialogVisible"
+      ref="detailDialog"
+      :detail-query="detailQuery"
+      @cancel="handleCancel"
+    />
+  </div>
+</template>
+
+<script>
+import pickerOptions from '@/utils/time-picker.js'
+import { getRecordProductCount } from '@/api/user/record/record'
+import ProductDetailDialog from './components/product-detail-dialog'
+const defaultListQuery = {
+  productId: '', // 商品ID
+  beginTime: '', // 开始日期
+  endTime: '', // 结束日期
+  pageNum: 1,
+  pageSize: 20
+}
+export default {
+  name: 'RecordProductList',
+  components: {
+    ProductDetailDialog
+  },
+  filters: {},
+  data() {
+    return {
+      isLoading: true,
+      listQuery: Object.assign({}, defaultListQuery),
+      pickerOptions,
+      time: [],
+      list: [],
+      total: 0,
+      dialogVisible: false,
+      detailQuery: {}
+    }
+  },
+  computed: {},
+  created() {
+    this.getList()
+  },
+  mounted() {},
+  methods: {
+    // 获取列表
+    getList() {
+      this.listQuery.pageNum = 1
+      this.listQuery.beginTime = this.time[0]
+      this.listQuery.endTime = this.time[1]
+      this.getRecordProductCount()
+    },
+
+    // 获取商品列表
+    async getRecordProductCount() {
+      try {
+        this.isLoading = true
+        const res = await getRecordProductCount(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 查看详情
+    handleOperate(row) {
+      this.detailQuery = { productId: row.productId }
+      this.dialogVisible = true
+    },
+    // 关闭详情弹窗
+    handleCancel() {
+      this.dialogVisible = false
+    }
+  }
+}
+</script>
+
+<style></style>