Ver Fonte

Merge remote-tracking branch 'origin/developerA' into developer

yuwenjun1997 há 2 anos atrás
pai
commit
82d4e5e044

+ 2 - 1
.env.development

@@ -4,7 +4,8 @@ ENV = 'development'
 # base api
 # VUE_APP_BASE_API = 'http://192.168.2.67:18015'
 # VUE_APP_BASE_API = 'http://localhost:18015'
-VUE_APP_BASE_API = 'https://mapi-b.caimei365.com'
+VUE_APP_BASE_API = 'http://192.168.2.102:18015'
+# VUE_APP_BASE_API = 'https://mapi-b.caimei365.com'
 # 图片上传,索引更新等
 VUE_APP_CORE_API = 'https://core-b.caimei365.com'
 

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

@@ -0,0 +1,24 @@
+import request from '@/utils/request'
+
+export function fetchRecordList(params) {
+  return request({
+    url: '/user/behavior/record/list',
+    method: 'get',
+    params
+  })
+}
+export function fetchBehaviorList(params) {
+  return request({
+    url: '/user/behavior/record/recordList',
+    method: 'get',
+    params
+  })
+}
+
+export function importExportToday(params) {
+  return request({
+    url: '/user/behavior/exportToday',
+    method: 'get',
+    params
+  })
+}

+ 72 - 0
src/api/user/supplier/supplier.js

@@ -0,0 +1,72 @@
+import request from '@/utils/request'
+
+export function fetchShopRelevanceList(params) {
+  return request({
+    url: '/user/shopLabel/shopRelevanceList',
+    method: 'get',
+    params
+  })
+}
+export function fetchShopLabelShopList(params) {
+  return request({
+    url: '/user/shopLabel/shopList',
+    method: 'get',
+    params
+  })
+}
+export function importShopRelevance(params) {
+  return request({
+    url: '/user/shopLabel/insertShopRelevance',
+    method: 'get',
+    params
+  })
+}
+export function fetchDelShopById(params) {
+  return request({
+    url: '/user/shopLabel/delShopById',
+    method: 'get',
+    params
+  })
+}
+export function fetchShopLabelList(params) {
+  return request({
+    url: '/user/shopLabel/shopLabelList',
+    method: 'get',
+    params
+  })
+}
+export function fetchLabelList(params) {
+  return request({
+    url: '/user/shopLabel/labelList',
+    method: 'get',
+    params
+  })
+}
+export function relevanceInsertLabel(params) {
+  return request({
+    url: '/user/shopLabel/insertLabel',
+    method: 'get',
+    params
+  })
+}
+export function delShopLabelList(params) {
+  return request({
+    url: '/user/shopLabel/delShopLabelList',
+    method: 'get',
+    params
+  })
+}
+export function addInsertSearch(params) {
+  return request({
+    url: '/user/shopLabel/insertSearch',
+    method: 'get',
+    params
+  })
+}
+export function searchBehaviorList(params) {
+  return request({
+    url: '/user/behavior/searchList',
+    method: 'get',
+    params
+  })
+}

+ 2 - 0
src/router/index.js

@@ -8,6 +8,7 @@ import Layout from '@/layout'
 
 /* Router Modules */
 import sysRouter from './modules/sys'
+import userRouter from './modules/user'
 import wechatRouter from './modules/wechat'
 import webPageRouter from './modules/webPage'
 import productRouter from './modules/product'
@@ -94,6 +95,7 @@ export const constantRoutes = [
  */
 export const asyncRoutes = [
   sysRouter,
+  userRouter,
   webPageRouter,
   productRouter,
   financeRouter,

+ 23 - 0
src/router/modules/keywordLibrary.js

@@ -46,6 +46,29 @@ const keywordLibraryRouter = {
           meta: { title: '标签列表' }
         }
       ]
+    },
+    {
+      path: 'supplierTag',
+      name: 'SupplierTagMenu',
+      redirect: '/library/supplierTag/list',
+      alwaysShow: true,
+      component: () => import('@/views/index'),
+      meta: { title: '供应商关联标签' },
+      children: [
+        {
+          path: 'list',
+          name: 'SupplierList',
+          component: () => import('@/views/library/supplierTag/supplier-list'),
+          meta: { title: '供应商列表' }
+        },
+        {
+          path: 'tagList',
+          hidden: true,
+          component: () => import('@/views/library/supplierTag/list'),
+          name: 'SupplierTagList',
+          meta: { title: '标签列表', noCache: true, activeMenu: '/library/supplierTag/' }
+        }
+      ]
     }
   ]
 }

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

@@ -0,0 +1,70 @@
+/** When your routing table is too long, you can split it into small modules **/
+
+import Layout from '@/layout'
+
+const userRouter = {
+  path: '/user',
+  component: Layout,
+  redirect: '/user/club',
+  alwaysShow: true, // will always show the root menu
+  name: 'userSetting', // name必须和后台配置一致,不然匹配不到
+  meta: { title: '用户管理' },
+  children: [
+    {
+      path: 'club',
+      name: 'UserClubMenu',
+      redirect: '/user/club/list',
+      alwaysShow: true,
+      component: () => import('@/views/index'),
+      meta: { title: '机构管理' },
+      children: [
+        {
+          path: 'list',
+          name: 'ClubList',
+          component: () => import('@/views/user/club/list'),
+          meta: { title: '机构列表' }
+        }
+      ]
+    },
+    {
+      path: 'record',
+      name: 'UserRecordMenu',
+      redirect: '/user/record/list',
+      alwaysShow: true,
+      component: () => import('@/views/index'),
+      meta: { title: '用户行为记录' },
+      children: [
+        {
+          path: 'list',
+          name: 'RecordList',
+          component: () => import('@/views/user/record/list'),
+          meta: { title: '用户行为记录' }
+        },
+        {
+          path: 'detail',
+          hidden: true,
+          component: () => import('@/views/user/record/detail-list.vue'),
+          name: 'RecordDtails',
+          meta: { title: '查看详情', noCache: true, activeMenu: '/user/record/' }
+        }
+      ]
+    },
+    {
+      path: 'supplier',
+      name: 'UserSupplierMenu',
+      redirect: '/user/supplier/list',
+      alwaysShow: true,
+      component: () => import('@/views/index'),
+      meta: { title: '供应商管理', useDefault: true },
+      children: [
+        {
+          path: 'list',
+          name: 'SupplierAllList',
+          component: () => import('@/views/user/supplier/list'),
+          meta: { title: '供应商列表' }
+        }
+      ]
+    }
+  ]
+}
+export default userRouter

+ 4 - 2
src/utils/index.js

@@ -245,7 +245,8 @@ export function getTime(type) {
 export function debounce(func, wait, immediate) {
   let timeout, args, context, timestamp, result
 
-  const later = function() {
+  // eslint-disable-next-line space-before-function-paren
+  const later = function () {
     // 据上一次触发时间间隔
     const last = +new Date() - timestamp
 
@@ -262,7 +263,8 @@ export function debounce(func, wait, immediate) {
     }
   }
 
-  return function(...args) {
+  // eslint-disable-next-line space-before-function-paren
+  return function (...args) {
     context = this
     timestamp = +new Date()
     const callNow = immediate && !timeout

+ 310 - 0
src/views/library/supplierTag/list.vue

@@ -0,0 +1,310 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>标签:</span>
+        <el-input
+          v-model="listQuery.keyword"
+          placeholder="关键词"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+        <el-button v-permission="'tag:list:add'" type="primary" @click="handleSelectionTagDialog">选择标签</el-button>
+        <el-button v-permission="'tag:list:del'" type="danger" :disabled="disabled" @click="handleDeleteTag">
+          删除
+        </el-button>
+      </div>
+    </div>
+    <!-- 关键词列表 -->
+    <el-table v-loading="isLoading" :data="list" border @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="序号" :index="indexMethod" type="index" sortable="custom" align="center" width="80" />
+      <el-table-column prop="keyword" label="关键词" align="center" />
+      <el-table-column prop="frequency" label="搜索次数" width="120" align="center" sortable />
+      <el-table-column width="180" label="来源" align="center">
+        <template slot-scope="{ row }">
+          <span v-if="row.fromSearch === 1">单个添加</span>
+          <span v-else-if="row.fromSearch === 2">系统推荐</span>
+          <span v-else>导入</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="最近搜索时间" align="center" width="200">
+        <template slot-scope="{ row }">
+          <span v-if="row.searchTime">{{ row.searchTime | parseTime }}</span>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="添加时间" align="center" width="200">
+        <template slot-scope="{ row }">
+          <span v-if="row.pickTime">{{ row.pickTime | parseTime }}</span>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="240">
+        <template slot-scope="{ row }">
+          <el-button v-permission="'tag:list:del'" type="danger" size="mini" @click="handleDeleteTag(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 页码 -->
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="fetchShopLabelList"
+    />
+
+    <!-- 选择标签 -->
+    <el-dialog
+      title="选择标签"
+      :visible.sync="selectionTagDialog"
+      width="1000px"
+      :close-on-click-modal="false"
+      :show-close="false"
+    >
+      <div class="filter-container">
+        <div class="filter-control">
+          <span>标签:</span>
+          <el-input
+            v-model="labelQuery.keyword"
+            placeholder="标签"
+            clearable
+            @keyup.enter.native="fetchLabelList"
+            @clear="fetchLabelList"
+          />
+        </div>
+        <div class="filter-control">
+          <el-button type="primary" @click="fetchLabelList">查询</el-button>
+          <el-button type="primary" @click="handleAddTag">新增标签</el-button>
+        </div>
+      </div>
+      <el-table ref="table" v-loading="isLoading" :data="labelList" height="340px" border @selection-change="handleSelectionTagChange">
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="keyword" label="标签" align="center" />
+        <el-table-column prop="frequency" label="搜索次数" width="120" align="center" sortable />
+        <el-table-column label="最近搜索时间" align="center" width="200">
+          <template slot-scope="{ row }">
+            <span v-if="row.searchTime">{{ row.searchTime | parseTime }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="添加时间" align="center" width="200">
+          <template slot-scope="{ row }">
+            <span v-if="row.addTime">{{ row.addTime | parseTime }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div slot="footer">
+        <el-button @click="selectionTagDialog = false">取 消</el-button>
+        <el-button type="primary" :disabled="labelDisabled" @click="handleSelectionTagConfirm">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 添加标签 -->
+    <el-dialog
+      title="添加标签"
+      :visible.sync="addTagDialog"
+      width="460px"
+      :close-on-click-modal="false"
+      :show-close="false"
+    >
+      <el-form ref="tagForm" :model="tagForm" :rules="tagFormRules">
+        <el-form-item label="标签名:" prop="keyword">
+          <el-input v-model="tagForm.keyword" maxlength="20" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="closeAddTagDialog">取 消</el-button>
+        <el-button type="primary" @click="handleAddTagConfirm">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { fetchShopLabelList, fetchLabelList, relevanceInsertLabel, delShopLabelList, addInsertSearch } from '@/api/user/supplier/supplier'
+export default {
+  data() {
+    return {
+      isLoading: true,
+      relevanceId: null,
+      listQuery: {
+        relevanceId: '',
+        keyword: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      list: [],
+      total: 0,
+      currentList: [],
+      searchDialog: false,
+      addTagDialog: false,
+      selectionTagDialog: false,
+      labelQuery: {
+        shopId: '', // 供应商Id
+        keyword: '', // 标签名
+        pageNum: 1, // 页码
+        pageSize: 100// 页面数据数
+      },
+      tagForm: {
+        keyword: ''
+      },
+      tagFormRules: {
+        keyword: [{ required: true, message: '请输入标签名称', trigger: ['blur'] }]
+      },
+      exportKeywordList: [],
+      importDialog: false,
+      fileList: [],
+      importForm: {
+        fileUrl: ''
+      },
+      importFormRules: {
+        fileUrl: [{ required: true, message: '文件不能为空', trigger: ['change'] }]
+      },
+      labelList: [],
+      labelCurrentList: []
+    }
+  },
+  computed: {
+    disabled() {
+      return this.currentList.length === 0
+    },
+    labelDisabled() {
+      return this.labelCurrentList.length === 0
+    }
+  },
+  created() {
+    this.relevanceId = this.$route.query.relevanceId
+    this.labelQuery.shopId = this.$route.query.shopId
+    this.listQuery = { ...this.listQuery, ...this.$route.query }
+    console.log('listQuery', this.listQuery)
+    this.getList()
+  },
+  methods: {
+    // 获取关键词列表
+    getList() {
+      this.listQuery.pageNum = 1
+      this.fetchShopLabelList()
+    },
+
+    // 获取供应商标签列表
+    async fetchShopLabelList() {
+      try {
+        this.isLoading = true
+        const res = await fetchShopLabelList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 显示选择标签列表
+    handleSelectionTagDialog() {
+      this.selectionTagDialog = true
+      this.fetchLabelList()
+    },
+
+    // 获取标签列表
+    async fetchLabelList() {
+      try {
+        const res = await fetchLabelList(this.labelQuery)
+        this.labelList = res.data.results
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 选中标签项
+    handleSelectionTagChange(current) {
+      this.labelCurrentList = current
+    },
+
+    // 选中列表项
+    handleSelectionChange(current) {
+      this.currentList = current
+    },
+
+    // 添加标签
+    handleAddTag() {
+      this.selectionTagDialog = false
+      this.addTagDialog = true
+    },
+
+    // 添加标签取消
+    closeAddTagDialog() {
+      this.$refs.tagForm.resetFields()
+      this.addTagDialog = false
+    },
+
+    // 选择标签确定
+    async handleSelectionTagConfirm() {
+      const ids = this.labelCurrentList.map((item) => item.id).join(',')
+      try {
+        await relevanceInsertLabel({ relevanceId: this.relevanceId, keywordIds: ids })
+        this.$message.success('添加成功')
+        this.selectionTagDialog = false
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 添加标签确定
+    async handleAddTagConfirm() {
+      try {
+        await this.$refs.tagForm.validate()
+        this.addTagSubmit()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 提交添加标签信息
+    async addTagSubmit() {
+      try {
+        await addInsertSearch(this.tagForm)
+        this.$message.success('添加标签成功')
+        this.closeAddTagDialog()
+        this.handleSelectionTagDialog()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 删除标签
+    async handleDeleteTag(row) {
+      try {
+        await this.$confirm('确定删除选中的标签吗?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        this.deleteTagSubmit(row)
+      } catch (error) {
+        this.$message.info('已取消删除操作')
+      }
+    },
+
+    // 提交删除关联标签操作
+    async deleteTagSubmit(row) {
+      const keywords = row instanceof Event ? this.currentList : [].concat(row)
+      const ids = keywords.map((item) => item.id).join(',')
+      try {
+        await delShopLabelList({ labelIds: ids })
+        this.$message.success('操作成功')
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
+    }
+  }
+}
+</script>
+
+<style></style>

+ 269 - 0
src/views/library/supplierTag/supplier-list.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <div class="filter-container">
+      <div class="filter-control">
+        <span>供应商名称:</span>
+        <el-input
+          v-model="listQuery.shopName"
+          placeholder="供应商名称"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>手机号:</span>
+        <el-input
+          v-model="listQuery.contractMobile"
+          placeholder="手机号"
+          clearable
+          style="width:160px;"
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <span>联系人:</span>
+        <el-input
+          v-model="listQuery.linkMan"
+          placeholder="联系人"
+          clearable
+          style="width:160px;"
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
+      </div>
+      <div class="filter-control">
+        <el-button type="primary" @click="getList">查询</el-button>
+        <el-button v-permission="'tag:list:add'" type="primary" @click="handleAddSupplier">添加供应商</el-button>
+      </div>
+    </div>
+    <!-- 关键词列表 -->
+    <el-table v-loading="isLoading" :data="list" border>
+      <el-table-column label="序号" type="index" align="center" width="100" />
+      <el-table-column prop="shopName" label="供应商名称" align="center" width="300" />
+      <el-table-column prop="linkMan" label="联系人" align="center" width="300" />
+      <el-table-column prop="contractMobile" label="手机号" align="center" width="300">
+        <template slot-scope="{ row }">
+          {{ row.contractMobile ? row.contractMobile : '---' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="addTime" label="添加时间" align="center" width="300" />
+      <el-table-column label="操作" align="center">
+        <template slot-scope="{ row }">
+          <el-button type="primary" size="mini" @click="handleTagsList(row)">标签列表</el-button>
+          <el-button v-permission="'tag:list:del'" type="danger" size="mini" @click="handleDeleteSupper(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 页码 -->
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="fetchShopRelevanceList"
+    />
+    <!-- 添加供应商 -->
+    <el-dialog
+      title="添加供应商"
+      :visible.sync="addSupplierDialog"
+      width="1000px"
+      :close-on-click-modal="false"
+      :show-close="false"
+    >
+      <div class="filter-container">
+        <div class="filter-control">
+          <span>供应商ID:</span>
+          <el-input
+            v-model="shopListQuery.shopId"
+            placeholder="供应商ID"
+            clearable
+            @keyup.enter.native="fetchShopLabelShopList"
+            @clear="fetchShopLabelShopList"
+          />
+        </div>
+        <div class="filter-control">
+          <span>供应商名称:</span>
+          <el-input
+            v-model="shopListQuery.name"
+            placeholder="供应商名称"
+            clearable
+            style="width:160px;"
+            @keyup.enter.native="fetchShopLabelShopList"
+            @clear="fetchShopLabelShopList"
+          />
+        </div>
+        <div class="filter-control">
+          <el-button type="primary" @click="fetchShopLabelShopList">查询</el-button>
+        </div>
+      </div>
+      <el-table ref="table" v-loading="isLoading" :data="supplierList" height="350px" border @select="handleSelect">
+        <el-table-column type="selection" width="55" />
+        <el-table-column label="供应商ID" prop="shopId" align="center" width="100" />
+        <el-table-column label="供应商名称" prop="name" align="center" />
+        <el-table-column label="联系人" prop="linkMan" align="center" />
+        <el-table-column label="手机号" prop="contractMobile" align="center">
+          <template slot-scope="{ row }">
+            {{ row.contractMobile ? row.contractMobile : '---' }}
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 页码 -->
+      <pagination
+        :total="shopListTotal"
+        :page-sizes="[100, 200]"
+        :page-size="100"
+        :page.sync="shopListQuery.pageNum"
+        :limit.sync="shopListQuery.pageSize"
+        @pagination="fetchShopLabelShopList"
+      />
+      <div slot="footer">
+        <el-button @click="addSupplierDialog = false">取 消</el-button>
+        <el-button type="primary" :disabled="disabled" @click="handleAddTagConfirm(supplierRadio)">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { fetchShopRelevanceList, fetchShopLabelShopList, importShopRelevance, fetchDelShopById } from '@/api/user/supplier/supplier'
+
+export default {
+  data() {
+    return {
+      isLoading: true,
+      addSupplierDialog: false,
+      supplierList: [],
+      listQuery: {
+        shopName: '',
+        linkMan: '',
+        contractMobile: '',
+        pageNum: 1,
+        pageSize: 100
+      },
+      list: [],
+      total: 0,
+      shopListTotal: 0,
+      shopListQuery: {
+        pageNum: 1, // 页码
+        pageSize: 200, // 页面数据数
+        shopId: '', // 供应商ID
+        name: ''// 供应商公司名称
+      },
+      supplierRadio: null,
+      isAddSupplier: false
+    }
+  },
+  computed: {
+    disabled() {
+      return this.supplierRadio === null
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 选择供应商
+    handleSelect(selection, row) {
+      this.$refs.table.clearSelection()
+      this.$refs.table.toggleRowSelection(row)
+      this.supplierRadio = row.shopId
+      console.log('supplierRadio', this.supplierRadio)
+    },
+    // 获取关键词列表
+    getList() {
+      this.listQuery.pageNum = 1
+      this.fetchShopRelevanceList()
+    },
+
+    // 获取供应商列表
+    async fetchShopRelevanceList() {
+      try {
+        this.isLoading = true
+        const res = await fetchShopRelevanceList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 显示选择供应商弹窗 查询列表
+    handleAddSupplier() {
+      this.addSupplierDialog = true
+      this.fetchShopLabelShopList()
+    },
+    // 获取所有供应商列表
+    async fetchShopLabelShopList() {
+      this.supplierList = []
+      try {
+        const res = await fetchShopLabelShopList(this.shopListQuery)
+        this.supplierList = res.data.results
+        this.shopListTotal = res.data.totalRecord
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 确定选择供应商
+    async handleAddTagConfirm(shopId) {
+      try {
+        if (this.isAddSupplier) { return }
+        this.addSupplierDialog = false
+        this.isAddSupplier = true
+        await importShopRelevance({ shopId: shopId })
+        this.$message.success({
+          message: '添加供应商成功',
+          type: 'success',
+          center: true,
+          duration: 2000,
+          onClose: () => {
+            this.isAddSupplier = false
+            this.shopListQuery.name = ''
+            this.shopListQuery.shopId = ''
+            this.supplierRadio = null
+            this.getList()
+          }
+        })
+      } catch (error) {
+        this.isAddSupplier = false
+        console.log(error)
+      }
+    },
+    // 删除供应商
+    async handleDeleteSupper(row) {
+      try {
+        await this.$confirm('确定删除吗?该供应商关联的标签也将一并删除。', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        this.deleteShowSubmit(row)
+      } catch (error) {
+        this.$message.info('已取消删除操作')
+      }
+    },
+    // 删除
+    async deleteShowSubmit(row) {
+      try {
+        await fetchDelShopById({ relevanceId: row.id })
+        this.$message.success('操作成功')
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 标签列表
+    handleTagsList(row) {
+      this.$router.push({ path: '/library/supplierTag/tagList', query: { shopId: row.shopId, relevanceId: row.id }})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep{
+  thead .el-checkbox{display: none !important;}
+}
+</style>

+ 203 - 0
src/views/user/club/list.vue

@@ -0,0 +1,203 @@
+<template>
+  <div class="app-container">
+    机构列表
+  </div>
+</template>
+
+<script>
+import { parseTime } from '@/utils'
+import { fetchKeywordList, joinKeywordLibrary } from '@/api/library/keyword'
+import { export_json_to_excel } from '@/vendor/Export2Excel'
+
+export default {
+  data() {
+    const pickerOptions = {
+      shortcuts: [
+        {
+          text: '近1年',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 365)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近半年',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 183)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近1月',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近1周',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '昨天',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24)
+            picker.$emit('pick', [start, end])
+          }
+        }
+      ]
+    }
+    return {
+      isLoading: true,
+      time: '',
+      listQuery: {
+        keyword: '',
+        beginTime: '',
+        endTime: '',
+        labelStatus: '',
+        searchTimeCode: '',
+        fromSearch: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      pickerOptions,
+      list: [],
+      total: 0,
+      currentList: [],
+      searchDialog: false,
+      visibleKeyword: ''
+    }
+  },
+  computed: {
+    disabled() {
+      return this.currentList.length === 0
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 获取关键词列表
+    getList() {
+      this.listQuery.pageNum = 1
+      if (this.time && this.time.length > 0) {
+        this.listQuery.beginTime = this.time[0]
+        this.listQuery.endTime = this.time[1]
+      } else {
+        this.listQuery.beginTime = ''
+        this.listQuery.endTime = ''
+      }
+      this.fetchKeywordList()
+    },
+
+    // 获取关键词列表
+    async fetchKeywordList() {
+      try {
+        this.isLoading = true
+        const res = await fetchKeywordList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 添加到标签库
+    async handleAddLibrary(row) {
+      try {
+        await this.$confirm('确定将所选标签加入到标签库?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        this.addLibrarySubmit(row)
+      } catch (error) {
+        this.$message.info('已取消操作')
+      }
+    },
+
+    // 添加到标签库提交
+    async addLibrarySubmit(row) {
+      const keywords = row instanceof Event ? this.currentList : [].concat(row)
+      const ids = keywords.map((item) => item.id).join(',')
+      try {
+        await joinKeywordLibrary({ id: ids })
+        this.$message.success('添加关键词库成功')
+        keywords.forEach((item) => {
+          item.labelStatus = 1
+        })
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 选中列表项
+    handleSelectionChange(current) {
+      this.currentList = current
+    },
+
+    // 去搜索
+    handleSearch(keyword) {
+      this.visibleKeyword = keyword
+      this.searchDialog = true
+    },
+
+    // 导出
+    async handleExport() {
+      try {
+        await this.$confirm('确定将所选标签导出为xlsx?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        // 导出数据格式化
+        const filterVal = ['index', 'keyword', 'frequency', 'searchTime', 'labelStatus']
+        const data = this.formatJson(filterVal, this.currentList.slice(0))
+        export_json_to_excel({
+          header: ['序号', '标签', '搜索次数', '最近搜索时间', '标签库状态'],
+          data,
+          filename: '关键词列表'
+        })
+      } catch (error) {
+        this.$message.info('已取消导出操作')
+      }
+    },
+
+    formatJson(filterVal, jsonData) {
+      return jsonData.map((v, index) =>
+        filterVal.map((key) => {
+          if (['searchTime'].includes(key)) {
+            return parseTime(v[key])
+          }
+          if (key === 'index') return index + 1
+          if (key === 'labelStatus') {
+            const t = ['未添加', '已添加', '导入']
+            return t[v[key]]
+          }
+          return v[key]
+        })
+      )
+    },
+
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style></style>

+ 139 - 0
src/views/user/record/detail-list.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <div class="filter-container">
+      <!-- 关键词列表 -->
+      <el-table v-loading="isLoading" :data="list" border>
+        <el-table-column prop="pagePath" label="页面路径" align="center" width="300">
+          <template slot-scope="{ row }">
+            <a v-if="row.accessClient === '0'" style="color:#2fa4e7;" target="_blank" :href="row.pagePath"> {{ row.pagePath }}</a>
+            <span v-else> {{ row.pagePath }} </span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="accessSource" label="访问来源" align="center" width="100">
+          <template slot-scope="{ row }">
+            {{ row.accessSource | accessSourceFilters }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="pageTypes" label="页面类型" align="center">
+          <template slot-scope="{ row }">
+            {{ row.pageTypes ? row.pageTypes : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="pageLabel" label="页面标签" align="center">
+          <template slot-scope="{ row }">
+            {{ row.pageLabel ? row.pageLabel : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="pagePath" label="商品图片" align="center" width="100">
+          <template v-if="row.productId" slot-scope="{ row }">
+            <el-popover
+              placement="top-start"
+              title=""
+              width="180"
+              trigger="hover"
+            >
+              <img :src="row.productImage" alt="" style="width:150px;height:150px;">
+              <img slot="reference" :src="row.productImage" alt="" style="width:30px;height:30px;">
+            </el-popover>
+          </template>
+          <template v-else>
+            <span>---</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品ID" align="center" width="100">
+          <template slot-scope="{ row }">
+            {{ row.productId ? row.productId : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="productName" label="商品名称" align="center">
+          <template slot-scope="{ row }">
+            {{ row.productName ? row.productName : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column label="关联供应商" align="center">
+          <template slot-scope="{ row }">
+            {{ row.relevanceShop ? row.relevanceShop : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="accessTime" label="访问时间" align="center" />
+        <el-table-column prop="accessDuration" label="访问时长" align="center" />
+      </el-table>
+      <!-- 页码 -->
+      <pagination
+        :total="total"
+        :page-sizes="[100, 200]"
+        :page-size="100"
+        :page.sync="listQuery.pageNum"
+        :limit.sync="listQuery.pageSize"
+        @pagination="fetchBehaviorList"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import { fetchBehaviorList } from '@/api/user/record/record'
+
+export default {
+  filters: {
+    accessSourceFilters(value) {
+      // 公司类型
+      const map = {
+        '0': '直接访问',
+        '1': '百度搜索',
+        '2': '360搜索',
+        '3': '谷歌搜索',
+        '4': '神马搜索',
+        '5': '头条搜索',
+        '6': '搜狗搜索',
+        '7': '直接访问'
+      }
+      return map[value]
+    }
+  },
+  data() {
+    return {
+      isLoading: true,
+      listQuery: {
+        ip: '',
+        accessDate: '',
+        userId: 0,
+        pageNum: 1,
+        pageSize: 100
+      },
+      list: [],
+      total: 0
+    }
+  },
+  computed: {
+
+  },
+  created() {
+    this.listQuery = { ...this.listQuery, ...this.$route.query }
+    console.log('listQuery', this.listQuery)
+    this.getList()
+  },
+  methods: {
+    // 获取详情列表
+    getList() {
+      this.listQuery.pageNum = 1
+      this.fetchBehaviorList()
+    },
+    // 获取关键词列表
+    async fetchBehaviorList() {
+      try {
+        this.isLoading = true
+        const res = await fetchBehaviorList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    }
+  }
+}
+</script>
+
+<style></style>

+ 436 - 0
src/views/user/record/list.vue

@@ -0,0 +1,436 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    <el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
+      <el-tab-pane label="今日数据" name="first" />
+      <el-tab-pane label="过往数据" name="second" />
+      <div class="filter-container">
+        <div class="filter-control">
+          <span>公司名称:</span>
+          <el-input
+            v-model="listQuery.corporateName"
+            placeholder="公司名称"
+            clearable
+            @keyup.enter.native="getList"
+            @clear="getList"
+          />
+        </div>
+        <div class="filter-control">
+          <span>IP:</span>
+          <el-input
+            v-model="listQuery.ip"
+            placeholder="IP"
+            clearable
+            @keyup.enter.native="getList"
+            @clear="getList"
+          />
+        </div>
+        <div class="filter-control">
+          <span>手机号:</span>
+          <el-input
+            v-model="listQuery.phoneNumber"
+            placeholder="手机号"
+            clearable
+            style="width:160px;"
+            @keyup.enter.native="getList"
+            @clear="getList"
+          />
+        </div>
+        <div class="filter-control">
+          <span>联系人:</span>
+          <el-input
+            v-model="listQuery.contacts"
+            placeholder="联系人"
+            clearable
+            style="width:160px;"
+            @keyup.enter.native="getList"
+            @clear="getList"
+          />
+        </div>
+        <div class="filter-control">
+          <span>协销:</span>
+          <el-input
+            v-model="listQuery.spName"
+            placeholder="协销"
+            clearable
+            style="width:120px;"
+            @keyup.enter.native="getList"
+            @clear="getList"
+          />
+        </div>
+        <div class="filter-control">
+          <span>公司类型:</span>
+          <el-select v-model="listQuery.companyType" style="width:120px;" clearable @change="getList">
+            <el-option :value="0" label="请选择" />
+            <el-option :value="1" label="游客" />
+            <el-option :value="2" label="机构" />
+            <el-option :value="3" label="供应商" />
+          </el-select>
+        </div>
+        <div class="filter-control">
+          <span>访问客户端:</span>
+          <el-select v-model="listQuery.accessClient" style="width:120px;" clearable @change="getList">
+            <el-option value="" label="请选择" />
+            <el-option :value="0" label="网站" />
+            <el-option :value="1" label="小程序" />
+          </el-select>
+        </div>
+        <div class="filter-control">
+          <span>关联供应商:</span>
+          <el-select v-model="listQuery.relevanceShop" clearable @change="getList">
+            <el-option
+              v-for="item in supplierList"
+              :key="item.shopId"
+              :label="item.shopName"
+              :value="item.shopName"
+            />
+          </el-select>
+        </div>
+        <div class="filter-control">
+          <span>标签:</span>
+          <el-select v-model="tagsList" multiple filterable placeholder="请选择" @change="handleTagsOptions">
+            <el-option
+              v-for="item in tagsOptions"
+              :key="item.id"
+              :label="item.value"
+              :value="item.value"
+            />
+          </el-select>
+        </div>
+        <div v-if="tabsCurrent === 1" class="filter-control">
+          <span>访问日期:</span>
+          <el-date-picker
+            v-model="time"
+            type="daterange"
+            unlink-panels
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            :picker-options="pickerOptions"
+            @change="getList"
+          />
+        </div>
+        <div class="filter-control">
+          <el-button type="primary" @click="getList">查询</el-button>
+          <el-button v-permission="'keyword:list:export'" type="primary" @click="handleExport">
+            导出
+          </el-button>
+        </div>
+      </div>
+      <!-- 关键词列表 -->
+      <el-table v-loading="isLoading" :data="list" border>
+        <el-table-column prop="ip" label="IP" align="center" />
+        <el-table-column prop="region" label="地区" align="center" width="160" />
+        <el-table-column prop="accessClient" label="访问客户端" align="center" width="80">
+          <template slot-scope="{ row }">
+            {{ row.accessClient | accessClientFilters }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="companyType" label="公司类型" align="center" width="80">
+          <template slot-scope="{ row }">
+            {{ row.companyType | companyTypeFilters }}
+          </template>
+        </el-table-column>
+        <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="relevanceShop" label="关联供应商" align="center" width="250">
+          <template slot-scope="{ row }">
+            {{ row.relevanceShop ? row.relevanceShop : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="spName" label="标签" align="center">
+          <template slot-scope="{ row }">
+            {{ row.label ? row.label : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="addTime" label="注册时间" align="center">
+          <template slot-scope="{ row }">
+            {{ row.addTime ? row.addTime : '---' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="numbers" label="访问页面数量" align="center" width="50">
+          <template slot-scope="{ row }">
+            {{ row.numbers ? row.numbers : '0' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="accessDuration" label="总时长" align="center" />
+        <el-table-column prop="accessDate" label="访问日期" align="center" />
+        <el-table-column prop="lastAccessTime" label="最后访问时间" align="center" />
+        <el-table-column label="操作" align="center" width="120">
+          <template slot-scope="{ row }">
+            <el-button type="primary" size="mini" @click="handleRecordDetail(row)">查看详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 页码 -->
+      <pagination
+        :total="total"
+        :page-sizes="[100, 200]"
+        :page-size="100"
+        :page.sync="listQuery.pageNum"
+        :limit.sync="listQuery.pageSize"
+        @pagination="fetchRecordList"
+      />
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import { downloadWithUrl } from '@/utils'
+import { fetchRecordList } from '@/api/user/record/record'
+import { fetchShopRelevanceList, searchBehaviorList } from '@/api/user/supplier/supplier'
+import { export_json_to_excel } from '@/vendor/Export2Excel'
+
+export default {
+  filters: {
+    companyTypeFilters(value) {
+      // 公司类型
+      const map = {
+        '1': '游客',
+        '2': '机构',
+        '3': '供应商'
+      }
+      return map[value]
+    },
+    accessClientFilters(value) {
+      // 访问客户端
+      const map = {
+        '0': '网站',
+        '1': '小程序'
+      }
+      return map[value]
+    }
+  },
+  data() {
+    const pickerOptions = {
+      shortcuts: [
+        {
+          text: '近1年',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 365)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近半年',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 183)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近1月',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近1周',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '昨天',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24)
+            picker.$emit('pick', [start, end])
+          }
+        }
+      ]
+    }
+    return {
+      activeName: 'first',
+      isLoading: true,
+      pickerOptions,
+      time: '',
+      listQuery: {
+        todayType: 0, // 0 今日数据 1 以往数据
+        ip: '', // 访问人ID
+        corporateName: '', // 公司名称
+        companyType: '', // 公司类型
+        accessClient: '', // 访问客户端
+        relevanceShop: '', // 供应商名称
+        label: '', // 标签
+        contacts: '', //  联系人
+        phoneNumber: '', // 手机号
+        spName: '', // 协销
+        startTime: '',
+        endTime: '',
+        pageNum: 1,
+        pageSize: 100
+      },
+      shopListQuery: {
+        shopName: '',
+        linkMan: '',
+        contractMobile: '',
+        pageNum: 1,
+        pageSize: 100
+      },
+      list: [],
+      total: 0,
+      currentList: [],
+      supplierList: [], // 关联供应商列表
+      tagsOptions: [],
+      tagsList: [],
+      tabsCurrent: 0
+    }
+  },
+  computed: {
+
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // tab切换
+    handleClick(tab, event) {
+      if (tab.name === 'first') {
+        console.log('data', '今日数据')
+        this.tabsCurrent = this.listQuery.todayType = 0
+        this.listQuery.startTime = ''
+        this.listQuery.endTime = ''
+        this.getList()
+      } else if (tab.name === 'second') {
+        console.log('data', '以往数据')
+        this.tabsCurrent = this.listQuery.todayType = 1
+        this.getList()
+      }
+    },
+    handleTagsOptions(arr) {
+      this.listQuery.label = arr.join(',')
+      this.getList()
+      console.log('label', this.listQuery.label)
+    },
+    // 获取行为记录列表
+    getList() {
+      this.listQuery.pageNum = 1
+      if (this.time && this.time.length > 0) {
+        this.listQuery.startTime = this.time[0]
+        this.listQuery.endTime = this.time[1]
+      } else {
+        this.listQuery.startTime = ''
+        this.listQuery.endTime = ''
+      }
+      this.fetchRecordList()
+      this.fetchShopRelevanceList()
+      this.searchBehaviorList()
+    },
+
+    // 获取关键词列表
+    async fetchRecordList() {
+      try {
+        this.isLoading = true
+        const res = await fetchRecordList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取关联供应商列表选项
+    async fetchShopRelevanceList() {
+      try {
+        const res = await fetchShopRelevanceList(this.shopListQuery)
+        this.supplierList = res.data.results
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取标签库列表选项
+    async searchBehaviorList() {
+      try {
+        const res = await searchBehaviorList()
+        this.tagsOptions = res.data
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 查看详情
+    handleRecordDetail(row) {
+      this.$router.push({ path: '/user/record/detail', query: { ip: row.ip, accessDate: row.accessDate, userId: row.userId }})
+    },
+
+    // 导出
+    async handleExport() {
+      if (this.tabsCurrent === 0) {
+        // 导出今日数据
+        const url = process.env.VUE_APP_BASE_API + '/user/behavior/exportToday'
+        downloadWithUrl(url, '用户行为记录')
+      } else {
+        // 导出以往当前数据
+        try {
+          await this.$confirm('确定将用户行为记录导出为xlsx?', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          })
+          // 导出数据格式化
+          const filterVal = ['ip', 'region', 'accessClient', 'companyType', 'corporateName', 'contacts', 'phoneNumber', 'spName', 'relevanceShop', 'label', 'addTime', 'numbers', 'accessDuration', 'accessDate']
+          const data = this.formatJson(filterVal, this.list.slice(0))
+          export_json_to_excel({
+            header: ['IP', '地区', '访问客户端', '公司类型', '公司名称', '联系人', '手机号', '所属协销', '关联供应商', '标签', '注册时间', '访问页面数量', '总时长', '访问日期'],
+            data,
+            filename: '用户行为记录'
+          })
+        } catch (error) {
+          console.log(error)
+          this.$message.info('已取消导出操作')
+        }
+      }
+    },
+    formatJson(filterVal, jsonData) {
+      return jsonData.map((v, index) =>
+        filterVal.map((key) => {
+          if (key === 'accessClient') {
+            const t = ['网站', '小程序']
+            return t[v[key]]
+          }
+          if (key === 'companyType') {
+            const t = ['游客', '机构', '供应商']
+            return t[v[key]]
+          }
+          return v[key]
+        })
+      )
+    }
+  }
+}
+</script>
+
+<style></style>

+ 204 - 0
src/views/user/supplier/list.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="app-container">
+    <!-- 顶部操作区域 -->
+    供应商列表
+  </div>
+</template>
+
+<script>
+import { parseTime } from '@/utils'
+import { fetchKeywordList, joinKeywordLibrary } from '@/api/library/keyword'
+import { export_json_to_excel } from '@/vendor/Export2Excel'
+
+export default {
+  data() {
+    const pickerOptions = {
+      shortcuts: [
+        {
+          text: '近1年',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 365)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近半年',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 183)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近1月',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '近1周',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+            picker.$emit('pick', [start, end])
+          }
+        },
+        {
+          text: '昨天',
+          onClick(picker) {
+            const end = new Date()
+            const start = new Date()
+            start.setTime(start.getTime() - 3600 * 1000 * 24)
+            picker.$emit('pick', [start, end])
+          }
+        }
+      ]
+    }
+    return {
+      isLoading: true,
+      time: '',
+      listQuery: {
+        keyword: '',
+        beginTime: '',
+        endTime: '',
+        labelStatus: '',
+        searchTimeCode: '',
+        fromSearch: '',
+        pageNum: 1,
+        pageSize: 10
+      },
+      pickerOptions,
+      list: [],
+      total: 0,
+      currentList: [],
+      searchDialog: false,
+      visibleKeyword: ''
+    }
+  },
+  computed: {
+    disabled() {
+      return this.currentList.length === 0
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    // 获取关键词列表
+    getList() {
+      this.listQuery.pageNum = 1
+      if (this.time && this.time.length > 0) {
+        this.listQuery.beginTime = this.time[0]
+        this.listQuery.endTime = this.time[1]
+      } else {
+        this.listQuery.beginTime = ''
+        this.listQuery.endTime = ''
+      }
+      this.fetchKeywordList()
+    },
+
+    // 获取关键词列表
+    async fetchKeywordList() {
+      try {
+        this.isLoading = true
+        const res = await fetchKeywordList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 添加到标签库
+    async handleAddLibrary(row) {
+      try {
+        await this.$confirm('确定将所选标签加入到标签库?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        this.addLibrarySubmit(row)
+      } catch (error) {
+        this.$message.info('已取消操作')
+      }
+    },
+
+    // 添加到标签库提交
+    async addLibrarySubmit(row) {
+      const keywords = row instanceof Event ? this.currentList : [].concat(row)
+      const ids = keywords.map((item) => item.id).join(',')
+      try {
+        await joinKeywordLibrary({ id: ids })
+        this.$message.success('添加关键词库成功')
+        keywords.forEach((item) => {
+          item.labelStatus = 1
+        })
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 选中列表项
+    handleSelectionChange(current) {
+      this.currentList = current
+    },
+
+    // 去搜索
+    handleSearch(keyword) {
+      this.visibleKeyword = keyword
+      this.searchDialog = true
+    },
+
+    // 导出
+    async handleExport() {
+      try {
+        await this.$confirm('确定将所选标签导出为xlsx?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        // 导出数据格式化
+        const filterVal = ['index', 'keyword', 'frequency', 'searchTime', 'labelStatus']
+        const data = this.formatJson(filterVal, this.currentList.slice(0))
+        export_json_to_excel({
+          header: ['序号', '标签', '搜索次数', '最近搜索时间', '标签库状态'],
+          data,
+          filename: '关键词列表'
+        })
+      } catch (error) {
+        this.$message.info('已取消导出操作')
+      }
+    },
+
+    formatJson(filterVal, jsonData) {
+      return jsonData.map((v, index) =>
+        filterVal.map((key) => {
+          if (['searchTime'].includes(key)) {
+            return parseTime(v[key])
+          }
+          if (key === 'index') return index + 1
+          if (key === 'labelStatus') {
+            const t = ['未添加', '已添加', '导入']
+            return t[v[key]]
+          }
+          return v[key]
+        })
+      )
+    },
+
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
+    }
+  }
+}
+</script>
+
+<style></style>