瀏覽代碼

关键词库版本接口联调

yuwenjun1997 2 年之前
父節點
當前提交
32e804143c

+ 5 - 2
.env.development

@@ -2,8 +2,11 @@
 ENV = 'development'
 
 # base api
-# VUE_APP_BASE_API = 'http://192.168.2.75:18015'
+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 = 'https://mapi-b.caimei365.com'
 # 图片上传,索引更新等
 VUE_APP_CORE_API = 'https://core-b.caimei365.com'
+
+# 采美网站url
+VUE_APP_CAIMEI_URL = 'https://www.caimei365.com'

+ 3 - 0
.env.production

@@ -5,3 +5,6 @@ ENV = 'production'
 VUE_APP_BASE_API = 'https://mapi.caimei365.com'
 # 图片上传,索引更新等
 VUE_APP_CORE_API = 'https://core.caimei365.com'
+
+# 采美网站url
+VUE_APP_CAIMEI_URL = 'https://www.caimei365.com'

+ 3 - 0
.env.staging

@@ -7,3 +7,6 @@ ENV = 'staging'
 VUE_APP_BASE_API = 'https://mapi-b.caimei365.com'
 # 图片上传,索引更新等
 VUE_APP_CORE_API = 'https://core-b.caimei365.com'
+
+# 采美网站url
+VUE_APP_CAIMEI_URL = 'https://www.caimei365.com'

+ 60 - 0
src/api/library/keyword.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+export function fetchKeywordList(params) {
+  return request({
+    url: '/keyword/keyword/list',
+    method: 'get',
+    params
+  })
+}
+
+export function joinKeywordLibrary(params) {
+  return request({
+    url: '/keyword/join/label',
+    method: 'get',
+    params
+  })
+}
+
+export function fetchKeywordRecommendList(params) {
+  return request({
+    url: '/keyword/recommend/list',
+    method: 'get',
+    params
+  })
+}
+
+export function ignoreKeyword(params) {
+  return request({
+    url: '/keyword/ignore/label',
+    method: 'get',
+    params
+  })
+}
+
+export function addKeyword(params) {
+  return request({
+    url: '/keyword/insert/label',
+    method: 'get',
+    params
+  })
+}
+
+export function deleteKeyword(params) {
+  return request({
+    url: '/keyword/delete/label',
+    method: 'get',
+    params
+  })
+}
+
+export function importKeywordXlsx(data) {
+  return request({
+    url: '/keyword/import/label',
+    method: 'post',
+    data,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  })
+}

+ 19 - 4
src/components/SearchModel/index.vue

@@ -1,10 +1,10 @@
 <template>
   <el-dialog title="搜索页面" :visible="value" width="480px" @close="toggleVisiable(false)">
     <div class="wrapper">
-      <el-button plain @click="$emit('click', 1)">商城</el-button>
-      <el-button plain @click="$emit('click', 2)">美业资料</el-button>
-      <el-button plain @click="$emit('click', 3)">信息平台</el-button>
-      <el-button plain @click="$emit('click', 4)">采美百科</el-button>
+      <el-button plain @click="handleClick(1)">商城</el-button>
+      <el-button plain @click="handleClick(2)">美业资料</el-button>
+      <el-button plain @click="handleClick(3)">信息平台</el-button>
+      <el-button plain @click="handleClick(4)">采美百科</el-button>
     </div>
   </el-dialog>
 </template>
@@ -19,11 +19,26 @@ export default {
     value: {
       type: Boolean,
       default: false
+    },
+    keyword: {
+      type: String,
+      default: ''
     }
   },
   methods: {
     toggleVisiable(visible) {
       this.$emit('change', visible)
+    },
+    handleClick(type) {
+      const localhost = process.env.VUE_APP_CAIMEI_URL
+      const urls = {
+        1: localhost + '/product/list.html?keyword=' + this.keyword,
+        2: localhost,
+        3: localhost + '/info/search-1.html?keyword=' + this.keyword,
+        4: localhost + '/encyclopedia/search.html?keyword=' + this.keyword
+      }
+      window.open(urls[type], '_blank')
+      this.$emit('change', false)
     }
   }
 }

+ 12 - 0
src/directive/debounce/debounce.js

@@ -0,0 +1,12 @@
+import { debounce } from '@/utils'
+
+export default {
+  inserted: function(el, binding) {
+    el.addEventListener(
+      'click',
+      debounce(() => {
+        binding.value()
+      }, 200)
+    )
+  }
+}

+ 13 - 0
src/directive/debounce/index.js

@@ -0,0 +1,13 @@
+import debounce from './debounce'
+
+const install = function(Vue) {
+  Vue.directive('debounce', debounce)
+}
+
+if (window.Vue) {
+  window['debounce'] = debounce
+  Vue.use(install) // eslint-disable-line
+}
+
+debounce.install = install
+export default debounce

+ 6 - 0
src/directive/index.js

@@ -0,0 +1,6 @@
+import Vue from 'vue'
+import permission from './permission'
+import debounce from './debounce'
+
+Vue.use(permission)
+Vue.use(debounce)

+ 1 - 2
src/main.js

@@ -23,7 +23,7 @@ import * as filters from './filters' // global filters
 
 import Component from '@/components'
 
-import permission from './directive/permission'
+import './directive/index'
 
 /**
  * If you don't want to use mock-server
@@ -50,7 +50,6 @@ Vue.use(Element, {
 })
 
 Vue.use(Component)
-Vue.use(permission)
 
 // register global utility filters
 Object.keys(filters).forEach((key) => {

+ 1 - 1
src/utils/index.js

@@ -25,7 +25,7 @@ export function parseTime(time, cFormat) {
       } else {
         // support safari
         // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
-        time = time.replace(new RegExp(/-/gm), '/')
+        // time = time.replace(new RegExp(/-/gm), '/')
       }
     }
 

+ 1 - 1
src/utils/request.js

@@ -85,7 +85,7 @@ service.interceptors.response.use(
   error => {
     console.log('err' + error) // for debug
     Message({
-      message: error.msg,
+      message: error.msg || error.message,
       type: 'error',
       duration: 5 * 1000
     })

+ 21 - 0
src/vendor/Imprt2Excel.js

@@ -0,0 +1,21 @@
+import XLSX from 'xlsx'
+
+/**
+ * xlsx文件转换成json数据
+ * @param {File} file xlsx文件
+ * @returns {Promise<error:string, Array>}
+ */
+export function import_excel_to_json(file) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+
+    reader.onload = function(e) {
+      const data = e.target.result
+      const wb = XLSX.read(data, { type: 'binary' })
+      const jsonData = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]])
+      resolve(jsonData)
+    }
+    reader.onerror = () => reject('错误的文件类型!')
+    reader.readAsBinaryString(file)
+  })
+}

+ 130 - 61
src/views/library/keyword/list.vue

@@ -4,24 +4,31 @@
     <div class="filter-container">
       <div class="filter-control">
         <span>关键词:</span>
-        <el-input v-model="listQuery.keyword" placeholder="关键词" @keyup.enter.native="getList" />
+        <el-input
+          v-model="listQuery.keyword"
+          placeholder="关键词"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
       </div>
       <div class="filter-control">
         <span>搜索统计:</span>
         <el-date-picker
-          v-model="listQuery.time"
+          v-model="time"
           type="daterange"
           unlink-panels
           range-separator="至"
           start-placeholder="开始日期"
           end-placeholder="结束日期"
           :picker-options="pickerOptions"
+          value-format="yyyy-MM-dd"
           @change="getList"
         />
       </div>
       <div class="filter-control">
         <span>标签库状态:</span>
-        <el-select v-model="listQuery.status" clearable @change="getList">
+        <el-select v-model="listQuery.labelStatus" clearable @change="getList">
           <el-option :value="0" label="未添加" />
           <el-option :value="1" label="已添加" />
         </el-select>
@@ -31,41 +38,57 @@
         <el-button v-permission="'keyword:list:join'" type="primary" :disabled="disabled" @click="handleAddLibrary">
           加入标签库
         </el-button>
-        <el-button v-permission="'keyword:list:export'" type="primary" @click="handleExport">导出</el-button>
+        <el-button v-permission="'keyword:list:export'" type="primary" :disabled="disabled" @click="handleExport">
+          导出
+        </el-button>
       </div>
     </div>
 
     <!-- 关键词列表 -->
-    <el-table :data="list" border @selection-change="handleSelectionChange">
+    <el-table v-loading="isLoading" :data="list" border @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column prop="name" label="序号" width="100" align="center" />
-      <el-table-column prop="name" label="关键词" align="center" />
-      <el-table-column prop="count" label="搜索次数" width="120" align="center" sortable />
-      <el-table-column prop="name" label="最近搜索时间" 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 label="最近搜索时间" align="center">
+        <template slot-scope="{ row }">
+          <span v-if="row.searchTime">{{ row.searchTime | parseTime }}</span>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
       <el-table-column width="140" label="标签库状态" align="center">
-        <template>未添加</template>
+        <template slot-scope="{ row }">{{ row.labelStatus === 1 ? '已添加' : '未添加' }}</template>
       </el-table-column>
       <el-table-column label="操作" align="center" width="240">
         <template slot-scope="{ row }">
-          <el-button v-permission="'keyword:list:join'" type="primary" size="mini" @click="handleAddLibrary(row)">
-            加入标签库
-          </el-button>
-          <el-button type="primary" size="mini">去搜索</el-button>
+          <template v-if="row.labelStatus === 0">
+            <el-button v-permission="'keyword:list:join'" type="primary" size="mini" @click="handleAddLibrary(row)">
+              加入标签库
+            </el-button>
+          </template>
+          <el-button type="primary" size="mini" @click="handleSearch(row.keyword)">去搜索</el-button>
         </template>
       </el-table-column>
     </el-table>
 
     <!-- 页码 -->
-    <pagination :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList" />
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="fetchKeywordList"
+    />
 
     <!-- 去搜索跳转 -->
-    <SearchModel v-model="visible" @click="handleSearch" />
+    <SearchModel v-model="searchDialog" :keyword="visibleKeyword" />
   </div>
 </template>
 
 <script>
 import SearchModel from '@/components/SearchModel'
-import { downloadWithUrl } from '@/utils'
+import { parseTime } from '@/utils'
+import { fetchKeywordList, joinKeywordLibrary } from '@/api/library/keyword'
+import { export_json_to_excel } from '@/vendor/Export2Excel'
 
 export default {
   components: { SearchModel },
@@ -120,28 +143,24 @@ export default {
       ]
     }
     return {
+      isLoading: true,
+      time: '',
       listQuery: {
-        pageNum: 1,
-        pageSize: 10,
         keyword: '',
-        time: '',
-        status: ''
+        beginTime: '',
+        endTime: '',
+        labelStatus: '',
+        searchTimeCode: '',
+        fromSearch: '',
+        pageNum: 1,
+        pageSize: 10
       },
       pickerOptions,
-      list: [
-        {
-          count: 1
-        },
-        {
-          count: 2
-        },
-        {
-          count: 3
-        }
-      ],
+      list: [],
       total: 0,
       currentList: [],
-      visible: false
+      searchDialog: false,
+      visibleKeyword: ''
     }
   },
   computed: {
@@ -149,9 +168,35 @@ export default {
       return this.currentList.length === 0
     }
   },
+  created() {
+    this.getList()
+  },
   methods: {
     // 获取关键词列表
-    getList() {},
+    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) {
@@ -168,7 +213,19 @@ export default {
     },
 
     // 添加到标签库提交
-    addLibrarySubmit() {},
+    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) {
@@ -176,38 +233,50 @@ export default {
     },
 
     // 去搜索
-    handleSearch(type) {
-      console.log(type)
-      this.visible = false
+    handleSearch(keyword) {
+      this.visibleKeyword = keyword
+      this.searchDialog = true
     },
 
     // 导出
     async handleExport() {
-      const text = await this.$confirm(`确认导出全部关键词?`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).catch(() => {
-        this.$message.info('已取消操作')
-      })
-      if (text !== 'confirm') return
-      let notification = null
-      notification = this.$notify({
-        title: '提示',
-        message: `正在导出全部关键词,请勿重复操作!`,
-        duration: 0
-      })
-      // 使用a链接下载
-      const downUrl = `${process.env.VUE_APP_BASE_API}/download`
-      downloadWithUrl(downUrl, '关键词')
-        .catch((err) => {
-          console.log(err)
-          this.$message.error(`导出关键词失败`)
+      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: '关键词列表'
         })
-        .finally(() => {
-          notification.close()
-          this.onSelectorCancel()
+      } 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
     }
   }
 }

+ 152 - 52
src/views/library/keyword/recommend.vue

@@ -4,12 +4,18 @@
     <div class="filter-container">
       <div class="filter-control">
         <span>关键词:</span>
-        <el-input v-model="listQuery.keyword" placeholder="关键词" @keyup.enter.native="getList" />
+        <el-input
+          v-model="listQuery.keyword"
+          placeholder="关键词"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
       </div>
       <div class="filter-control">
         <span>推荐日期:</span>
         <el-date-picker
-          v-model="listQuery.time"
+          v-model="time"
           type="daterange"
           unlink-panels
           range-separator="至"
@@ -21,44 +27,75 @@
       </div>
       <div class="filter-control">
         <el-button type="primary" @click="getList">查询</el-button>
-        <el-button v-permission="'keyword:recommend:join'" type="primary" :disabled="disabled" @click="handleAddLibrary">
+        <el-button
+          v-permission="'keyword:recommend:join'"
+          type="primary"
+          :disabled="disabled"
+          @click="handleAddLibrary"
+        >
           加入标签库
         </el-button>
-        <el-button v-permission="'keyword:recommend:ignore'" type="primary" :disabled="disabled" @click="handleIgnoreTag">
+        <el-button
+          v-permission="'keyword:recommend:ignore'"
+          type="primary"
+          :disabled="disabled"
+          @click="handleIgnoreTag"
+        >
           忽略
         </el-button>
-        <el-button v-permission="'keyword:recommend:export'" type="primary" @click="handleExport"> 导出 </el-button>
+        <el-button v-permission="'keyword:recommend:export'" type="primary" :disabled="disabled" @click="handleExport">
+          导出
+        </el-button>
       </div>
     </div>
 
     <!-- 关键词列表 -->
-    <el-table :data="list" border @selection-change="handleSelectionChange">
+    <el-table v-loading="isLoading" :data="list" border @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column prop="name" label="序号" width="100" align="center" />
-      <el-table-column prop="name" label="关键词" align="center" />
-      <el-table-column prop="name" label="推荐日期" width="160" 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 label="推荐日期" width="200" align="center">
+        <template slot-scope="{ row }">
+          <span v-if="row.recommendTime">{{ row.recommendTime | parseTime }}</span>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
       <el-table-column label="操作" align="center" width="320">
         <template slot-scope="{ row }">
-          <el-button v-permission="'keyword:recommend:join'" type="primary" size="mini" @click="handleAddLibrary(row)">
-            加入标签库
-          </el-button>
+          <template v-if="row.labelStatus === 0">
+            <el-button
+              v-permission="'keyword:recommend:join'"
+              type="primary"
+              size="mini"
+              @click="handleAddLibrary(row)"
+            >
+              加入标签库
+            </el-button>
+          </template>
           <el-button v-permission="'keyword:recommend:ignore'" type="primary" size="mini" @click="handleIgnoreTag(row)">忽略</el-button>
-          <el-button type="primary" size="mini" @click="visible = true">去搜索</el-button>
+          <el-button type="primary" size="mini" @click="handleSearch(row.keyword)">去搜索</el-button>
         </template>
       </el-table-column>
     </el-table>
 
     <!-- 页码 -->
-    <pagination :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList" />
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="fetchKeywordList"
+    />
 
     <!-- 去搜索跳转 -->
-    <SearchModel v-model="visible" @click="handleSearch" />
+    <SearchModel v-model="searchDialog" :keyword="visibleKeyword" />
   </div>
 </template>
 
 <script>
 import SearchModel from '@/components/SearchModel'
-import { downloadWithUrl } from '@/utils'
+import { parseTime } from '@/utils'
+import { fetchKeywordRecommendList, ignoreKeyword, joinKeywordLibrary } from '@/api/library/keyword'
+import { export_json_to_excel } from '@/vendor/Export2Excel'
 export default {
   components: { SearchModel },
   data() {
@@ -112,12 +149,14 @@ export default {
       ]
     }
     return {
+      isLoading: true,
+      time: '',
       listQuery: {
-        pageNum: 1,
-        pageSize: 10,
         keyword: '',
-        time: '',
-        status: ''
+        beginTime: '',
+        endTime: '',
+        pageNum: 1,
+        pageSize: 10
       },
       pickerOptions,
       list: [
@@ -133,7 +172,8 @@ export default {
       ],
       total: 0,
       currentList: [],
-      visible: false
+      searchDialog: false,
+      visibleKeyword: ''
     }
   },
   computed: {
@@ -141,9 +181,35 @@ export default {
       return this.currentList.length === 0
     }
   },
+  created() {
+    this.getList()
+  },
   methods: {
     // 获取关键词列表
-    getList() {},
+    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 fetchKeywordRecommendList(this.listQuery)
+        this.list = res.data.results
+        this.total = res.data.totalRecord
+        this.isLoading = false
+      } catch (error) {
+        console.log(error)
+      }
+    },
 
     // 添加到标签库
     async handleAddLibrary(row) {
@@ -160,9 +226,21 @@ export default {
     },
 
     // 添加到标签库提交
-    addLibrarySubmit() {},
+    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)
+      }
+    },
 
-    // 添加到标签库
+    // 忽略关键词
     async handleIgnoreTag(row) {
       try {
         await this.$confirm('确定忽略所选标签?', {
@@ -176,8 +254,18 @@ export default {
       }
     },
 
-    // 添加到标签库提交
-    ignoreTagSubmit() {},
+    // 忽略关键词提交
+    async ignoreTagSubmit(row) {
+      const keywords = row instanceof Event ? this.currentList : [].concat(row)
+      const ids = keywords.map((item) => item.id).join(',')
+      try {
+        await ignoreKeyword({ id: ids })
+        this.$message.success('已忽略所选关键词')
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
+    },
 
     // 选中列表项
     handleSelectionChange(current) {
@@ -185,38 +273,50 @@ export default {
     },
 
     // 去搜索
-    handleSearch(type) {
-      console.log(type)
-      this.visible = false
+    handleSearch(keyword) {
+      this.visibleKeyword = keyword
+      this.searchDialog = true
     },
 
     // 导出
     async handleExport() {
-      const text = await this.$confirm(`确认导出全部关键词?`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).catch(() => {
-        this.$message.info('已取消操作')
-      })
-      if (text !== 'confirm') return
-      let notification = null
-      notification = this.$notify({
-        title: '提示',
-        message: `正在导出全部关键词,请勿重复操作!`,
-        duration: 0
-      })
-      // 使用a链接下载
-      const downUrl = `${process.env.VUE_APP_BASE_API}/download`
-      downloadWithUrl(downUrl, '关键词')
-        .catch((err) => {
-          console.log(err)
-          this.$message.error(`导出关键词失败`)
+      try {
+        await this.$confirm('确定将所选标签导出为xlsx?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        })
+        // 导出数据格式化
+        const filterVal = ['index', 'keyword', 'recommendTime']
+        const data = this.formatJson(filterVal, this.currentList.slice(0))
+        export_json_to_excel({
+          header: ['序号', '标签', '推荐日期'],
+          data,
+          filename: '推荐关键词列表'
         })
-        .finally(() => {
-          notification.close()
-          this.onSelectorCancel()
+      } catch (error) {
+        this.$message.info('已取消导出操作')
+      }
+    },
+
+    formatJson(filterVal, jsonData) {
+      return jsonData.map((v, index) =>
+        filterVal.map((key) => {
+          if (['recommendTime'].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
     }
   }
 }

+ 173 - 60
src/views/library/tag/list.vue

@@ -4,20 +4,26 @@
     <div class="filter-container">
       <div class="filter-control">
         <span>关键词:</span>
-        <el-input v-model="listQuery.keyword" placeholder="关键词" @keyup.enter.native="getList" />
+        <el-input
+          v-model="listQuery.keyword"
+          placeholder="关键词"
+          clearable
+          @keyup.enter.native="getList"
+          @clear="getList"
+        />
       </div>
       <div class="filter-control">
         <span>来源:</span>
-        <el-select v-model="listQuery.status" clearable @change="getList">
-          <el-option :value="0" label="手动添加" />
-          <el-option :value="1" label="用户关键词统计" />
-          <el-option :value="2" label="导入" />
+        <el-select v-model="listQuery.fromSearch" clearable @change="getList">
+          <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-date-picker
-          v-model="listQuery.time"
+          v-model="time"
           type="daterange"
           unlink-panels
           range-separator="至"
@@ -30,38 +36,57 @@
       <div class="filter-control">
         <el-button type="primary" @click="getList">查询</el-button>
         <el-button v-permission="'tag:list:add'" type="primary" @click="handleAddTag">添加标签</el-button>
-        <el-button v-permission="'tag:list:del'" type="danger" :disabled="disabled" @click="handleDeleteTag">删除</el-button>
-        <el-button v-permission="'tag:list:improt'" type="primary" @click="importDialog = true">导入</el-button>
-        <el-button v-permission="'tag:list:export'" type="primary" @click="handleExport">导出</el-button>
+        <el-button v-permission="'tag:list:del'" type="danger" :disabled="disabled" @click="handleDeleteTag">
+          删除
+        </el-button>
+        <el-button v-permission="'tag:list:improt'" type="primary" @click="importDialog = true"> 导入 </el-button>
+        <el-button v-permission="'tag:list:export'" type="primary" :disabled="disabled" @click="handleExport">导出</el-button>
       </div>
     </div>
 
     <!-- 关键词列表 -->
-    <el-table :data="list" border @selection-change="handleSelectionChange">
+    <el-table v-loading="isLoading" :data="list" border @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
-      <el-table-column prop="name" label="序号" width="100" align="center" />
-      <el-table-column prop="name" label="关键词" align="center" />
-      <el-table-column prop="count" label="搜索次数" width="120" align="center" sortable />
+      <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>单个添加</template>
+        <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 prop="name" label="最近搜索时间" align="center" width="160" />
-      <el-table-column width="140" label="标签库状态" align="center">
-        <template>未添加</template>
+      <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.searchTime">{{ row.searchTime | 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>
-          <el-button type="primary" size="mini" @click="searchDialog = true">去搜索</el-button>
+          <el-button type="primary" size="mini" @click="handleSearch(row.keyword)">去搜索</el-button>
         </template>
       </el-table-column>
     </el-table>
 
     <!-- 页码 -->
-    <pagination :total="total" :page.sync="listQuery.pageNum" :limit.sync="listQuery.pageSize" @pagination="getList" />
+    <pagination
+      :total="total"
+      :page.sync="listQuery.pageNum"
+      :limit.sync="listQuery.pageSize"
+      @pagination="fetchKeywordList"
+    />
 
     <!-- 去搜索跳转 -->
-    <SearchModel v-model="searchDialog" @click="handleSearch" />
+    <SearchModel v-model="searchDialog" :keyword="visibleKeyword" />
 
     <!-- 添加标签 -->
     <el-dialog
@@ -72,8 +97,8 @@
       :show-close="false"
     >
       <el-form ref="tagForm" :model="tagForm" :rules="tagFormRules">
-        <el-form-item label="标签名:" prop="name">
-          <el-input v-model="tagForm.name" />
+        <el-form-item label="标签名:" prop="keyword">
+          <el-input v-model="tagForm.keyword" />
         </el-form-item>
       </el-form>
       <div slot="footer">
@@ -100,6 +125,8 @@
             :file-list="fileList"
             :on-success="handleUploadSuccess"
             :on-remove="handleUploadRemove"
+            :on-change="handleUploadChange"
+            :auto-upload="false"
           >
             <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
             <div slot="tip" class="el-upload__tip">只能上传.xlsx, .xls文件</div>
@@ -109,7 +136,7 @@
       </el-form>
       <div slot="footer">
         <el-button @click="closeImportDialog">取 消</el-button>
-        <el-button type="primary" @click="handleImportConfirm">导 入</el-button>
+        <el-button v-debounce="handleImportConfirm" type="primary">导 入</el-button>
       </div>
     </el-dialog>
   </div>
@@ -117,7 +144,9 @@
 
 <script>
 import SearchModel from '@/components/SearchModel'
-import { downloadWithUrl } from '@/utils'
+import { parseTime } from '@/utils'
+import { addKeyword, deleteKeyword, fetchKeywordList, importKeywordXlsx } from '@/api/library/keyword'
+import { export_json_to_excel } from '@/vendor/Export2Excel'
 export default {
   components: { SearchModel },
   data() {
@@ -171,24 +200,30 @@ export default {
       ]
     }
     return {
+      isLoading: true,
       pickerOptions,
+      time: '',
       listQuery: {
-        pageNum: 1,
-        pageSize: 10,
         keyword: '',
-        time: '',
-        status: ''
+        beginTime: '',
+        endTime: '',
+        labelStatus: 1,
+        searchTimeCode: '',
+        fromSearch: '',
+        pageNum: 1,
+        pageSize: 10
       },
       list: [],
       total: 0,
       currentList: [],
       searchDialog: false,
+      visibleKeyword: '',
       addTagDialog: false,
       tagForm: {
-        name: ''
+        keyword: ''
       },
       tagFormRules: {
-        name: [{ required: true, message: '请输入标签名称', trigger: ['blur'] }]
+        keyword: [{ required: true, message: '请输入标签名称', trigger: ['blur'] }]
       },
       exportKeywordList: [],
       importDialog: false,
@@ -206,16 +241,45 @@ export default {
       return this.currentList.length === 0
     }
   },
+  created() {
+    this.getList()
+  },
   methods: {
     // 获取关键词列表
-    getList() {},
+    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)
+      }
+    },
 
     // 选中列表项
-    handleSelectionChange() {},
+    handleSelectionChange(current) {
+      this.currentList = current
+    },
 
     // 去搜索
-    handleSearch(type) {
-      this.searchDialog = false
+    handleSearch(keyword) {
+      this.visibleKeyword = keyword
+      this.searchDialog = true
     },
 
     // 添加标签
@@ -240,7 +304,14 @@ export default {
     },
 
     // 提交添加标签信息
-    addTagSubmit() {
+    async addTagSubmit() {
+      try {
+        await addKeyword(this.tagForm)
+        this.$message.success('添加标签成功')
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
       this.closeAddTagDialog()
     },
 
@@ -259,7 +330,17 @@ export default {
     },
 
     // 提交删除标签操作
-    deleteTagSubmit(row) {},
+    async deleteTagSubmit(row) {
+      const keywords = row instanceof Event ? this.currentList : [].concat(row)
+      const ids = keywords.map((item) => item.id).join(',')
+      try {
+        await deleteKeyword({ id: ids })
+        this.$message.success('已忽略所选关键词')
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
+    },
 
     // 导入关键词
     async handleImport() {},
@@ -272,6 +353,7 @@ export default {
 
     // 导入关键词确定
     async handleImportConfirm() {
+      console.log(1)
       try {
         await this.$refs.importForm.validate()
         this.importSumbit()
@@ -280,35 +362,57 @@ export default {
       }
     },
 
-    importSumbit() {},
+    async importSumbit() {
+      const file = this.fileList.length > 0 && this.fileList[0]
+      if (!file) return
+      try {
+        const formData = new FormData()
+        this.fileList = []
+        this.importDialog = false
+        formData.append('keywordFile', file.raw)
+        await importKeywordXlsx(formData)
+        this.$message.success('导入标签成功')
+        this.getList()
+      } catch (error) {
+        console.log(error)
+      }
+    },
 
     // 导出
     async handleExport() {
-      const text = await this.$confirm(`确认导出全部关键词?`, '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).catch(() => {
-        this.$message.info('已取消操作')
-      })
-      if (text !== 'confirm') return
-      let notification = null
-      notification = this.$notify({
-        title: '提示',
-        message: `正在导出全部关键词,请勿重复操作!`,
-        duration: 0
-      })
-      // 使用a链接下载
-      const downUrl = `${process.env.VUE_APP_BASE_API}/download`
-      downloadWithUrl(downUrl, '关键词')
-        .catch((err) => {
-          console.log(err)
-          this.$message.error(`导出关键词失败`)
+      try {
+        await this.$confirm('确定将所选标签导出为xlsx?', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
         })
-        .finally(() => {
-          notification.close()
-          this.onSelectorCancel()
+        // 导出数据格式化
+        const filterVal = ['index', 'keyword', 'frequency', 'fromSearch', 'searchTime', 'addTime']
+        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', 'addTime'].includes(key)) {
+            return parseTime(v[key])
+          }
+          if (key === 'index') return index + 1
+          if (key === 'fromSearch') {
+            const t = ['单个添加', '系统推荐', '导入']
+            return t[v[key] - 1]
+          }
+          return v[key]
+        })
+      )
     },
 
     // 文件上传成功
@@ -320,6 +424,15 @@ export default {
     // 上传文件删除
     handleUploadRemove(file, fileList) {
       this.fileList = fileList
+    },
+
+    handleUploadChange(file, fileList) {
+      this.fileList = fileList
+      this.importForm.fileUrl = file.name
+    },
+
+    indexMethod(index) {
+      return index + this.listQuery.pageSize * (this.listQuery.pageNum - 1) + 1
     }
   }
 }