Эх сурвалжийг харах

Merge branch 'master' into dev1.1.6

lianbingjie 3 жил өмнө
parent
commit
2dff198a4f
31 өөрчлөгдсөн 3778 нэмэгдсэн , 1290 устгасан
  1. 1 0
      README.md
  2. 12 1
      src/api/modules/home.js
  3. 47 0
      src/api/modules/subscribe.js
  4. BIN
      src/assets/images/icon/question.png
  5. 7 1
      src/assets/js/selector.js
  6. 83 10
      src/components/article-item/ArticleItem.vue
  7. 998 17
      src/components/push-list/PushList.vue
  8. 1 0
      src/components/selector/BusinessScopeSelectorContent.vue
  9. 3 0
      src/components/selector/BuyerclassSelector.vue
  10. 47 4
      src/components/selector/BuyerclassSelectorContent.vue
  11. 5 0
      src/components/selector/IndustrySelector.vue
  12. 49 19
      src/components/selector/IndustrySelectorContent.vue
  13. 59 18
      src/components/selector/InfoTypeSelectorContent.vue
  14. 321 0
      src/components/selector/PopSelector.vue
  15. 5 0
      src/components/selector/TimeSelector.vue
  16. 79 22
      src/components/selector/TimeSelectorContent.vue
  17. 3 0
      src/utils/bus.js
  18. 193 8
      src/views/SubPush.vue
  19. 1 2
      src/views/portrayal/EntSearchPortrayal.vue
  20. 1 1
      src/views/reportData/pageWeek.vue
  21. 149 11
      src/views/subscribe/Config.vue
  22. 54 6
      src/views/subscribe/Scope.vue
  23. 587 0
      src/views/subscribe/components/key/Edit.vue
  24. 141 275
      src/views/subscribe/components/key/KeyConfig.vue
  25. 0 164
      src/views/subscribe/components/key/KeyEdit.vue
  26. 252 163
      src/views/subscribe/components/key/List.vue
  27. 51 75
      src/views/subscribe/components/key/SubConfig.vue
  28. 0 289
      src/views/subscribe/components/scope/Add.vue
  29. 416 64
      src/views/subscribe/components/scope/Edit.vue
  30. 205 139
      src/views/subscribe/components/scope/List.vue
  31. 8 1
      vue.config.js

+ 1 - 0
README.md

@@ -1,4 +1,5 @@
 ## 剑鱼大会员PC端
 ## 剑鱼大会员PC端
+
 > 采用Single-SPA微前端架构,避免后期多次同步修改剑鱼公用头部导航及底部、登录等
 > 采用Single-SPA微前端架构,避免后期多次同步修改剑鱼公用头部导航及底部、登录等
 
 
 v1.1.6
 v1.1.6

+ 12 - 1
src/api/modules/home.js

@@ -10,7 +10,18 @@ export const getSearchTag = params => request({
 export function getPushList (data) {
 export function getPushList (data) {
   data = qs.stringify(data)
   data = qs.stringify(data)
   return request({
   return request({
-    url: '/subscribe/push/list',
+    baseURL: '/publicapply',
+    url: '/subscribe/historyPaging',
+    method: 'post',
+    data: data
+  })
+}
+
+export function getExportPushList (data) {
+  data = qs.stringify(data)
+  return request({
+    baseURL: '/publicapply',
+    url: '/dataexpoet/byPushHistory',
     method: 'post',
     method: 'post',
     data: data
     data: data
   })
   })

+ 47 - 0
src/api/modules/subscribe.js

@@ -75,3 +75,50 @@ export function updateMatchType (data) {
     data: data
     data: data
   })
   })
 }
 }
+
+// 订阅地区、采购单位行业修改
+export function setAreaBuyer (data) {
+  data.vSwitch = 'm'
+  data = qs.stringify(data)
+  return request({
+    baseURL: '/publicapply',
+    url: '/subscribe/update',
+    method: 'post',
+    data: data
+  })
+}
+
+// 订阅修改(换成超级订阅)
+export function setUserInfoVip (data) {
+  data.vSwitch = 'm'
+  data = qs.stringify(data)
+  return request({
+    baseURL: '/publicapply',
+    url: '/subscribe/setUserInfo',
+    method: 'post',
+    data: data
+  })
+}
+
+// 相似订阅推荐
+export function getRecommend (data) {
+  data = qs.stringify(data)
+  return request({
+    baseURL: '/member',
+    url: '/getRecomKWs',
+    method: 'post',
+    data: data
+  })
+}
+
+// 关键词近30天推送数量查询
+export function getPushCount (data) {
+  data.vt = 'm'
+  data = qs.stringify(data)
+  return request({
+    baseURL: '/publicapply',
+    url: '/subscribe/keyset/getPushCount',
+    method: 'post',
+    data: data
+  })
+}

BIN
src/assets/images/icon/question.png


+ 7 - 1
src/assets/js/selector.js

@@ -275,6 +275,12 @@ export const industryListMapExp = {
   ]
   ]
 }
 }
 
 
+// 采购单位类型
+export const buyerclassListMapExp = {
+  "党政机关事业单位": ['人大', '政协', '党委办', '组织', '宣传', '统战', '纪委', '政府办', '发改委', '财政', '教育', '科技', '工信', '民政', '民宗', '人社', '公安', '检察院', '法院', '司法', '应急管理', '军队', '自然资源', '生态环境', '住建', '市政', '城管', '交通', '水利', '农业', '气象', '文旅', '卫健委', '医疗', '学校', '档案', '体育', '政务中心', '机关事务', '国资委', '海关', '税务', '市场监管', '商务', '人行', '银保监', '证监', '审计', '出版广电', '统计', '公共资源交易', '社会团体'],
+  "企业": ['传媒', '采矿业', '电信行业', '金融业', '建筑业', '能源化工', '农林牧渔', '批发零售', '信息技术', '运输物流', '制造业', '住宿餐饮']
+}
+
 // 行业
 // 行业
 export const industryJson = [
 export const industryJson = [
   {
   {
@@ -504,4 +510,4 @@ export const branchJson = [
   '产品',
   '产品',
   '销售',
   '销售',
   '渠道'
   '渠道'
-]
+]

+ 83 - 10
src/components/article-item/ArticleItem.vue

@@ -1,6 +1,7 @@
 <template>
 <template>
-  <div class="article-item" @click="onClick">
-    <div class="a-i-left ellipsis" v-html="calcTitle"></div>
+  <div class="article-item" :class="{'style-for-gray': config.gray, 'style-for-table': config.table }">
+    <input v-if="config.collect" @click.stop class="custom-checkbox title-text-checkbox" name="bid-list" type="checkbox" :dataid="article._id">
+    <div class="a-i-left ellipsis" @click="onClick" v-html="calcTitle"></div>
     <div class="a-i-right">
     <div class="a-i-right">
       <div class="tags">
       <div class="tags">
         <span class="tag" v-if="article.area">{{ article.area }}</span>
         <span class="tag" v-if="article.area">{{ article.area }}</span>
@@ -9,15 +10,18 @@
         <span class="tag dpink" v-if="calcBudget">{{ calcBudget }}</span>
         <span class="tag dpink" v-if="calcBudget">{{ calcBudget }}</span>
       </div>
       </div>
       <div class="time-container">
       <div class="time-container">
-        <span class="el-icon-jy-time"></span>
+        <span class="el-icon-jy-time" v-if="!config.gray"></span>
         <span class="time-text">
         <span class="time-text">
           <slot name="right-time">{{ dateFromNow(article.publishtime * 1000) }}</slot>
           <slot name="right-time">{{ dateFromNow(article.publishtime * 1000) }}</slot>
         </span>
         </span>
       </div>
       </div>
+      <i class="icon-collect" @click.stop="collectChange(article, $event)" v-if="config.collect" :class="{'checked': article.collection}" :dataid="article._id"></i>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
+<script>
 
 
+</script>
 <script>
 <script>
 import { Tag } from 'element-ui'
 import { Tag } from 'element-ui'
 import { moneyUnit, dateFromNow, replaceKeyword } from '@/utils/'
 import { moneyUnit, dateFromNow, replaceKeyword } from '@/utils/'
@@ -31,9 +35,19 @@ export default {
       type: [String, Number],
       type: [String, Number],
       default: '1'
       default: '1'
     },
     },
+    config: {
+      type: Object,
+      default() {
+        return {
+          gray: false,
+          table: false,
+          collect: false
+        }
+      }
+    },
     article: {
     article: {
       type: Object,
       type: Object,
-      default () {
+      default() {
         return {
         return {
           title: '', // 标题
           title: '', // 标题
           area: '', // 区域
           area: '', // 区域
@@ -47,27 +61,48 @@ export default {
     }
     }
   },
   },
   computed: {
   computed: {
-    calcBudget () {
+    calcBudget() {
       if (this.article.budget) {
       if (this.article.budget) {
         return moneyUnit(this.article.budget)
         return moneyUnit(this.article.budget)
       } else {
       } else {
         return this.article.budget
         return this.article.budget
       }
       }
     },
     },
-    calcTitle () {
+    calcTitle() {
       const hightLightedTitle = replaceKeyword(this.article.title, this.article.matchkeys, ['<span class="highlight-text">', '</span>'])
       const hightLightedTitle = replaceKeyword(this.article.title, this.article.matchkeys, ['<span class="highlight-text">', '</span>'])
       return `${this.index}. ${hightLightedTitle}`
       return `${this.index}. ${hightLightedTitle}`
     }
     }
   },
   },
   methods: {
   methods: {
     dateFromNow,
     dateFromNow,
-    onClick () {
+    onClick() {
       this.$emit('onClick')
       this.$emit('onClick')
+    },
+    collectChange(item, event) {
+      this.$emit('onCollect', {
+        item: item,
+        event: event
+      })
     }
     }
   }
   }
 }
 }
 </script>
 </script>
-
+<style>
+.custom-toast .toast-container{
+  /* display: none; */
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  width: auto;
+  padding: 16px 32px;
+  font-size: 16px;
+  background: rgba(0, 0, 0, 0.65);
+  border-radius: 8px;
+  color: #fff;
+  transform: translateX(-50%) translateY(-50%);
+  z-index: 99;
+}
+</style>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
   $border-color: #ECECEC;
   $border-color: #ECECEC;
   @include diy-icon('time', 20, 20);
   @include diy-icon('time', 20, 20);
@@ -76,7 +111,7 @@ export default {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
-    padding: 22px 0;
+    padding: 22px 16px;
     border-bottom: 1px solid $border-color;
     border-bottom: 1px solid $border-color;
     cursor: pointer;
     cursor: pointer;
 
 
@@ -85,6 +120,44 @@ export default {
       border-top: 1px solid $border-color;
       border-top: 1px solid $border-color;
     }
     }
 
 
+    &.style-for-gray {
+
+      .icon-collect{
+        display: inline-block;
+        width: 20px;
+        height: 20px;
+        margin-left: 6px;
+        background: transparent url(https://cdn-ali.jianyu360.com/images/collect.png) center no-repeat;
+        background-size: contain;
+        cursor: pointer;
+        vertical-align: sub;
+      }
+      .icon-collect.checked{
+        background: transparent url(https://cdn-ali.jianyu360.com/images/collected.png) center no-repeat;
+        background-size: contain;
+      }
+
+      .tag,
+      .orange.tag,
+      .green.tag,
+      .dpink.tag {
+        background: #f5f5fb;
+        border: 1px solid #ececec;
+        border-radius: 4px;
+        font-size: 12px;
+        font-family: Microsoft YaHei, Microsoft YaHei-Regular;
+        font-weight: 400;
+        text-align: center;
+        color: #686868;
+        line-height: 20px;
+        height: 22px;
+        padding: 0 8px;
+      }
+    }
+    &.style-for-table {
+
+    }
+
     .a-i-left {
     .a-i-left {
       flex: 1;
       flex: 1;
     }
     }
@@ -107,7 +180,7 @@ export default {
       background-color: #2CB7CA;
       background-color: #2CB7CA;
       border-radius: 3px;
       border-radius: 3px;
       &:not(:last-of-type) {
       &:not(:last-of-type) {
-        margin-right: 4px;
+        margin-right: 8px;
       }
       }
 
 
       &.orange {
       &.orange {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 998 - 17
src/components/push-list/PushList.vue


+ 1 - 0
src/components/selector/BusinessScopeSelectorContent.vue

@@ -161,6 +161,7 @@ export default {
       &.no-more {
       &.no-more {
         height: 38px;
         height: 38px;
         overflow: hidden;
         overflow: hidden;
+        flex: 1;
       }
       }
     }
     }
     .select-group-container {
     .select-group-container {

+ 3 - 0
src/components/selector/BuyerclassSelector.vue

@@ -59,6 +59,9 @@ export default {
     onConfirm () {
     onConfirm () {
       const selected = this.getSelected()
       const selected = this.getSelected()
       this.$emit('onConfirm', selected)
       this.$emit('onConfirm', selected)
+      if (this.$refs.content.notClass) {
+        this.$emit('onOtherConfirm', this.$refs.content.notClass)
+      }
     },
     },
     onChange (selected) {
     onChange (selected) {
       this.$emit('onChange', selected)
       this.$emit('onChange', selected)

+ 47 - 4
src/components/selector/BuyerclassSelectorContent.vue

@@ -20,10 +20,36 @@
               active: cate.selected,
               active: cate.selected,
               [cate.id]: true
               [cate.id]: true
             }"
             }"
-            @click="changeCateState($event,cate)">{{ cate.name }}</button>
+            @click="changeCateState($event,cate)">{{ cate.name }}
+            </button>
+            <button
+              :class="{
+                active: notClass.selected,
+                [notClass.id]: true
+              }"
+              class="j-button-item"
+              v-if="item[0].name === '全部'"
+              @click="changeCateState($event,notClass)"
+            >匹配未分类类型</button>
+            <img class="icon-quesion" src="@/assets/images/icon/question.png" v-if="item[0].name === '全部'" @click="dialogTip = true" />
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
+    <el-dialog
+      custom-class="buyer-dialog"
+      title="匹配未分类类型"
+      :visible.sync="dialogTip"
+      width="24%"
+      center
+      :show-close="false"
+      append-to-body
+      >
+      <p style="font-size:14px;color:#1D1D1D;line-height:22px;">每条信息的采购单位类型属性由机器自动识别,会存在少数无法识别进行分类的情况。</p>
+      <p style="margin-top:4px;font-size:14px;color:#686868;line-height:22px;">注:选择全部类型时,未分类类型默认被选中,关闭无效</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button style="background: #2cb7ca;border-radius: 6px;border:0;" type="primary" @click="dialogTip = false">我知道了</el-button>
+      </span>
+    </el-dialog>
   </div>
   </div>
   <div class="selector-content" :class="{ 'no-more': !showMore }" v-else-if="selectorType === 'line'" key="s-content">
   <div class="selector-content" :class="{ 'no-more': !showMore }" v-else-if="selectorType === 'line'" key="s-content">
     <span class="action-button show-more" @click="showMore = !showMore">
     <span class="action-button show-more" @click="showMore = !showMore">
@@ -51,13 +77,15 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import { Input } from 'element-ui'
+import { Input, Dialog, Button } from 'element-ui'
 import { cateListMapExp } from '@/assets/js/selector.js'
 import { cateListMapExp } from '@/assets/js/selector.js'
 import { debounce, getRandomString } from '@/utils/'
 import { debounce, getRandomString } from '@/utils/'
 export default {
 export default {
   name: 'buyerclass-selector-content',
   name: 'buyerclass-selector-content',
   components: {
   components: {
-    [Input.name]: Input
+    [Input.name]: Input,
+    [Dialog.name]: Dialog,
+    [Button.name]: Button
   },
   },
   props: {
   props: {
     selectorType: {
     selectorType: {
@@ -86,7 +114,14 @@ export default {
       },
       },
       // indexBar数据
       // indexBar数据
       indexList: [],
       indexList: [],
-      showMore: false
+      showMore: false,
+      dialogTip: false,
+      notClass: {
+        id: 'bc-ppwfl',
+        level: 1,
+        name: '匹配未分类行业',
+        selected: true
+      }
     }
     }
   },
   },
   watch: {
   watch: {
@@ -257,9 +292,16 @@ export default {
       .select-group {
       .select-group {
         display: flex;
         display: flex;
         flex-wrap: wrap;
         flex-wrap: wrap;
+        align-items: center;
         margin: 0 16px;
         margin: 0 16px;
       }
       }
     }
     }
+    .icon-quesion{
+      width: 18px;
+      height: 18px;
+      margin-left: 2px;
+      cursor: pointer;
+    }
   }
   }
 
 
   .s-line {
   .s-line {
@@ -270,6 +312,7 @@ export default {
       width: 100%;
       width: 100%;
       padding-right: 45px;
       padding-right: 45px;
       &.no-more {
       &.no-more {
+        flex: 1;
         height: 38px;
         height: 38px;
         overflow: hidden;
         overflow: hidden;
       }
       }

+ 5 - 0
src/components/selector/IndustrySelector.vue

@@ -11,6 +11,7 @@
     <IndustrySelectorContent
     <IndustrySelectorContent
       ref="content"
       ref="content"
       @onChange="onChange"
       @onChange="onChange"
+      :dataType="dataType"
       :selectorType="selectorType"
       :selectorType="selectorType"
       :initIndustry="initIndustry"
       :initIndustry="initIndustry"
     />
     />
@@ -31,6 +32,10 @@ export default {
       type: String,
       type: String,
       default: 'card'
       default: 'card'
     },
     },
+    dataType: {
+      type: String,
+      default: 'industry'
+    },
     initIndustry: {
     initIndustry: {
       type: Object,
       type: Object,
       default () {
       default () {

+ 49 - 19
src/components/selector/IndustrySelectorContent.vue

@@ -38,15 +38,14 @@
       <span class="action-text">{{ showMore ? '收起' : '更多' }}</span>
       <span class="action-text">{{ showMore ? '收起' : '更多' }}</span>
       <span class="el-icon-arrow-down" :class="showMore ? 'rotate180' : ''"></span>
       <span class="el-icon-arrow-down" :class="showMore ? 'rotate180' : ''"></span>
     </span>
     </span>
-    <div
-      v-for="(item, index) in industryListMap"
-      :key="index"
-      class="select-group-container"
-      :class="{
-        'right-line': showMore
-      }"
-    >
-      <div class="select-group-header">
+    <div class="select-group-container" >
+      <div class="select-group-header" 
+        v-show="!showMore"
+          v-for="(item, index) in industryListMap"
+          :key="index+'A'"
+          :class="{
+            'right-line': showMore
+          }">
         <button
         <button
           class="j-button-item"
           class="j-button-item"
           :class="{
           :class="{
@@ -60,16 +59,25 @@
           @click="changeIndustryState(item)"
           @click="changeIndustryState(item)"
         >{{ item.name }}</button>
         >{{ item.name }}</button>
       </div>
       </div>
-      <div class="select-group-main" v-show="showMore">
+      <div class="select-group-header" 
+        v-show="showMore"
+          v-for="(item, index) in getIndustryListMap"
+          :key="index+'B'"
+          :class="{
+            'right-line': showMore
+          }">
         <button
         <button
-          v-for="(iitem, iindex) in item.children" :key="999-iindex"
-          class="j-button-item bgc-opacity button-level-2"
+          class="j-button-item"
           :class="{
           :class="{
-            active: iitem.selected,
-            [iitem.id]: true
+            active: item.selected,
+            [item.id]: true,
+            'button-level-0': item.level === 0,
+            'button-level-1': item.level === 1,
+            'all': item.level === 0,
+            'bgc': item.level === 1
           }"
           }"
-          @click="changeIndustryState(iitem)"
-          >{{ iitem.name }}</button>
+          @click="changeIndustryState(item)"
+        >{{ item.name }}</button>
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
@@ -77,7 +85,7 @@
 
 
 <script>
 <script>
 import { Input, Icon } from 'element-ui'
 import { Input, Icon } from 'element-ui'
-import { industryListMapExp } from '@/assets/js/selector.js'
+import { industryListMapExp, buyerclassListMapExp } from '@/assets/js/selector.js'
 import { debounce, getRandomString } from '@/utils/'
 import { debounce, getRandomString } from '@/utils/'
 export default {
 export default {
   name: 'industry-selector-content',
   name: 'industry-selector-content',
@@ -90,6 +98,10 @@ export default {
       type: String,
       type: String,
       default: 'card'
       default: 'card'
     },
     },
+    dataType: {
+      type: String,
+      default: 'industry'
+    },
     initIndustry: {
     initIndustry: {
       type: Object,
       type: Object,
       default () {
       default () {
@@ -114,11 +126,11 @@ export default {
     return {
     return {
       searchContent: '',
       searchContent: '',
       // 原始数据
       // 原始数据
-      industryListMapExp,
+      industryListMapExp: this.dataType === 'industry' ? industryListMapExp : buyerclassListMapExp,
       // 页面中循环的数据
       // 页面中循环的数据
       industryListMap: [],
       industryListMap: [],
       industryExp: {
       industryExp: {
-        name: '全部行业',
+        name: this.dataType === 'industry' ? '全部行业' : '全部',
         selected: false,
         selected: false,
         level: 0,
         level: 0,
         children: [],
         children: [],
@@ -142,6 +154,23 @@ export default {
       }
       }
     }, 300)
     }, 300)
   },
   },
+  computed: {
+    getIndustryListMap () {
+      const tempArr = []
+      this.industryListMap.forEach(v => {
+        const tempNode = v
+        tempNode.zindex = 1
+        tempArr.push(tempNode)
+        // eslint-disable-next-line no-unused-expressions
+        tempNode?.children.forEach(s => {
+          const tempS = s
+          tempS.zindex = 2
+          tempArr.push(tempS)
+        })
+      })
+      return tempArr
+    }
+  },
   created () {
   created () {
     this.initIndustryMap()
     this.initIndustryMap()
     this.setIndustryState(this.initIndustry)
     this.setIndustryState(this.initIndustry)
@@ -371,6 +400,7 @@ export default {
       flex-wrap: wrap;
       flex-wrap: wrap;
       padding-right: 45px;
       padding-right: 45px;
       &.no-more {
       &.no-more {
+        flex: 1;
         height: 38px;
         height: 38px;
         overflow: hidden;
         overflow: hidden;
       }
       }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 59 - 18
src/components/selector/InfoTypeSelectorContent.vue


+ 321 - 0
src/components/selector/PopSelector.vue

@@ -0,0 +1,321 @@
+<template>
+  <selector-card
+    class="pop-selector"
+    :cardType="selectorType"
+    @onConfirm="onConfirm"
+    @onCancel="onCancel">
+    <div slot="header" :class="{ 's-header': selectorType === 'line' }">
+      <slot name="header">关键词</slot>
+    </div>
+    <div class="p-selector--group">
+      <div class="p-selector--item all" :class="{ checked: !noSelectTop }" @click="selectAllForTop">全部</div>
+      <div class="p-selector--item" :class="{'checked': selectInfo.originMap[item].selects.length, 'select': selectInfo.popStatus.show && item === selectInfo.popStatus.key}" @click="openChildren(item, $event)" v-for="(item, index) in getTopList" :key="index">{{item}}</div>
+      <div class="p-selector--pop-group flex-c-c center" v-show="selectInfo.popStatus.show">
+        <div class="flex-r-c c-l">
+          <div class="p-selector--pop-item all" :class="{checked: selectInfo.popStatus.selectAllForPop}" @click="selectAllForPop">全部</div>
+          <div class="p-selector--pop-item" @click="toggleCheckStatus(keys)" v-for="(keys, i) in nowOpenData" :key="keys + i" :class="{ checked: keys.checked }">{{keys.key}}</div>
+        </div>
+        <div class="flex-r-c center">
+          <button class="button-submit" @click="submitChange(true)">确定</button>
+          <button class="button-submit cancel" @click="submitChange(false)">取消</button>
+        </div>
+      </div>
+    </div>
+  </selector-card>
+</template>
+
+<script>
+import SelectorCard from '@/components/selector/SelectorCard.vue'
+export default {
+  name: 'pop-selector',
+  components: {
+    SelectorCard
+  },
+  props: {
+    selectorType: {
+      type: String,
+      default: 'line' // card/line
+    },
+    singleChoice: { // 是是否单选? 只有在selectorType=line下才会生效
+      // type: Boolean,
+      default: false
+    }
+  },
+  data () {
+    return {
+      nowRow: -1,
+      selectInfo: {
+        origin: {},
+        originMap: {},
+        popMap: {},
+        popStatus: {
+          show: false,
+          key: '',
+          selectAllForPop: false
+        },
+        cacheMap: {}
+      }
+    }
+  },
+  created () {},
+  mounted () {},
+  computed: {
+    getTopList () {
+      return Object.keys(this.selectInfo.origin || {})
+    },
+    getTopListLength () {
+      const allSelectStatus = {}
+      this.getTopList.forEach(v => {
+        allSelectStatus[v] = this.selectInfo.originMap[v].selects.length
+      })
+      return allSelectStatus
+    },
+    getPopListLength () {
+      const allSelectStatus = {}
+      this.getTopList.forEach(v => {
+        allSelectStatus[v] = this.selectInfo.popMap[v].filter(v => v.checked).length
+      })
+      return allSelectStatus
+    },
+    noSelectTop () {
+      return Object.values(this.getTopListLength).filter(v => v).length
+    },
+    getSelectAllPop () {
+      return this.getPopListLength[this.selectInfo.popStatus.key] === this.nowOpenData.length
+    },
+    nowOpenData () {
+      return this.selectInfo.popMap[this.selectInfo.popStatus.key]
+    }
+  },
+  methods: {
+    initDataMap (dataMap) {
+      const tempData = {
+        origin: {},
+        popMap: {},
+        originMap: {}
+      }
+      for (const k in dataMap) {
+        tempData.origin[k] = dataMap[k]
+        tempData.popMap[k] = dataMap[k].map(v => ({ key: v, checked: false }))
+        tempData.originMap[k] = {
+          select: false,
+          selectAll: false,
+          selects: []
+        }
+      }
+      this.$set(this.selectInfo, 'origin', tempData.origin)
+      this.$set(this.selectInfo, 'originMap', tempData.originMap)
+      this.$set(this.selectInfo, 'popMap', tempData.popMap)
+      this.$nextTick(() => {
+        this.computRow()
+      })
+    },
+    submitChange (type) {
+      if (type) {
+        this.selectInfo.originMap[this.selectInfo.popStatus.key].select = true
+        this.selectInfo.originMap[this.selectInfo.popStatus.key].selects = this.nowOpenData.filter(v => v.checked).map(v => v.key)
+        const isSelectAll = Boolean(this.selectInfo.popStatus.selectAllForPop)
+        this.selectInfo.originMap[this.selectInfo.popStatus.key].selectAll = isSelectAll
+        if (isSelectAll) {
+          this.selectInfo.originMap[this.selectInfo.popStatus.key].selects = this.nowOpenData.map(v => v.key)
+        }
+        this.onChange(this.getSelected())
+      } else {
+        this.recoverPopCache()
+      }
+      this.selectInfo.popStatus.show = false
+    },
+    recoverPopCache (type = true) {
+      const tempData = {
+        popMap: {},
+        cacheMap: {}
+      }
+      if (type) {
+        tempData.popMap = Object.assign({}, this.selectInfo.cacheMap)
+        tempData.cacheMap = {}
+        this.$set(this.selectInfo, 'popMap', tempData.popMap)
+      } else {
+        tempData.cacheMap = Object.assign({}, this.selectInfo.popMap)
+      }
+      this.$set(this.selectInfo, 'cacheMap', tempData.cacheMap)
+    },
+    recoverPopSelect (key) {
+      this.selectInfo.popMap[key].forEach(v => {
+        v.checked = false
+      })
+    },
+    selectAllForTop () {
+      this.initDataMap(this.selectInfo.origin)
+      this.onChange('')
+    },
+    selectAllForPop () {
+      this.selectInfo.popStatus.selectAllForPop = !this.selectInfo.popStatus.selectAllForPop
+      if (this.selectInfo.popStatus.selectAllForPop) {
+        this.recoverPopSelect(this.selectInfo.popStatus.key)
+      }
+    },
+    toggleCheckStatus (item) {
+      this.selectInfo.popStatus.selectAllForPop = false
+      item.checked = !item.checked
+      if (this.getSelectAllPop) {
+        this.selectAllForPop()
+      }
+    },
+    computRow () {
+      $('.p-selector--group .p-selector--item').each(function () {
+        $(this).attr('data-row', parseInt($(this).position().top / 34))
+      })
+    },
+    changePosition (e) {
+      const nowRow = $(e.target).attr('data-row')
+      const tempDom = $(`.p-selector--group .p-selector--item[data-row='${parseInt(nowRow) + 1}']`)
+      if (tempDom.length) {
+        tempDom.eq(0).before($('.p-selector--pop-group'))
+      } else {
+        $('.p-selector--group').append($('.p-selector--pop-group'))
+      }
+    },
+    openChildren (item, e) {
+      this.changePosition(e)
+      this.$nextTick(() => this.changePosition(e))
+      this.selectInfo.popStatus.show = true
+      this.selectInfo.popStatus.key = item
+      this.selectInfo.popStatus.selectAllForPop = this.selectInfo.originMap[this.selectInfo.popStatus.key].selectAll
+      this.recoverPopCache(false)
+    },
+    getSelected () {
+      let tempStr = ''
+      const tempData = this.getTopListLength
+      for (const k in tempData) {
+        if (tempData[k]) {
+          if (tempStr !== '') {
+            tempStr += ','
+          }
+          tempStr += this.selectInfo.originMap[k].selects.map(v => (`${v}`)).join(',')
+        }
+      }
+      return tempStr
+    },
+    onCancel () {
+      this.$emit('onCancel')
+    },
+    onConfirm () {
+      const selectedCity = this.getSelectedCity()
+      this.$emit('onConfirm', selectedCity)
+    },
+    onChange (selected) {
+      this.$emit('onChange', selected)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.p-selector-- {
+  &group {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+  }
+  &item {
+    font-size: 14px;
+    font-family: Microsoft YaHei, Microsoft YaHei-Bold;
+    font-weight: 700;
+    text-align: CENTER;
+    color: #1d1d1d;
+    line-height: 22px;
+    background: transparent;
+    border-radius: 4px;
+    padding: 2px 6px;
+    margin-left: 6px;
+    margin-top: 8px;
+    height: 26px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    text-align: justify;
+    max-width: 14em;
+    border: 1px solid transparent;
+    cursor: pointer;
+    &.checked {
+      color: #fff;
+      background: #2cb7ca;
+      border-radius: 4px;
+    }
+    &.select {
+      background: #f5f6f7 !important;
+      color: #1d1d1d !important;
+      position: relative;
+      height: 36px;
+      border: 1px solid #ececec;
+      border-bottom-color: transparent;
+      &::after {
+        content: "";
+        position: absolute;
+        display: inline-block;
+        width: 100%;
+        height: 6px;
+        left: 0;
+        bottom: 0;
+        background: #f5f6f7;
+      }
+    }
+  }
+  &pop-group {
+    background: #f5f6f7;
+    border: 1px solid #ececec;
+    font-size: 14px;
+    font-family: Microsoft YaHei, Microsoft YaHei-Regular;
+    font-weight: 400;
+    text-align: CENTER;
+    color: #1d1d1d;
+    line-height: 22px;
+    padding: 12px;
+    border-radius: 4px;
+    width: 100%;
+    margin-top: -2px;
+    .c-l {
+      width: 100%;
+      justify-content: flex-start;
+    }
+    button {
+      width: 60px;
+      height: 26px;
+      font-size: 14px;
+      font-family: Microsoft YaHei, Microsoft YaHei-Regular;
+      font-weight: 400;
+      line-height: 22px;
+      border-radius: 2px;
+      margin-top: 20px;
+      cursor: pointer;
+      &.button-submit {
+        background: #2cb7ca;
+        border: 1px solid #2cb7ca;
+        color: #fff;
+        &.cancel {
+          margin-left: 16px;
+          color: #1d1d1d;
+          border-color: #e0e0e0;
+          background: #fff;
+        }
+      }
+    }
+    .p-selector--pop-item {
+      border-radius: 4px;
+      background: transparent;
+      margin-left: 12px;
+      padding: 2px 6px;
+      cursor: pointer;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      text-align: justify;
+      max-width: 14em;
+    }
+    .checked {
+      color: #ffffff;
+      background: #2cb7ca;
+    }
+  }
+}
+</style>

+ 5 - 0
src/components/selector/TimeSelector.vue

@@ -10,6 +10,7 @@
     </div>
     </div>
     <TimeSelectorContent
     <TimeSelectorContent
       ref="content"
       ref="content"
+      :selectorTime="selectorTime"
       :selectorType="selectorType"
       :selectorType="selectorType"
       @onChange="onChange"
       @onChange="onChange"
     />
     />
@@ -29,6 +30,10 @@ export default {
     selectorType: {
     selectorType: {
       type: String,
       type: String,
       default: 'line'
       default: 'line'
+    },
+    selectorTime: {
+      type: String,
+      default: 'default'
     }
     }
   },
   },
   data () {
   data () {

+ 79 - 22
src/components/selector/TimeSelectorContent.vue

@@ -46,6 +46,60 @@
 <script>
 <script>
 import { DatePicker } from 'element-ui'
 import { DatePicker } from 'element-ui'
 import { dateFormatter } from '@/utils/'
 import { dateFormatter } from '@/utils/'
+import moment from 'moment'
+import 'moment/locale/zh-cn'
+moment.locale('zh-cn')
+const timeSelectMap = {
+  default: [
+    {
+      name: '全部',
+      value: 'all',
+      selected: true
+    },
+    {
+      name: '最近7天',
+      value: 'lately7',
+      selected: false
+    },
+    {
+      name: '最近30天',
+      value: 'lately30',
+      selected: false
+    },
+    {
+      name: '去年',
+      value: 'lastYear',
+      selected: false
+    }
+  ],
+  sub: [
+    {
+      name: '全部',
+      value: 'all',
+      selected: true
+    },
+    {
+      name: '今天',
+      value: 'today',
+      selected: false
+    },
+    {
+      name: '昨天',
+      value: 'yesterday',
+      selected: false
+    },
+    {
+      name: '最近一周',
+      value: 'lately7',
+      selected: false
+    },
+    {
+      name: '最近一月',
+      value: 'lately30',
+      selected: false
+    }
+  ]
+}
 export default {
 export default {
   name: 'time-selector-content',
   name: 'time-selector-content',
   components: {
   components: {
@@ -55,32 +109,15 @@ export default {
     selectorType: {
     selectorType: {
       type: String,
       type: String,
       default: 'card' // card/line
       default: 'card' // card/line
+    },
+    selectorTime: {
+      type: String,
+      default: 'default'
     }
     }
   },
   },
   data () {
   data () {
     return {
     return {
-      timeSelectList: [
-        {
-          name: '全部',
-          value: 'all',
-          selected: true
-        },
-        {
-          name: '最近7天',
-          value: 'lately7',
-          selected: false
-        },
-        {
-          name: '最近30天',
-          value: 'lately30',
-          selected: false
-        },
-        {
-          name: '去年',
-          value: 'lastYear',
-          selected: false
-        }
-      ],
+      timeSelectList: timeSelectMap[this.selectorTime],
       dateTimePickerConf: {
       dateTimePickerConf: {
         type: 'date',
         type: 'date',
         editable: false,
         editable: false,
@@ -130,6 +167,16 @@ export default {
             this.clearDateTimePicker()
             this.clearDateTimePicker()
             break
             break
           }
           }
+          case 'today': {
+            this.setTimeSelectListState('today')
+            this.clearDateTimePicker()
+            break
+          }
+          case 'yesterday': {
+            this.setTimeSelectListState('yesterday')
+            this.clearDateTimePicker()
+            break
+          }
           case 'lately7': {
           case 'lately7': {
             this.setTimeSelectListState('lately7')
             this.setTimeSelectListState('lately7')
             this.clearDateTimePicker()
             this.clearDateTimePicker()
@@ -206,6 +253,16 @@ export default {
         day30: 60 * 60 * 1000 * 24 * 30
         day30: 60 * 60 * 1000 * 24 * 30
       }
       }
       switch (exact) {
       switch (exact) {
+        case 'today': {
+          t.start = moment().startOf('day').format('x')
+          t.end = moment().endOf('day').format('x')
+          break
+        }
+        case 'yesterday': {
+          t.start = moment().startOf('day').format('x') - durations.day1
+          t.end = moment().endOf('day').format('x') - durations.day1
+          break
+        }
         case 'lately7': {
         case 'lately7': {
           t.start = t.end - durations.day7
           t.start = t.end - durations.day7
           break
           break

+ 3 - 0
src/utils/bus.js

@@ -0,0 +1,3 @@
+import Vue from 'vue'
+const $bus = new Vue()
+export default $bus

+ 193 - 8
src/views/SubPush.vue

@@ -1,39 +1,146 @@
 <template>
 <template>
   <div class="page--sub-push">
   <div class="page--sub-push">
     <div class="page-content">
     <div class="page-content">
+      <div class="clearfix">
+        <span class="card-title">订阅推送</span>
+        <el-button v-if="canSubmanager" @click="goManage" class="sub-manager" type="plain" icon="el-icon-jy-edit">订阅管理
+        </el-button>
+      </div>
       <div class="border-box">
       <div class="border-box">
-        <TimeSelector  @onChange="changeTime" selectorType="line">
+        <TimeSelector ref="timeSelector" selectorTime="sub"  @onChange="changeTime" selectorType="line">
           <div slot="header">选择时间:</div>
           <div slot="header">选择时间:</div>
         </TimeSelector>
         </TimeSelector>
         <AreaSelector @onChange="changeArea" ref="areaSelector" selectorType="line">
         <AreaSelector @onChange="changeArea" ref="areaSelector" selectorType="line">
           <div slot="header">选择区域:</div>
           <div slot="header">选择区域:</div>
         </AreaSelector>
         </AreaSelector>
+        <IndustrySelector @onChange="changeIndustry"  selectorType="line">
+          <div slot="header">行业:</div>
+        </IndustrySelector>
+        <IndustrySelector @onChange="changeBuyer" dataType="buyer" selectorType="line">
+          <div slot="header">采购单位类型:</div>
+        </IndustrySelector>
+        <InfoTypeSelector class="my-line" @onChange="changeInfo" selectorType="line">
+          <div slot="header">信息类型:</div>
+        </InfoTypeSelector>
+        <PopSelector @onChange="changeKeys" ref="keySelector" selectorType="line">
+          <div slot="header">关键词:</div>
+        </PopSelector>
       </div>
       </div>
-      <push-list :filters="filters" class="m-24" ref="pushList" :showMore="false"></push-list>
+      <push-list :config="config" :filters="filters" class="m-24" ref="pushList" :showMore="false"></push-list>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+import { Button } from 'element-ui'
 import PushList from '@/components/push-list/PushList.vue'
 import PushList from '@/components/push-list/PushList.vue'
 import AreaSelector from '@/components/selector/AreaSelector.vue'
 import AreaSelector from '@/components/selector/AreaSelector.vue'
+import PopSelector from '@/components/selector/PopSelector.vue'
 import TimeSelector from '@/components/selector/TimeSelector.vue'
 import TimeSelector from '@/components/selector/TimeSelector.vue'
+import IndustrySelector from '@/components/selector/IndustrySelector.vue'
+import InfoTypeSelector from '@/components/selector/InfoTypeSelector.vue'
+import { mapState } from 'vuex'
+
 export default {
 export default {
   name: 'SubPush',
   name: 'SubPush',
   components: {
   components: {
     PushList,
     PushList,
     AreaSelector,
     AreaSelector,
-    TimeSelector
+    PopSelector,
+    TimeSelector,
+    IndustrySelector,
+    InfoTypeSelector,
+    [Button.name]: Button
   },
   },
   data () {
   data () {
     return {
     return {
+      config: {
+        gray: true,
+        table: true,
+        collect: true
+      },
       filters: {
       filters: {
         area: '',
         area: '',
-        time: ''
+        time: '',
+        selectTime: '',
+        city: '',
+        buyerclass: '',
+        subtype: '',
+        subscopeclass: '',
+        key: '',
+        vt: 'm',
+        exportNum: ''
       }
       }
     }
     }
   },
   },
+  mounted () {
+    this.initQuery()
+    this.initKeyMap()
+  },
+  watch: {
+    bigKeywordsData: function () {
+      this.initKeyMap()
+    }
+  },
   methods: {
   methods: {
+    initKeyMap () {
+      console.log(this.bigKeywordsData, 'ss')
+      const tempKeys = {}
+      // eslint-disable-next-line no-unused-expressions
+      this.bigKeywordsData?.member_jy?.a_items.forEach(v => {
+        let tempArr = []
+        v.a_key.forEach(s => {
+          let tempList = s?.key || []
+          if (s?.appendkey && s?.key) {
+            tempList = [s?.key + ' ' + s?.appendkey.join(' ')]
+          }
+          tempArr = tempArr.concat(tempList)
+        })
+        tempKeys[v.s_item] = tempArr
+      })
+      console.log(tempKeys, 'keys')
+      this.$refs.keySelector.initDataMap(tempKeys)
+    },
+    initQuery () {
+      const tempTime = this.$refs.timeSelector.getState()
+      let time = tempTime.exact
+      if (tempTime.exact === 'exact') {
+        time = tempTime.start / 1000 + '_' + tempTime.end / 1000
+      }
+      this.filters.selectTime = time
+      this.filters.time = time
+      this.doQuery()
+    },
+    formatIndustryMap (item) {
+      const tempArr = []
+      Object.keys(item).forEach(v => {
+        const tempItem = item[v]
+        if (Array.isArray(tempItem)) {
+          tempItem.forEach(vv => {
+            tempArr.push(`${v}_${vv}`)
+          })
+        }
+      })
+      return tempArr
+    },
+    changeIndustry (item) {
+      this.filters.subscopeclass = this.formatIndustryMap(item).join(',')
+      this.doQuery()
+    },
+    changeBuyer (item) {
+      this.filters.buyerclass = this.formatIndustryMap(item).map(v => v.split('_')[1]).join(',')
+      this.doQuery()
+    },
+    changeInfo (item) {
+      this.filters.subtype = item.join(',')
+      this.doQuery()
+    },
+    doQuery () {
+      this.$refs.pushList.doQuery(this.filters)
+    },
+    goManage () {
+      this.$router.push('/set_subscribe/config')
+    },
     changeTime (item) {
     changeTime (item) {
       let time = parseInt(item.start / 1000)
       let time = parseInt(item.start / 1000)
       if (item.end) {
       if (item.end) {
@@ -41,10 +148,16 @@ export default {
       }
       }
       if (item.start === 0 && item.end === 0) {
       if (item.start === 0 && item.end === 0) {
         this.filters.time = ''
         this.filters.time = ''
+        this.filters.selectTime = ''
       } else {
       } else {
         this.filters.time = time
         this.filters.time = time
+        this.filters.selectTime = time
       }
       }
-      this.$refs.pushList.doQuery(this.filters)
+      this.doQuery()
+    },
+    changeKeys (item) {
+      this.filters.key = item.replace(/\s/g, ',')
+      this.doQuery()
     },
     },
     changeArea (item) {
     changeArea (item) {
       const area = Object.keys(item).map(v => {
       const area = Object.keys(item).map(v => {
@@ -54,17 +167,89 @@ export default {
         return v
         return v
       })
       })
       this.filters.area = area.join(',')
       this.filters.area = area.join(',')
-      this.$refs.pushList.doQuery(this.filters)
+      this.doQuery()
     }
     }
   },
   },
-  computed: {},
-  created () {}
+  computed: {
+    canSubmanager () {
+      return !this.$store.state.user.info?.isSubCount
+    },
+    ...mapState({
+      bigKeywordsData: state => state.user.bigKeywordsData
+    })
+  },
+  async created () {
+    await this.$store.dispatch('user/getKeywordsList')
+  }
 }
 }
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
+  @include diy-icon('edit', 20, 20);
   .page--sub-push {
   .page--sub-push {
     background-color: #fff;
     background-color: #fff;
+    ::v-deep {
+      .selector-card.s-line .selector-card-header {
+        min-width: 114px;
+      }
+      .selector-card.industry-selector  .selector-card-content .j-button-item.active {
+        color: #fff;
+        background-color: #2CB7CA;
+      }
+      .s-line {
+        .select-group-container.right-line {
+          &:last-child {
+            &::after {
+              content: unset !important;
+            }
+          }
+        }
+      }
+      .my-line.s-line {
+        .select-group-container {
+          margin-left: 0;
+        }
+         .select-group-container .select-group-main {
+           position: relative;
+           &::after {
+             content: "";
+             position: absolute;
+             right: -1px;
+             top: 50%;
+             width: 1px;
+             height: 16px;
+             background-color: #E3E4E6;
+             margin-top: -8px;
+           }
+         }
+      }
+    }
+    .card-title {
+      font-size: 24px;
+      color: #1d1d1d;
+      line-height: 36px;
+    }
+    .clearfix .sub-manager {
+      float: right;
+      margin-bottom: 24px;
+    }
+
+    .sub-manager {
+      display: flex;
+      align-items: center;
+      padding: 8px 16px;
+      font-size: 14px;
+      line-height: 24px;
+      color: #1d1d1d;
+      border-color: #E0E0E0;
+
+      &.el-button:focus,
+      &.el-button:hover {
+        color: inherit;
+        background-color: inherit;
+      }
+    }
+
     .page-content {
     .page-content {
       width: 1200px;
       width: 1200px;
       margin: 0 auto;
       margin: 0 auto;

+ 1 - 2
src/views/portrayal/EntSearchPortrayal.vue

@@ -273,7 +273,6 @@ export default {
       return this.info.memberStatus > 0 && this.info.power.indexOf(4) !== -1
       return this.info.memberStatus > 0 && this.info.power.indexOf(4) !== -1
     },
     },
     showProActive () {
     showProActive () {
-      console.log(this.powerInfo.vip)
       return this.powerInfo.vip > 1 || (this.info.memberStatus > 0 && this.info.power.indexOf(13) !== -1)
       return this.powerInfo.vip > 1 || (this.info.memberStatus > 0 && this.info.power.indexOf(13) !== -1)
     },
     },
     // 是否显示4
     // 是否显示4
@@ -313,7 +312,7 @@ export default {
   // 路由组件内导航守卫
   // 路由组件内导航守卫
   beforeRouteEnter (to, from, next) {
   beforeRouteEnter (to, from, next) {
     next(vm => {
     next(vm => {
-      console.log(vm.info.memberStatus, vm.info.vipStatus, vm.info.viper, vm.pagePowerInfo.usage, vm.pagePowerInfo.total)
+      // console.log(vm.info.memberStatus, vm.info.vipStatus, vm.info.viper, vm.pagePowerInfo.usage, vm.pagePowerInfo.total)
       if (vm.info.memberStatus > 0 && vm.info.vipStatus > 1 && vm.info.viper && vm.pagePowerInfo.usage >= vm.pagePowerInfo.total) {
       if (vm.info.memberStatus > 0 && vm.info.vipStatus > 1 && vm.info.viper && vm.pagePowerInfo.usage >= vm.pagePowerInfo.total) {
         vm.$router.push({
         vm.$router.push({
           path: `/ent_portrait/${encodeURIComponent(to.params.eId)}`,
           path: `/ent_portrait/${encodeURIComponent(to.params.eId)}`,

+ 1 - 1
src/views/reportData/pageWeek.vue

@@ -64,7 +64,7 @@
         </div>
         </div>
         <!-- 本周新增招标项目规模 -->
         <!-- 本周新增招标项目规模 -->
         <div class="newAdd_scale" v-if="newScale.show">
         <div class="newAdd_scale" v-if="newScale.show">
-          <div class="chart-title">本周新增标项目规模</div>
+          <div class="chart-title">本周新增标项目规模</div>
           <BarLineChart :datas="newScale.data" :options="newScale.options"></BarLineChart>
           <BarLineChart :datas="newScale.data" :options="newScale.options"></BarLineChart>
         </div>
         </div>
         <!-- 项目金额排行榜 -->
         <!-- 项目金额排行榜 -->

+ 149 - 11
src/views/subscribe/Config.vue

@@ -3,20 +3,39 @@
     <!-- 订阅设置 -->
     <!-- 订阅设置 -->
     <sub-config :datas="setData" @update="getUpdate"></sub-config>
     <sub-config :datas="setData" @update="getUpdate"></sub-config>
     <!-- 关键词设置 -->
     <!-- 关键词设置 -->
-    <key-config ref="keyConfigRef" :datas="setData" @updateKey="updateKeyWordsApi"></key-config>
+    <key-config :datas="setData" @update="getUpdate"></key-config>
     <!-- 关键词列表 -->
     <!-- 关键词列表 -->
-    <key-list v-if="setData.keyList.length > 0" :datas="setData" @updateKey="updateKeyWordsApi"></key-list>
+    <key-list v-if="setData.keyList.length > 0" ref="keyConfigRef" :datas="setData"></key-list>
+    <el-dialog
+      custom-class="small-dialog"
+      title="关键词升级提示"
+      :visible.sync="dialogUpdate"
+      width="380px"
+      center
+      :show-close="false"
+      append-to-body
+      >
+      <p class="dialog-update-black">“附加词”已整合至“关键词”中,添加多个关键词用空格隔开即可,并可以灵活选择匹配模式(精准或模糊)</p>
+      <p class="dialog-update-gray">示例说明:<br> 调整前:关键词:软件 <br> <em style="opacity:0;">占位符:</em>附加词:系统 <br>调整后:关键词:软件 系统 <br> <em style="opacity:0;">占位符:</em>匹配模式:精准(同时包含所有关键词才推送)</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button class="know-btn" type="primary" @click="updateTipFn">我知道了</el-button>
+      </span>
+    </el-dialog>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+import { Dialog, Button } from 'element-ui'
 import SubConfig from './components/key/SubConfig'
 import SubConfig from './components/key/SubConfig'
 import KeyConfig from './components/key/KeyConfig'
 import KeyConfig from './components/key/KeyConfig'
-import KeyList from './components/key/KeyList'
-import { getBigMemberInfo, updateKey } from '@/api/modules'
+import KeyList from './components/key/List'
+import { getBigMemberInfo, updateKey, setUserInfoVip } from '@/api/modules'
+import $bus from '@/utils/bus'
 export default {
 export default {
   name: 'config',
   name: 'config',
   components: {
   components: {
+    [Dialog.name]: Dialog,
+    [Button.name]: Button,
     KeyList,
     KeyList,
     SubConfig,
     SubConfig,
     KeyConfig
     KeyConfig
@@ -45,7 +64,8 @@ export default {
         maxCount: 0
         maxCount: 0
       },
       },
       // 子账号业务范围
       // 子账号业务范围
-      scope: []
+      scope: [],
+      dialogUpdate: false
     }
     }
   },
   },
   computed: {
   computed: {
@@ -59,7 +79,19 @@ export default {
     }
     }
     this.getBigInfo()
     this.getBigInfo()
   },
   },
+  mounted () {
+    $bus.$on('updateKey', (data) => {
+      // this.updateKeyWordsApi(data)
+      this.getBigInfo()
+    })
+  },
   methods: {
   methods: {
+    // 排序
+    sortData (arr) {
+      return arr.sort((a, b) => {
+        return b.updatetime - a.updatetime
+      })
+    },
     async getBigInfo () {
     async getBigInfo () {
       const provinceArr = []
       const provinceArr = []
       let areaArr = []
       let areaArr = []
@@ -72,8 +104,19 @@ export default {
           this.setData.buyClassStr = '全部'
           this.setData.buyClassStr = '全部'
           this.setData.infoTypeStr = '全部类型'
           this.setData.infoTypeStr = '全部类型'
           this.setData.projectmatch = 0
           this.setData.projectmatch = 0
-          this.setData.buyclassmatch = 0
+          this.setData.buyclassmatch = 1
           this.setData.mathway = 1
           this.setData.mathway = 1
+          this.setData.keyList = [{
+            a_key: [],
+            s_item: '未分类'
+          }]
+          setUserInfoVip({
+            pageType: 'keyWords',
+            actionType: 'SC',
+            classify_name: '未分类'
+          }).then((res) => {
+            this.getBigInfo()
+          })
         } else {
         } else {
           // 项目匹配
           // 项目匹配
           this.setData.projectmatch = res.member_jy.i_projectmatch
           this.setData.projectmatch = res.member_jy.i_projectmatch
@@ -81,6 +124,10 @@ export default {
           this.setData.buyclassmatch = res.member_jy.i_matchbuyerclass_other
           this.setData.buyclassmatch = res.member_jy.i_matchbuyerclass_other
           // 匹配方式
           // 匹配方式
           this.setData.mathway = res.member_jy.i_matchway ? res.member_jy.i_matchway : 1
           this.setData.mathway = res.member_jy.i_matchway ? res.member_jy.i_matchway : 1
+          // 是否提示升级
+          if (!res.member_jy.b_keytip) {
+            this.dialogUpdate = true
+          }
           // 区域
           // 区域
           if (res.member_jy.o_area && Object.keys(res.member_jy.o_area).length > 0) {
           if (res.member_jy.o_area && Object.keys(res.member_jy.o_area).length > 0) {
             for (const key in res.member_jy.o_area) {
             for (const key in res.member_jy.o_area) {
@@ -110,10 +157,10 @@ export default {
           }
           }
           // 采购单位行业
           // 采购单位行业
           if (res.member_jy.a_buyerclass && JSON.stringify(res.member_jy.a_buyerclass) !== '[]') {
           if (res.member_jy.a_buyerclass && JSON.stringify(res.member_jy.a_buyerclass) !== '[]') {
-            this.setData.buyClassStr = res.member_jy.a_buyerclass.join('、')
+            this.setData.buyClassStr = res.member_jy.a_buyerclass.join('、') + (res.member_jy.i_matchbuyerclass_other ? '、匹配未分类行业' : '')
             this.setData.buyClassArr = res.member_jy.a_buyerclass
             this.setData.buyClassArr = res.member_jy.a_buyerclass
           } else {
           } else {
-            this.setData.buyClassStr = '全部'
+            this.setData.buyClassStr = '全部' + (res.member_jy.i_matchbuyerclass_other ? '、匹配未分类类型' : '')
           }
           }
           // 信息类型
           // 信息类型
           if (res.member_jy.a_infotype && JSON.stringify(res.member_jy.a_infotype) !== '[]') {
           if (res.member_jy.a_infotype && JSON.stringify(res.member_jy.a_infotype) !== '[]') {
@@ -124,16 +171,45 @@ export default {
           }
           }
           // 关键词
           // 关键词
           if (res.member_jy.a_items && JSON.stringify(res.member_jy.a_items) !== '[]') {
           if (res.member_jy.a_items && JSON.stringify(res.member_jy.a_items) !== '[]') {
+            res.member_jy.a_items.forEach((v, index) => {
+              if (v) {
+                v.groupIndex = index
+                v.showForm = false
+                if (!v.updatetime) {
+                  v.updatetime = 0
+                }
+                if (v.a_key && v.a_key.length > 0) {
+                  v.a_key.forEach((s, j) => {
+                    s.keyindex = j
+                    s.showForm = false
+                    if (!s.updatetime) {
+                      s.updatetime = 0
+                    }
+                  })
+                }
+              }
+            })
+            // console.log(res.member_jy.a_items, '增加索引后的数组')
             this.setData.keyList = res.member_jy.a_items
             this.setData.keyList = res.member_jy.a_items
           } else {
           } else {
-            this.setData.keyList = []
+            this.setData.keyList = [{
+              a_key: [],
+              s_item: '未分类'
+            }]
+            setUserInfoVip({
+              pageType: 'keyWords',
+              actionType: 'SC',
+              classify_name: '未分类'
+            }).then((res) => {
+              this.getBigInfo()
+            })
           }
           }
         }
         }
       } else {
       } else {
         console.log('状态值为:' + res.status)
         console.log('状态值为:' + res.status)
       }
       }
     },
     },
-    // 子组件通知父组件更新关键词接口
+    // 子组件通知父组件更新关键词接口(大会员为全量提交,关键词修改使用超级订阅关键词接口提交)
     updateKeyWordsApi (data) {
     updateKeyWordsApi (data) {
       updateKey({
       updateKey({
         a_items: data
         a_items: data
@@ -143,7 +219,26 @@ export default {
             type: 'success',
             type: 'success',
             message: '操作成功'
             message: '操作成功'
           })
           })
-          this.$refs.keyConfigRef.clearInputData()
+          this.getBigInfo()
+        } else {
+          this.$message({
+            type: 'error',
+            message: res.error_msg || '操作失败'
+          })
+          this.getBigInfo()
+        }
+      })
+    },
+    // 除地区、采购单位以外设置修改接口
+    setUserInfoFn (data) {
+      setUserInfoVip({
+        pageType: data
+      }).then((res) => {
+        if (res.data.status === 1) {
+          this.$message({
+            type: 'success',
+            message: '操作成功'
+          })
           this.getBigInfo()
           this.getBigInfo()
         } else {
         } else {
           this.$message({
           this.$message({
@@ -156,6 +251,14 @@ export default {
     },
     },
     getUpdate () {
     getUpdate () {
       this.getBigInfo()
       this.getBigInfo()
+    },
+    // 关闭升级提示按钮
+    updateTipFn () {
+      setUserInfoVip({
+        pageType: 'keytip'
+      }).then(res => {
+        this.dialogUpdate = false
+      })
     }
     }
   }
   }
 
 
@@ -163,6 +266,41 @@ export default {
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
+.buyer-dialog{
+  border-radius: 8px!important;
+}
+.sub-dialog{
+  border-radius: 5px;
+}
+.small-dialog{
+  border-radius: 8px;
+  .el-dialog__title{
+    color: #1d1d1d;
+  }
+  .dialog-text{
+    margin-top:4px;
+    font-size:14px;
+    color:#686868;
+    line-height:22px;
+    text-align:center;
+  }
+  .know-btn{
+    background: #2cb7ca;
+    border-radius: 6px;
+    border:0;
+  }
+  .dialog-update-black{
+    color: #1D1D1D;
+    line-height: 22px;
+    text-align: justify;
+  }
+  .dialog-update-gray{
+    margin-top: 20px;
+    color: #686868;
+    line-height: 22px;
+    text-align: justify;
+  }
+}
 .config{
 .config{
   width: 1080px;
   width: 1080px;
   margin: 32px auto 56px;
   margin: 32px auto 56px;

+ 54 - 6
src/views/subscribe/Scope.vue

@@ -1,9 +1,7 @@
 <template>
 <template>
   <div class="scope-config">
   <div class="scope-config">
-    <!-- 关键词设置 -->
-    <ScopeAdd ref="keyConfigRef" :datas="setData" @updateKey="updateKeyWordsApi"></ScopeAdd>
     <!-- 关键词列表 -->
     <!-- 关键词列表 -->
-    <ScopeList v-if="setData.keyList.length > 0" :datas="setData" @updateKey="updateKeyWordsApi"></ScopeList>
+    <ScopeList :datas="setData" @updateKey="updateKeyWordsApi"></ScopeList>
     <div>
     <div>
       <div class="reset-tips">新增的关键词设置,将在登出时还原</div>
       <div class="reset-tips">新增的关键词设置,将在登出时还原</div>
       <button type="button" class="save-btn" @click="saveSetting">保存设置</button>
       <button type="button" class="save-btn" @click="saveSetting">保存设置</button>
@@ -12,14 +10,13 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import ScopeAdd from './components/scope/Add'
 import ScopeList from './components/scope/List'
 import ScopeList from './components/scope/List'
 import { getParam } from '@/utils/'
 import { getParam } from '@/utils/'
 import { mapActions, mapState } from 'vuex'
 import { mapActions, mapState } from 'vuex'
+import $bus from '@/utils/bus'
 export default {
 export default {
   name: 'scope',
   name: 'scope',
   components: {
   components: {
-    ScopeAdd,
     ScopeList
     ScopeList
   },
   },
   data () {
   data () {
@@ -42,6 +39,13 @@ export default {
       isDeleteAllBuyClass: state => state.user.isDeleteAllBuyClass
       isDeleteAllBuyClass: state => state.user.isDeleteAllBuyClass
     })
     })
   },
   },
+  watch: {
+    'setData.list': function (newVal) {
+      if (newVal) {
+        this.sortData(newVal)
+      }
+    }
+  },
   created () {
   created () {
     const params = decodeURIComponent(getParam('title'))
     const params = decodeURIComponent(getParam('title'))
     if (params) {
     if (params) {
@@ -50,16 +54,30 @@ export default {
     }
     }
     this.getBigInfo()
     this.getBigInfo()
   },
   },
+  mounted () {
+    $bus.$on('updateScope', (data) => {
+      this.setData.keyList = data
+    })
+  },
   methods: {
   methods: {
     ...mapActions('user', ['getKeywordsList']),
     ...mapActions('user', ['getKeywordsList']),
     // 从接口中取数据 执行业务范围页面逻辑
     // 从接口中取数据 执行业务范围页面逻辑
     async getBigInfo () {
     async getBigInfo () {
       const state = this.setData.from ? this.buyClass : this.scope
       const state = this.setData.from ? this.buyClass : this.scope
+      console.log(state, 'state')
+      state.forEach((v, i) => {
+        v.showForm = false
+        v.iidex = i
+        if (!v.updatetime) {
+          v.updatetime = 0
+        }
+      })
       const isClear = this.setData.from ? this.isDeleteAllBuyClass : this.isDeleteAllScope
       const isClear = this.setData.from ? this.isDeleteAllBuyClass : this.isDeleteAllScope
       if (state || isClear) {
       if (state || isClear) {
         this.setData.keyList = state
         this.setData.keyList = state
       } else {
       } else {
         this.getKeywordsList(this.setData.from).then((res) => {
         this.getKeywordsList(this.setData.from).then((res) => {
+          console.log(res, 'res')
           // eslint-disable-next-line
           // eslint-disable-next-line
           this.setData.keyList = res ? res : []
           this.setData.keyList = res ? res : []
         })
         })
@@ -80,7 +98,7 @@ export default {
     // 子组件通知父组件更新关键词接口
     // 子组件通知父组件更新关键词接口
     updateKeyWordsApi (data) {
     updateKeyWordsApi (data) {
       console.log(data, '子组件传来的修改完缓存里的数据')
       console.log(data, '子组件传来的修改完缓存里的数据')
-      this.$refs.keyConfigRef.clearInputData()
+      // this.$refs.keyConfigRef.clearInputData()
       this.setData.keyList = data
       this.setData.keyList = data
     },
     },
     // 保存设置
     // 保存设置
@@ -136,6 +154,36 @@ export default {
     text-align: center;
     text-align: center;
     cursor: pointer;
     cursor: pointer;
     border: 0;
     border: 0;
+    &:disabled{
+      opacity: 0.5;
+      cursor: not-allowed;
+    }
+  }
+}
+.small-dialog{
+  border-radius: 8px;
+  .dialog-text{
+    margin-top:4px;
+    font-size:14px;
+    color:#686868;
+    line-height:22px;
+    text-align:center;
+  }
+  .know-btn{
+    background: #2cb7ca;
+    border-radius: 6px;
+    border:0;
+  }
+  .dialog-update-black{
+    color: #1D1D1D;
+    line-height: 22px;
+    text-align: justify;
+  }
+  .dialog-update-gray{
+    margin-top: 20px;
+    color: #686868;
+    line-height: 22px;
+    text-align: justify;
   }
   }
 }
 }
 .sub-dialog .el-dialog__header,.sub-dialog .el-dialog__body{
 .sub-dialog .el-dialog__header,.sub-dialog .el-dialog__body{

+ 587 - 0
src/views/subscribe/components/key/Edit.vue

@@ -0,0 +1,587 @@
+<template>
+  <div class="edit-form">
+    <div class="input-box">
+      <div class="input-box-title">{{title}}关键词组</div>
+      <div class="item">
+        <div class="item-label">关键词:</div>
+        <div class="item-value">
+          <el-input
+            type="textarea"
+            autosize
+            resize="none"
+            placeholder="请输入关键词,多个关键词用空格隔开,例如:税务局 软件"
+            debounce="600"
+            maxlength="200"
+            @input="keywordsInput"
+            @blur="keywordsBlur"
+            v-model="cur.key">
+          </el-input>
+        </div>
+      </div>
+      <div class="item" v-if="sameWordsList && sameWordsList.length > 0">
+        <div class="item-label"></div>
+        <div class="item-value">
+          <div class="recommend">
+            <div class="recommend-title">
+              <span>相似订阅推荐</span>
+              <span class="batch-btn" v-if="sameWordsList && sameWordsList.length > 9" @click="getRecommendFn"><i class="el-icon-refresh"></i> 换一批</span>
+            </div>
+            <div class="recommend-main">
+              <div class="r-list"
+              :class="{active: s.selected}"
+              v-for="(s, j) in sameWordsList.slice(0, 9)"
+              :key="'00' + j"
+              @click="addSameItem($event, s)">{{s.word}}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="item">
+        <div class="item-label">匹配模式:</div>
+        <div class="item-value">
+          <el-radio-group v-model="cur.matchway" @change="chooseMatchWay($event)">
+            <el-radio name="matchway" :label="1">模糊<span class="math-tips">(任意1个关键词匹配成功即推送)</span></el-radio>
+            <el-radio  name="matchway" :label="0">精准<span class="math-tips">(同时包含所有关键词才推送)</span></el-radio>
+          </el-radio-group>
+        </div>
+      </div>
+      <div class="item" v-if="showNotWay" key="notway">
+        <div class="item-label">排除词:</div>
+        <div class="item-value">
+          <el-input
+            type="textarea"
+            autosize
+            resize="none"
+            placeholder="请输入排除词,多个排除词用空格隔开,不希望接收,与关键词互斥"
+            @blur="notKeyBlur"
+            v-model="cur.notkey">
+          </el-input>
+        </div>
+      </div>
+      <div class="item" v-else key="notway">
+        <div class="item-label"></div>
+        <div class="item-value">
+          <div class="add-words-btn" @click="showNotWay = true">+添加排除词(不希望接收,与关键词互斥)</div>
+        </div>
+      </div>
+      <div class="little-tips" v-show="littleTip">当前匹配信息过少,请适当修改关键词</div>
+      <div>
+        <button type="button" :disabled="keyDisabled" class="confirm-btn" @click="submitKeywords">保存<span style="font-size:12px;"> (近3个月内共匹配{{pushCount}}条信息)</span></button>
+      </div>
+    </div>
+    <!-- 关键词重复提示 -->
+    <el-dialog
+      custom-class="small-dialog"
+      title="新增关键词组"
+      :visible.sync="dialog.repeat"
+      width="380px"
+      center
+      :show-close="false"
+      append-to-body
+      >
+      <p class="dialog-text">该组关键词已存在,请勿重复添加</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button class="know-btn" type="primary" @click="dialog.repeat = false">我知道了</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { Input, Button, RadioGroup, Radio, Dialog } from 'element-ui'
+import { getPushCount, getRecommend } from '@/api/modules'
+import { debounce } from '@/utils/'
+import $bus from '@/utils/bus'
+/* eslint-disable */
+Array.prototype.indexOf = function (val) {
+  for (var i = 0; i < this.length; i++) {
+    if (this[i] === val) return i
+  }
+  return -1
+}
+Array.prototype.remove = function (val) {
+  var index = this.indexOf(val)
+  if (index > -1) {
+    this.splice(index, 1)
+  }
+}
+/* eslint-disable */
+export default {
+  name: 'key-config',
+  props: {
+    datas: Array,
+    title: String,
+    keywords: Object,
+    className: '',
+    classIndex: Number,
+    keyIndex: Number
+  },
+  components: {
+    [Input.name]: Input,
+    [Button.name]: Button,
+    [RadioGroup.name]: RadioGroup,
+    [Radio.name]: Radio,
+    [Dialog.name]: Dialog
+  },
+  data () {
+    return {
+      // 当前输入框的数据
+      cur: {
+        classify: '',
+        key: '',
+        appendkey: '',
+        notkey: '',
+        matchway: 1
+      },
+      dialog: {
+        limit: false,
+        repeat: false
+      },
+      pushCount: 0,
+      showNotWay: false, // 是否展示添加排除词输入框
+      sameWordsList: [] // 相似推荐数据
+    }
+  },
+  computed: {
+    keyDisabled () {
+      return !this.cur.key
+    },
+    // 匹配信息过少提示
+    littleTip () {
+      return this.pushCount < 30 && this.cur.key !== ''
+    }
+  },
+  watch: {
+    'className': function (newVal) {
+      this.className = newVal
+      this.getPropsData()
+    }
+  },
+  mounted () {
+    this.getPropsData()
+  },
+  methods: {
+    getPropsData () {
+      if (this.keywords) {
+        if (this.keywords.appendkey) {
+          this.cur.key = this.keywords.key.join(' ') + ' ' + this.keywords.appendkey.join(' ')
+        } else {
+          this.cur.key = this.keywords.key.join(' ')
+        }
+        if (this.keywords.notkey && this.keywords.notkey.length > 0) {
+          this.showNotWay = true
+          this.cur.notkey = this.keywords.notkey ? this.keywords.notkey.join(' ') : null
+        }
+        if (this.keywords.matchway) {
+          this.cur.matchway = 1
+        } else {
+          this.cur.matchway = 0
+        }
+      }
+      if (this.className) {
+        this.cur.classify = this.className
+      }
+      if (this.title === '修改') {
+        this.getPushCountFn()
+      }
+    },
+    // 关键词推荐数量查询
+    keywordsInput () {
+      if (this.cur.key) {
+        setTimeout(() => {
+          this.getRecommendFn()
+        }, 800)
+        setTimeout(() => {
+          this.getPushCountFn()
+        }, 2000)
+      }
+    },
+    // 关键词输入框失去焦点 查询推送数量
+    keywordsBlur () {
+      if (this.cur.key) {
+        this.getPushCountFn()
+      }
+    },
+    // 排除词输入框失去焦点 查询推送数量
+    notKeyBlur () {
+      if (this.cur.notkey) {
+        this.getPushCountFn()
+      }
+    },
+    // 关键词近3个月推送数量查询
+    getPushCountFn: debounce(function () {
+      if (!this.cur.key) return
+      getPushCount({
+        key: this.cur.key,
+        notkey: this.cur.notkey,
+        matchway: this.cur.matchway
+      }).then(res => {
+        this.pushCount = res.count
+      })
+    }, 500),
+    // 获取相似项目推荐
+    getRecommendFn: debounce(function () {
+      getRecommend({
+        count: 20,
+        value: this.cur.key
+      }).then(res => {
+        this.sameWordsList = []
+        const arrList = []
+        if (res.length > 0) {
+          res.forEach(item => {
+            item.selected = false
+          })
+          const m = this.cur.key.split(' ')
+          m.forEach(v => {
+            res.forEach(s => {
+              if (v === s.word) {
+                s.selected = true
+              }
+            })
+          })
+        }
+        this.sameWordsList = res || []
+      })
+    }, 500),
+    chooseMatchWay () {
+      this.getPushCountFn()
+    },
+    // 添加订阅推荐到输入框
+    addSameItem (e, item) {
+      item.selected = !item.selected
+      const m = this.cur.key.split(' ')
+      if (item.selected) {
+        m.push(item.word)
+        this.cur.key = m.join(' ')
+      } else {
+        m.remove(item.word)
+        this.cur.key = m.join(' ')
+      }
+      setTimeout(() => {
+        this.getPushCountFn()
+      }, 2000)
+    },
+    getNotKeyArr () {
+      const notKey = this.cur.notkey.trim()
+      if (!notKey) return []
+      return notKey.split(/\s+/)
+    },
+    // 关键词接口
+    commonKeyWordsApi (classIndex, keyIndex) {
+      const datas = {
+        pageType: 'keyWords',
+        actionType: 'SK', // 操作类型 SK:保存关键词; DK:删除关键词; SC:保存分类 DC:删除分类
+        classify_index: classIndex, // 分类索引
+        classify_name: this.cur.classify, // 分类名称
+        kws_index: keyIndex, // 关键词索引
+        kws_name: this.cur.key, // 关键词名称
+        not_kws: this.getNotKeyArr(), // 排除词
+        match_way: this.cur.matchway, // 关键词匹配
+        vSwitch: 'm'
+      }
+      // 用jq axios数组参数无法序列化
+      $.ajax({
+        url: '/publicapply/subscribe/setUserInfo',
+        type: 'POST',
+        data: datas,
+        traditional: true,
+        success: (res) =>{
+          if (res.flag) {
+            $bus.$emit('updateKey')
+          } else {
+            if (res.msg.indexOf('存在') > -1) {
+              this.dialog.repeat = true
+            } else {
+              this.$message(res.msg)
+            }
+          }
+        }
+      })
+    },
+    // 提交关键词
+    submitKeywords () {
+      // console.log(this.classIndex, this.keyIndex, this.datas)
+      if (!this.datas) {
+        this.commonKeyWordsApi(this.classIndex, this.keyIndex)
+      } else {
+        const isChange = this.title === '修改' ? this.getIsChange() : false
+        if (isChange) {
+          this.keywords.showForm = false
+        } else {
+          this.commonKeyWordsApi(this.classIndex, this.keyIndex)
+        }
+      }
+    },
+    // 判断有无更改
+    getIsChange () {
+      const oldKey = JSON.stringify(this.keywords.key)
+      const oldAppendKey = JSON.stringify(this.keywords.appendkey ? this.keywords.appendkey : null)
+      const oldMatchWay = this.keywords.matchway
+      const oldNotKey = JSON.stringify(this.keywords.notkey ? this.keywords.notkey : null)
+      const newNotKey = JSON.stringify(this.cur.notkey ? this.cur.notkey.split(' ') : null)
+      const newMatchWay = this.cur.matchway
+      const newKeyArr = this.cur.key.split(' ')
+      const newKey = JSON.stringify(newKeyArr[0].split(','))
+      const temp = newKeyArr.slice(1, newKeyArr.length)
+      const newAppendKey = JSON.stringify(temp && temp.length > 0 ? temp : null)
+      // console.log(oldKey, newKey, oldAppendKey, newAppendKey, oldNotKey, newNotKey, oldMatchWay, newMatchWay)
+      // console.log(oldKey === newKey, oldAppendKey === newAppendKey, oldNotKey === newNotKey, oldMatchWay === newMatchWay)
+      return oldKey === newKey && oldAppendKey === newAppendKey && oldNotKey === newNotKey && oldMatchWay === newMatchWay
+    },
+    // 判断附加词排除词输入框是否有空
+    inputIsEmpty (arr) {
+      return arr.some((v) => !v)
+    },
+    // 获取所有关键词的key的属性,并返回一个数组(主要用于判断添加关键词是否重复)
+    getKeyTotalArray () {
+      if (!this.datas) return
+      const keysArr = []
+      this.datas.forEach((s) => {
+        if (s && s.a_key && Array.isArray(s.a_key)) {
+          s.a_key.forEach((v) => {
+            if (Array.isArray(v.key)) {
+              keysArr.push(v.key.toString().replace(',', ' '))
+            } else {
+              keysArr.push(v.key)
+            }
+          })
+        }
+      })
+      return keysArr
+    },
+    // 判断关键词出现的个数
+    getRepeatCounts (arr, value) {
+      arr.reduce((a, v) => {
+        v === value ? a + 1 : a + 0
+      }, 0)
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.edit-form{
+  margin-top: 20px;
+  .classify-title{
+    padding-bottom: 20px;
+    .icon-edit,.icon-delete{
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      background-repeat: no-repeat;
+      background-position: center center;
+      cursor: pointer;
+    }
+    .icon-edit{
+      margin: 0 10px;
+      background-image: url('~@/assets/images/icon-edit.png');
+      background-size: contain;
+    }
+    .icon-delete{
+      background-image: url('~@/assets/images/icon-delete.png');
+      background-size: contain;
+    }
+  }
+  .input-box{
+    padding: 20px 30px;
+    background: #f6f7f9;
+    border-radius: 8px;
+  }
+  .input-box-title{
+    margin-bottom: 16px;
+    font-size: 16px;
+    text-align: center;
+    color: #1d1d1d;
+  }
+  .item{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-bottom: 10px;
+  }
+  .item-label{
+    margin-right: 8px;
+    min-width: 120px;
+    height: 40px;
+    color: #1d1d1d;
+    font-size: 14px;
+    line-height: 40px;
+    text-align: right;
+  }
+  .item-label-required:before{
+    content: "*";
+    color: #f56c6c;
+    margin-right: 2px;
+  }
+  .recommend{
+    padding: 16px;
+    background: #fff;
+    border-radius: 4px;
+    .recommend-title{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      color: #686868;
+      font-size: 14px;
+      .batch-btn{
+        color: #2cb7ca;
+        font-size: 14px;
+        cursor: pointer;
+      }
+    }
+    .recommend-main{
+      display: flex;
+      align-items: center;
+      flex-wrap: wrap;
+      .r-list{
+        padding: 0 8px;
+        margin: 12px 12px 0 0;
+        height: 22px;
+        line-height: 22px;
+        background: #f5f6f7;
+        border-radius: 4px;
+        color: #1d1d1d;;
+        font-size: 14px;
+        cursor: pointer;
+      }
+      .active{
+        background: #2cb7ca;
+        color: #fff;
+      }
+    }
+  }
+  .add-words-btn{
+    width: 352px;
+    height: 40px;
+    line-height: 40px;
+    border: 1px dashed #2cb7ca;
+    border-radius: 6px;
+    background: #fff;
+    color: #2cb7ca;
+    text-align: center;
+    cursor: pointer;
+    font-size: 14px;
+  }
+  .little-tips{
+    margin-top: 20px;
+    height: 32px;
+    line-height: 32px;
+    background: rgba(255, 159, 64,0.1);
+    border-radius: 4px;
+    color: #ff9f40;
+    font-size: 13px;
+    text-align: center;
+  }
+  .item-value{
+    flex: 1;
+  }
+  .custom-long-input{
+    width: 352px;
+  }
+  .custom-short-input{
+    width: 170px;
+  }
+  .item-other{
+    display: flex;
+    justify-content: center;
+    margin-bottom: 10px;
+  }
+  .item-other-value{
+    margin-top: 8px;
+  }
+  .math-tips{
+    margin-top: 4px;
+    font-size: 12px;
+    color: #999999;
+    line-height: 20px;
+  }
+  .radio-item{
+    margin-bottom: 10px;
+  }
+  .item-keywords-value{
+    display: flex;
+    justify-content: space-between;
+  }
+  .add-tag{
+    width: 170px;
+    height: 40px;
+    line-height: 40px;
+    color: #2cb7ca;
+    border-radius: 6px;
+    text-align: center;
+    cursor: pointer;
+    color: #2cb7ca;
+    border: 1px dashed #2cb7ca;
+  }
+  .add-word-list{
+    margin-bottom: 6px;
+  }
+  .confirm-btn{
+    display: block;
+    width: 352px;
+    height: 46px;
+    margin: 20px auto 0;
+    line-height: 46px;
+    font-size: 16px;
+    background: #2cb7ca;
+    border-radius: 6px;
+    color: #fff;
+    cursor: pointer;
+    &:disabled{
+      opacity: 0.5;
+      cursor: not-allowed;
+    }
+  }
+  .keywords-help{
+    width: 412px;
+    margin-top: 20px;
+    font-size: 12px;
+    color: #999999;
+    line-height: 20px;
+    text-align: justify;
+  }
+}
+// element-ui样式修改
+::v-deep {
+  .el-input__inner,
+  .el-textarea__inner{
+    font-size: 14px;
+    color: #1D1D1D;
+    border-color: #ececec;
+  }
+  .el-textarea__inner{
+    padding: 8px 15px;
+  }
+  .el-input__inner:hover,
+  .el-textarea__inner:hover{
+    border-color: #ececec;
+  }
+  .el-input__inner:focus,
+  .el-textarea__inner:focus{
+    border-color: #2cb7ca;
+  }
+  .el-radio{
+    color: #1d1d1d;
+    font-size: 14px;
+  }
+  .el-radio__inner{
+    width: 20px;
+    height: 20px;
+  }
+  .el-radio__input.is-checked .el-radio__inner{
+    border: 0;
+    background: transparent;
+    width: 20px;
+    height: 20px;
+    background: url('~@/assets/images/icon-checked.png') no-repeat center center;
+    background-size: contain;
+  }
+  .el-radio__inner::after{
+    background: transparent;
+  }
+  .el-radio__input.is-checked+.el-radio__label{
+    color: #1d1d1d;
+  }
+  .el-radio__inner:hover{
+    border-color: #ececec;
+  }
+}
+</style>

+ 141 - 275
src/views/subscribe/components/key/KeyConfig.vue

@@ -1,56 +1,55 @@
 <template>
 <template>
   <div class="keywords">
   <div class="keywords">
     <div class="key-title">
     <div class="key-title">
-      <span>{{datas.title}}</span>
-      <span style="font-size:14px;"><em style="color: #2cb7ca;"> {{keyCounts}}</em>/{{datas.maxCount}}</span>
+      <div>
+        <span>{{datas.title}}</span>
+        <span style="font-size:14px;"><em style="color: #2cb7ca;"> {{keyCounts}}</em>/{{datas.maxCount}}</span>
+        <span style="font-size:14px;color: #2cb7ca;margin-left:16px;">注:任意1组关键词组匹配成功即推送相关信息</span>
+      </div>
+      <div class="add-classfily" @click="addClassfilyFn()"><i class="el-icon-plus"></i> 新增分类</div>
     </div>
     </div>
     <div class="key-content">
     <div class="key-content">
-      <div class="item">
-        <div class="item-label">关键词分类:</div>
-        <div class="item-value">
-          <el-input class="custom-long-input" v-model.trim="cur.classify" maxlength="20" placeholder="请输入关键词分类"></el-input>
-        </div>
-      </div>
-      <div class="item">
-        <div class="item-label item-label-required">关键词:</div>
-        <div class="item-value">
-          <el-input class="custom-long-input" v-model="cur.key" maxlength="20" placeholder="请输入关键词"></el-input>
+      <!-- <div v-if="datas.keyList.length === 0">
+        <div class="classify-title">
+          <span class="title-text">{{defaultClass}}</span>
         </div>
         </div>
-      </div>
-      <div class="item">
-        <div class="item-label"></div>
-        <div class="item-value item-keywords-value">
-          <div>
-            <div class="add-word-list" v-for="(add,index) in cur.appendkey" :key="'add' + index">
-              <el-input v-model.trim="cur.appendkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入附加词"></el-input>
-            </div>
-            <div class="add-tag" @click="addAttachWordsFn">+添加附加词</div>
-          </div>
-          <div>
-            <div class="add-word-list" v-for="(not,index) in cur.notkey" :key="'0' + index">
-              <el-input v-model.trim="cur.notkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入排除词"></el-input>
+        <Edit
+          title="新增"
+          :datas="datas.keyList"
+          :className="defaultClass"
+          :classIndex="0"
+          :keyIndex="0"
+        ></Edit>
+      </div> -->
+    </div>
+    <!-- 修改分类dialog -->
+    <el-dialog
+      custom-class="sub-dialog"
+      :visible.sync="add.dialog"
+      :close-on-click-modal="false"
+      :show-close="false"
+      center
+      width="800px"
+      v-if="add.dialog"
+    >
+      <KeyCard @onCancel="add.dialog = false" @onConfirm="confirmEditClassFn">
+        <div slot="header">新增关键词分类</div>
+        <div class="class-edit-content">
+          <div class="item">
+            <div class="item-label">关键词分类:</div>
+            <div class="item-value">
+              <el-input class="custom-long-input" v-model.trim="add.className"  maxlength="20" placeholder="请输入关键词分类"></el-input>
             </div>
             </div>
-            <div class="add-tag" @click="addExcludeWordsFn">+添加排除词</div>
           </div>
           </div>
         </div>
         </div>
-      </div>
-      <div class="item">
-        <div class="item-label"></div>
-        <div class="item-value">
-          <button type="button" :disabled="keyDisabled" class="save-btn" @click="submitKeywords">保存关键词</button>
-          <div class="keywords-help">
-            <p>例:某公司主管业务为软件系统开发 </p>
-            <p>关键词:目标信息钟的关键性词语,如“软件系统” </p>
-            <p>附加词:与关键词形成一体/组合,便于查找准确信息,如“开发”</p>
-            <p>排除词:与关键词互斥,可排除一部分非目标信息,如“运维”</p>
-          </div>
-        </div>
-      </div>
-    </div>
+      </KeyCard>
+    </el-dialog>
   </div>
   </div>
 </template>
 </template>
 <script>
 <script>
-import { Input, Button } from 'element-ui'
+import { Input, Button, Dialog, RadioGroup, Radio } from 'element-ui'
+import KeyCard from '@/components/selector/SelectorCard'
+import { setUserInfoVip } from '@/api/modules'
 export default {
 export default {
   name: 'key-config',
   name: 'key-config',
   props: {
   props: {
@@ -62,16 +61,18 @@ export default {
   },
   },
   components: {
   components: {
     [Input.name]: Input,
     [Input.name]: Input,
-    [Button.name]: Button
+    [Dialog.name]: Dialog,
+    [Button.name]: Button,
+    [RadioGroup.name]: RadioGroup,
+    [Radio.name]: Radio,
+    KeyCard
+    // Edit
   },
   },
   data () {
   data () {
     return {
     return {
-      // 当前输入框的数据
-      cur: {
-        classify: '',
-        key: '',
-        appendkey: [''],
-        notkey: ['']
+      add: {
+        dialog: false,
+        className: ''
       }
       }
     }
     }
   },
   },
@@ -84,186 +85,69 @@ export default {
         }
         }
       })
       })
       return count
       return count
-    },
-    keyDisabled () {
-      return !this.cur.key
     }
     }
   },
   },
   mounted () {},
   mounted () {},
   methods: {
   methods: {
-    // 保存数据
-    saveCurData () {
-      const data = this.datas.keyList
-      const obj = {
-        a_key: [
-          {
-            key: this.cur.key.split(' '),
-            notkey: this.cur.notkey,
-            appendkey: this.cur.appendkey
-          }
-        ],
-        s_item: this.cur.classify ? this.cur.classify : this.getNewClassName()
-      }
-      data.push(obj)
-      return data
+    // 新增关键词分类弹框
+    addClassfilyFn () {
+      this.add.className = ''
+      this.add.dialog = true
     },
     },
-    // 提交关键词
-    submitKeywords () {
-      const totalCount = this.addTotalCount()
+    // 确认添加分类
+    confirmEditClassFn () {
       const classArr = this.getClassArray()
       const classArr = this.getClassArray()
-      const keyArr = this.getKeyTotalArray()
-      if (totalCount >= this.datas.maxCount) {
-        return this.$message({
-          type: 'warning',
-          message: '关键词超出最大限制'
-        })
-      }
-      if (classArr.indexOf(this.cur.classify) > -1) {
+      if (classArr.indexOf(this.add.className) > -1) {
         return this.$message({
         return this.$message({
           type: 'warning',
           type: 'warning',
           message: '分类名不能重复'
           message: '分类名不能重复'
         })
         })
-      }
-      if (keyArr.indexOf(this.cur.key) > -1) {
+      } else if (this.add.className === '') {
         return this.$message({
         return this.$message({
           type: 'warning',
           type: 'warning',
-          message: '关键词不能重复'
+          message: '分类名不能为空'
         })
         })
       }
       }
-      // 判断附加词是否重复
-      if (this.getArrIsRepeat(this.cur.appendkey)) {
-        return this.$message({
-          type: 'warning',
-          message: '设置的附加词重复,请调整后再添加'
-        })
-      }
-      // 判断排除词是否重复
-      if (this.getArrIsRepeat(this.cur.notkey)) {
-        return this.$message({
-          type: 'warning',
-          message: '设置的排除词重复,请调整后再添加'
-        })
-      }
-      const params = this.saveCurData()
-      // 通知父组件发请求修改并更新
-      this.$emit('updateKey', params)
-    },
-    // 添加附加词
-    addAttachWordsFn () {
-      const arr = this.cur.appendkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入附加词',
-          type: 'warning'
-        })
-      } else {
-        this.cur.appendkey.push('')
-      }
-    },
-    // 添加排除词
-    addExcludeWordsFn () {
-      const arr = this.cur.notkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入排除词',
-          type: 'warning'
+      if (this.datas.keyList.length === 1 && this.datas.keyList[0].s_item === '未分类') {
+        setUserInfoVip({
+          pageType: 'keyWords',
+          classify_index: 0,
+          classify_name: this.add.className,
+          actionType: 'SC'
+        }).then(res => {
+          if (res.flag) {
+            this.$emit('update')
+            this.add.dialog = false
+          }
         })
         })
-      } else {
-        this.cur.notkey.push('')
+        return
       }
       }
+      setUserInfoVip({
+        pageType: 'keyWords',
+        actionType: 'SC',
+        classify_name: this.add.className
+      }).then((res) => {
+        if (res.flag) {
+          this.$emit('update')
+        } else {
+          this.$message({
+            message: '新增分类失败',
+            type: 'error'
+          })
+        }
+      })
+      this.add.dialog = false
     },
     },
-    // 判断附加词排除词输入框是否有空
-    inputIsEmpty (arr) {
-      return arr.some((v) => !v)
-    },
-    // 获取所有分类名 用于判断是否重复
+    // 获取所有分类名
     getClassArray () {
     getClassArray () {
+      const data = JSON.parse(JSON.stringify(this.datas.keyList))
       const classArr = []
       const classArr = []
-      this.datas.keyList.forEach((v) => {
+      data.forEach((v) => {
         if (v.s_item) {
         if (v.s_item) {
           classArr.push(v.s_item)
           classArr.push(v.s_item)
         }
         }
       })
       })
       return classArr
       return classArr
-    },
-    // 获取所有关键词的key的属性,并返回一个数组(主要用于判断添加关键词是否重复)
-    getKeyTotalArray () {
-      const keysArr = []
-      this.datas.keyList.forEach((s) => {
-        if (s && s.a_key && Array.isArray(s.a_key)) {
-          s.a_key.forEach((v) => {
-            if (Array.isArray(v.key)) {
-              keysArr.push(v.key.toString().replace(',', ' '))
-            } else {
-              keysArr.push(v.key)
-            }
-          })
-        }
-      })
-      return keysArr
-    },
-    // 判断附加词、排除词是否重复
-    getArrIsRepeat (arr) {
-      this.removeEmptyInput(arr)
-      return (new Set(arr)).size !== arr.length
-    },
-    // 通过已有分类得到一个未分类名
-    getNewClassName () {
-      var conf = {
-        defaultT: '未分类',
-        reg: /^未分类(\d*)$/,
-        // 未分类名称数组
-        unclassified: [],
-        // 未分类名称数字数组
-        unclassifiedNum: [],
-        sortedArr: []
-      }
-      this.datas.keyList.forEach((item) => {
-        if (item.s_item && conf.reg.test(item.s_item)) {
-          let num = item.s_item.replace(new RegExp(conf.defaultT, 'g'), '')
-          num = parseInt(num)
-          if (isNaN(num)) {
-            num = 0
-          }
-          conf.unclassifiedNum.push(num)
-          conf.unclassified.push(item.s_item)
-        }
-      })
-      // 得到分类名
-      if (conf.unclassifiedNum.length === 0) {
-        return '未分类'
-      } else {
-        conf.sortedArr = conf.unclassifiedNum.sort(function (a, b) {
-          return b - a
-        })
-        return conf.defaultT + (conf.sortedArr[0] + 1)
-      }
-    },
-    // 清空输入框 供父组件请求成功后刷新
-    clearInputData () {
-      this.cur.classify = ''
-      this.cur.key = ''
-      this.cur.appendkey = ['']
-      this.cur.notkey = ['']
-    },
-    addTotalCount () {
-      let count = 0
-      this.datas.keyList.forEach(v => {
-        if (v && v.a_key) {
-          count += v.a_key.length
-        }
-      })
-      return count
-    },
-    removeEmptyInput (arr) {
-      for (let i = 0; i < arr.length; i++) {
-        // eslint-disable-next-line
-        if (arr[i] === '' || arr[i] === null || typeof (arr[i]) === undefined) {
-          arr.splice(i, 1)
-          i = i - 1
-        }
-      }
-      return arr
     }
     }
   }
   }
 }
 }
@@ -271,14 +155,67 @@ export default {
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .keywords{
 .keywords{
   .key-title{
   .key-title{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
     padding-bottom: 8px;
     padding-bottom: 8px;
     font-size: 18px;
     font-size: 18px;
     color: #1d1d1d;
     color: #1d1d1d;
     line-height: 28px;
     line-height: 28px;
     border-bottom: 1px solid #ececec;
     border-bottom: 1px solid #ececec;
+    .add-classfily{
+      width: 110px;
+      height: 38px;
+      line-height: 38px;
+      border: 1px solid #2cb7ca;
+      border-radius: 6px;
+      text-align: center;
+      color: #2CB7CA;
+      font-size: 14px;
+      cursor: pointer;
+    }
   }
   }
   .key-content{
   .key-content{
     padding: 20px 0;
     padding: 20px 0;
+    .classify-title{
+      display: flex;
+      align-items: center;
+      .title-text{
+        font-size: 16px;
+        color: #1d1d1d;
+      }
+      .icon-edit,.icon-delete{
+        display: inline-block;
+        width: 16px;
+        height: 16px;
+        background-repeat: no-repeat;
+        background-position: center center;
+        cursor: pointer;
+      }
+      .icon-edit{
+        margin: 0 10px;
+        background-image: url('~@/assets/images/icon-edit.png');
+        background-size: contain;
+      }
+      .icon-delete{
+        background-image: url('~@/assets/images/icon-delete.png');
+        background-size: contain;
+      }
+    }
+  }
+  .selector-card{
+    width: 800px;
+    height: auto;
+  }
+  .class-edit-content{
+    width: 740px;
+    padding: 30px 0;
+    height: auto;
+    max-height: 340px;
+    margin: 0 auto;
+    border: 1px solid #ececec;
+    overflow-y: scroll;
+    box-sizing: border-box;
     .item{
     .item{
       display: flex;
       display: flex;
       align-items: center;
       align-items: center;
@@ -305,77 +242,6 @@ export default {
     .custom-long-input{
     .custom-long-input{
       width: 352px;
       width: 352px;
     }
     }
-    .custom-short-input{
-      width: 170px;
-    }
-    .item-other{
-      display: flex;
-      justify-content: center;
-      margin-bottom: 10px;
-    }
-    .item-other-value{
-      margin-top: 8px;
-    }
-    .math-tips{
-      margin-top: 4px;
-      font-size: 12px;
-      color: #999999;
-      line-height: 20px;
-    }
-    .radio-item{
-      margin-bottom: 10px;
-    }
-    .item-keywords-value{
-      display: flex;
-      justify-content: space-between;
-    }
-    .add-tag{
-      width: 170px;
-      height: 40px;
-      line-height: 40px;
-      color: #2cb7ca;
-      border-radius: 6px;
-      text-align: center;
-      cursor: pointer;
-      color: #2cb7ca;
-      border: 1px dashed #2cb7ca;
-    }
-    .add-word-list{
-      margin-bottom: 6px;
-    }
-    .save-btn{
-      width: 352px;
-      height: 46px;
-      line-height: 46px;
-      font-size: 16px;
-      background: #2cb7ca;
-      border-radius: 6px;
-      color: #fff;
-      &:disabled{
-        opacity: 0.5;
-        cursor: not-allowed;
-      }
-    }
-    .keywords-help{
-      width: 412px;
-      margin-top: 20px;
-      font-size: 12px;
-      color: #999999;
-      line-height: 20px;
-      text-align: justify;
-    }
-  }
-  // element-ui样式修改
-  ::v-deep.el-input__inner{
-    font-size: 14px;
-    color: #1D1D1D;
-    border-color: #ececec;
-  }
-  ::v-deep.el-input__inner:hover{
-    border-color: #ececec;
-  }
-  ::v-deep.el-input__inner:focus{
-    border-color: #2cb7ca;
   }
   }
 }
 }
 </style>
 </style>

+ 0 - 164
src/views/subscribe/components/key/KeyEdit.vue

@@ -1,164 +0,0 @@
-<template>
-  <div class="keywords-edit">
-    <div class="item">
-      <div class="item-label">关键词分类:</div>
-      <div class="item-value">{{datas.className}}</div>
-    </div>
-    <div class="item">
-      <div class="item-label item-label-required">关键词:</div>
-      <div class="item-value">
-        <el-input class="custom-long-input" v-model="cur.key" maxlength="20" placeholder="请输入关键词"></el-input>
-      </div>
-    </div>
-    <div class="item">
-      <div class="item-label"></div>
-      <div class="item-value item-keywords-value">
-        <div>
-          <div class="add-word-list" v-for="(add,index) in cur.appendkey" :key="'add' + index">
-            <el-input v-model.trim="cur.appendkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入附加词"></el-input>
-          </div>
-          <div class="add-tag" @click="addAppendKey">+添加附加词</div>
-        </div>
-        <div>
-          <div class="add-word-list" v-for="(not,index) in cur.notkey" :key="'0' + index">
-            <el-input v-model.trim="cur.notkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入排除词"></el-input>
-          </div>
-          <div class="add-tag" @click="addNotKey">+添加排除词</div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-<script>
-import { Input } from 'element-ui'
-export default {
-  name: 'keywords-edit',
-  components: {
-    [Input.name]: Input
-  },
-  props: {
-    datas: {
-      ways: String,
-      className: String,
-      key: Array,
-      notkey: Array,
-      appendkey: Array
-    }
-  },
-  data () {
-    return {
-      cur: {
-        key: '',
-        appendkey: [],
-        notkey: []
-      }
-    }
-  },
-  watch: {},
-  computed: {},
-  created () {
-    this.getParentData()
-  },
-  mounted () {},
-  methods: {
-    // 获取父组件keyList传来的数据 并进行格式转化渲染到输入框
-    getParentData () {
-      const datas = JSON.parse(JSON.stringify(this.datas))
-      this.cur.key = datas.key.join(' ')
-      this.cur.notkey = datas.notkey
-      this.cur.appendkey = datas.appendkey
-    },
-    addAppendKey () {
-      const arr = this.cur.appendkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入附加词',
-          type: 'warning'
-        })
-      } else {
-        this.cur.appendkey.push('')
-      }
-    },
-    addNotKey () {
-      const arr = this.cur.notkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入排除词',
-          type: 'warning'
-        })
-      } else {
-        this.cur.notkey.push('')
-      }
-    },
-    inputIsEmpty (arr) {
-      return arr.some((v) => !v)
-    }
-  }
-}
-</script>
-<style lang="scss" scoped>
-.keywords-edit{
-  .item{
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    margin-bottom: 10px;
-  }
-  .item-label{
-    margin-right: 8px;
-    min-width: 120px;
-    height: 40px;
-    color: #1d1d1d;
-    font-size: 14px;
-    line-height: 40px;
-    text-align: right;
-  }
-  .item-label-required:before{
-    content: "*";
-    color: #f56c6c;
-    margin-right: 2px;
-  }
-  .item-value{
-    width: 352px;
-  }
-  .custom-long-input{
-    width: 352px;
-  }
-  .custom-short-input{
-    width: 170px;
-  }
-  .item-keywords-value{
-    display: flex;
-    justify-content: space-between;
-  }
-  .add-tag{
-    width: 170px;
-    height: 40px;
-    line-height: 40px;
-    color: #2cb7ca;
-    border-radius: 6px;
-    text-align: center;
-    cursor: pointer;
-    color: #2cb7ca;
-    border: 1px dashed #2cb7ca;
-  }
-  .add-word-list{
-    margin-bottom: 6px;
-  }
-  // element-ui样式修改
-  ::v-deep.el-input__inner{
-    font-size: 14px;
-    color: #1D1D1D;
-    border-color: #ececec;
-    padding-left: 16px!important;
-    border-radius: 6px!important;
-    background: #fff!important;
-  }
-  ::v-deep.el-input__inner:hover{
-    border-color: #ececec;
-  }
-  ::v-deep.el-input__inner:focus{
-    border-color: #2cb7ca;
-  }
-}
-</style>

+ 252 - 163
src/views/subscribe/components/key/KeyList.vue → src/views/subscribe/components/key/List.vue

@@ -1,29 +1,48 @@
 <template>
 <template>
   <div class="classify">
   <div class="classify">
-    <div class="classify-list" v-for="(item,index) in datas.keyList" :key="'1' + index">
+    <div class="classify-list" v-for="(item,index) in newWordsList" :key="'1' + index">
       <div class="classify-title">
       <div class="classify-title">
         <span class="title-text">{{item.s_item}}</span>
         <span class="title-text">{{item.s_item}}</span>
-        <span class="icon-edit" @click="editClassFn(item.s_item, index)"></span>
-        <span class="icon-delete" @click="deleteClassFn(item.s_item, index)"></span>
+        <span class="icon-edit" @click="editClassFn(item.s_item, item.groupIndex)"></span>
+        <span class="icon-delete" @click="deleteClassFn(item, item.groupIndex)"></span>
       </div>
       </div>
       <div class="classify-content">
       <div class="classify-content">
-        <div class="list" v-for="(v, i) in item.a_key" :key="'2' + i">
-          <div class="list-box">
-            <div class="list-value">
-              <p>关键词: {{v.key.join(' ') || '--'}}</p>
-              <p>附加词: {{v.appendkey.join('、') || '--'}}</p>
-              <p>排除词: {{v.notkey.join('、') || '--'}}</p>
+        <div class="add-words-box" @click="addNewKeyword(item, item.groupIndex)" v-if="!item.showForm" key="add">+新增关键词组</div>
+        <div style="width:100%;" v-else key="add">
+          <Edit
+            :datas="newWordsList"
+            :className="item.s_item"
+            title="新增"
+            :classIndex="item.groupIndex"
+            :keyIndex="item.a_key.length">
+          </Edit>
+        </div>
+        <div v-for="(v, i) in item.a_key" :key="'2' + i" style="width:100%;">
+          <Edit
+            :datas="newWordsList"
+            title="修改"
+            :className="item.s_item"
+            :keywords="v"
+            :classIndex="item.groupIndex"
+            :keyIndex="v.keyindex"
+            v-if="v.showForm" key="edit">
+          </Edit>
+          <div class="words-list" v-else key="edit">
+            <div class="list-left yellow-box" v-if="v.matchway">模糊</div>
+            <div class="list-left blue-box" v-else>精准</div>
+            <div class="list-middle">
+              <div class="list-keywords" v-if="v.appendkey" key="append">
+                {{v.key.join(' ') + ' ' +  v.appendkey.join(' ')}}
+              </div>
+              <div class="list-keywords" v-else key="append">{{v.key.join(' ')}}</div>
+              <p class="list-notkey" v-if="v.notkey && v.notkey.length > 0">排除词: {{v.notkey.join(' ')}}</p>
             </div>
             </div>
-            <div class="list-icon" @click="deleteKeyFn(index, i)"></div>
-            <div class="list-edit" @click="editKeyFn(item, v, index, i)">
-              编辑
-              <i class="tri-down"></i>
+            <div class="list-right">
+              <span class="icon-edit" @click="editKeyFn(item, v, item.groupIndex, v.keyindex)"></span>
+              <span class="icon-delete" @click="deleteKeyFn(item.groupIndex, v.keyindex)"></span>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
-        <div class="words-add" @click="addKeyFn(item.s_item)">
-          <i class="el-icon-plus"></i>
-        </div>
       </div>
       </div>
     </div>
     </div>
     <!-- 修改分类dialog -->
     <!-- 修改分类dialog -->
@@ -68,29 +87,64 @@
         </div>
         </div>
       </div>
       </div>
     </el-dialog>
     </el-dialog>
-    <!-- 关键词dialog -->
+    <!-- 删除关键词dialog -->
     <el-dialog
     <el-dialog
-      custom-class="sub-dialog"
-      :visible.sync="dialog.editKey"
+      custom-class="sub-dialog small-dialog"
+      :visible.sync="dialog.delKey"
       :close-on-click-modal="false"
       :close-on-click-modal="false"
       :show-close="false"
       :show-close="false"
+      top="30vh"
       center
       center
-      width="800px"
-      v-if="dialog.editKey"
+      width="380px"
+      v-if="dialog.delKey"
     >
     >
-      <KeyCard @onCancel="cancleEdit" @onConfirm="submitKeywords">
-        <div slot="header">修改关键词</div>
-        <div class="key-edit-content">
-          <KeyEdit ref="keyEditRef" :datas="props"></KeyEdit>
+      <div class="delete-class">
+        <div class="delete-class-header">删除关键词组</div>
+        <div class="delete-class-content">确定删除该关键词组吗?</div>
+        <div class="delete-class-footer">
+          <el-button type="primary" class="confirm" @click="confirmDeleteKeyFn">确定</el-button>
+          <el-button class="cancel" @click="dialog.delKey = false">取消</el-button>
         </div>
         </div>
-      </KeyCard>
+      </div>
+    </el-dialog>
+    <!-- 删除分类dialog -->
+    <el-dialog
+      custom-class="small-dialog"
+      title="删除关键词分类"
+      :visible.sync="dialog.notDelClass"
+      width="380px"
+      center
+      :show-close="false"
+      append-to-body
+      >
+      <p class="dialog-text">该关键词分类下存在关键词组,无法删除,请先删除该分类下的关键词组</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button class="know-btn" type="primary" @click="dialog.notDelClass = false">我知道了</el-button>
+      </span>
+    </el-dialog>
+    <!-- 关键词添加超限提示 -->
+    <el-dialog
+      custom-class="small-dialog"
+      title="新增关键词组"
+      :visible.sync="dialog.limit"
+      width="380px"
+      center
+      :show-close="false"
+      append-to-body
+      >
+      <p class="dialog-text">您的关键词组数量已达300组,无法继续添加</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button class="know-btn" type="primary" @click="dialog.limit = false">我知道了</el-button>
+      </span>
     </el-dialog>
     </el-dialog>
   </div>
   </div>
 </template>
 </template>
 <script>
 <script>
 import { Tooltip, Dialog, Input, Button } from 'element-ui'
 import { Tooltip, Dialog, Input, Button } from 'element-ui'
 import KeyCard from '@/components/selector/SelectorCard'
 import KeyCard from '@/components/selector/SelectorCard'
-import KeyEdit from './KeyEdit'
+import Edit from './Edit'
+import $bus from '@/utils/bus'
+import { setUserInfoVip } from '@/api/modules'
 export default {
 export default {
   name: 'keywords-list',
   name: 'keywords-list',
   components: {
   components: {
@@ -99,7 +153,7 @@ export default {
     [Input.name]: Input,
     [Input.name]: Input,
     [Button.name]: Button,
     [Button.name]: Button,
     KeyCard,
     KeyCard,
-    KeyEdit
+    Edit
   },
   },
   props: {
   props: {
     datas: {
     datas: {
@@ -112,7 +166,10 @@ export default {
       dialog: {
       dialog: {
         editClass: false, // 修改分类弹框
         editClass: false, // 修改分类弹框
         delClass: false, // 删除分类弹框
         delClass: false, // 删除分类弹框
-        editKey: false // 修改关键词弹框
+        editKey: false, // 修改关键词弹框
+        delKey: false, // 删除关键词
+        notDelClass: false, // 不能删除分类弹框
+        limit: false // 关键词设置超出上限弹框
       },
       },
       // 传给dialog子组件的数据
       // 传给dialog子组件的数据
       props: {
       props: {
@@ -123,15 +180,54 @@ export default {
         key: [], // 关键词
         key: [], // 关键词
         notkey: [], // 排除词
         notkey: [], // 排除词
         appendkey: [] // 附加词
         appendkey: [] // 附加词
-      }
+      },
+      newWordsList: [] // 将父组件传来的数据赋值给该变量
     }
     }
   },
   },
   computed: {},
   computed: {},
-  mounted () {},
+  watch: {
+    'datas.keyList': function (newVal) {
+      if (newVal) {
+        this.newWordsList = newVal
+        this.formatDatasList()
+      }
+    }
+  },
+  mounted () {
+    this.formatDatasList()
+  },
   methods: {
   methods: {
-    // 取消编辑弹框
-    cancleEdit () {
-      this.dialog.editKey = false
+    sortData (arr) {
+      return arr.sort((a, b) => {
+        return b.updatetime - a.updatetime
+      })
+    },
+    // 修改设置接口
+    setInfoApi (data, callback) {
+      setUserInfoVip(data).then((res) => {
+        if (res.flag) {
+          $bus.$emit('updateKey')
+          callback && callback()
+        } else {
+          this.$message(res.msg)
+        }
+      })
+    },
+    // 处理数据 添加showForm字段
+    formatDatasList () {
+      const lists = JSON.parse(JSON.stringify(this.datas.keyList))
+      this.sortData(lists)
+      lists.forEach(v => {
+        if (v) {
+          if (v.a_key && v.a_key.length > 0) {
+            this.sortData(v.a_key)
+          } else {
+            v.a_key = []
+            v.showForm = true
+          }
+        }
+      })
+      this.newWordsList = lists
     },
     },
     // 打开修改关键词分类弹框
     // 打开修改关键词分类弹框
     editClassFn (name, index) {
     editClassFn (name, index) {
@@ -159,61 +255,83 @@ export default {
           v.s_item = this.props.className
           v.s_item = this.props.className
         }
         }
       })
       })
-      this.$emit('updateKey', data)
-      this.dialog.editClass = false
+      const datas = {
+        pageType: 'keyWords',
+        classify_index: this.props.classIndex,
+        classify_name: this.props.className,
+        actionType: 'SC'
+      }
+      this.setInfoApi(datas, () => {
+        this.dialog.editClass = false
+      })
+    },
+    // 新增关键词 打开编辑表单
+    addNewKeyword (item, index) {
+      console.log(item, index, '1111')
+      const isCan = this.getIsAdd()
+      // console.log(isCan, 'isCan')
+      if (isCan) {
+        this.dialog.limit = true
+        return
+      }
+      item.showForm = true
     },
     },
     // 打开删除关键词分类弹框
     // 打开删除关键词分类弹框
-    deleteClassFn (name, index) {
+    deleteClassFn (item, index) {
+      // console.log(this.newWordsList, item)
+      if (item.a_key && item.a_key.length > 0) {
+        this.dialog.notDelClass = true
+        return
+      }
       this.props.classIndex = null
       this.props.classIndex = null
       this.dialog.delClass = true
       this.dialog.delClass = true
-      this.props.className = name
+      this.props.className = item.s_item
       this.props.classIndex = index
       this.props.classIndex = index
     },
     },
     // 确认删除分类
     // 确认删除分类
     confirmDeleteClassFn () {
     confirmDeleteClassFn () {
-      const data = JSON.parse(JSON.stringify(this.datas.keyList))
-      this.dialog.delClass = false
-      // 删除整个分类的数据
-      data.splice(this.props.classIndex, 1)
-      this.$emit('updateKey', data)
-    },
-    // 添加关键词
-    addKeyFn (name) {
-      const isCan = this.getIsAdd()
-      if (isCan) {
-        return this.$message({
-          type: 'warning',
-          message: '关键词超出最大限制'
-        })
+      const datas = {
+        pageType: 'keyWords',
+        classify_index: this.props.classIndex,
+        actionType: 'DC'
       }
       }
-      this.dialog.editKey = true
-      this.props.ways = 'add'
-      this.props.className = name
-      this.props.key = []
-      this.props.notkey = []
-      this.props.appendkey = []
-    },
-    // 删除单个关键词
-    deleteKeyFn (index, i) {
-      console.log(index, i)
-      const data = JSON.parse(JSON.stringify(this.datas.keyList))
-      if (!data) return
-      data[index].a_key.splice(i, 1)
-      this.$emit('updateKey', data)
+      this.setInfoApi(datas, () => {
+        this.dialog.delClass = false
+      })
     },
     },
     // 编辑单个关键词
     // 编辑单个关键词
     editKeyFn (item, v, index, i) {
     editKeyFn (item, v, index, i) {
-      // index 分类下标 i 关键词下标
       this.clearPropsData()
       this.clearPropsData()
-      this.props.ways = 'edit'
-      this.dialog.editKey = true
-      // 把当前关键词传到dialog key,notkey,appendKey都为数组
       this.props.className = item.s_item
       this.props.className = item.s_item
       this.props.key = v.key
       this.props.key = v.key
       this.props.notkey = v.notkey
       this.props.notkey = v.notkey
       this.props.appendkey = v.appendkey
       this.props.appendkey = v.appendkey
       this.props.keyIndex = i
       this.props.keyIndex = i
       this.props.classIndex = index
       this.props.classIndex = index
+      v.showForm = true
+    },
+    // 打开删除关键词dialog
+    deleteKeyFn (index, i) {
+      // console.log(index, i)
+      this.props.classIndex = index
+      this.props.keyIndex = i
+      this.dialog.delKey = true
+    },
+    // 确定删除关键词组
+    confirmDeleteKeyFn () {
+      const delClassIndex = this.props.classIndex.toString()
+      const delKeyIndex = this.props.keyIndex.toString()
+      const obj = {
+        [delClassIndex]: delKeyIndex
+      }
+      const datas = {
+        pageType: 'keyWords',
+        actionType: 'DK',
+        delete_key: JSON.stringify(obj)
+      }
+      this.setInfoApi(datas, () => {
+        this.dialog.delKey = false
+      })
     },
     },
     // 清空传值
     // 清空传值
     clearPropsData () {
     clearPropsData () {
@@ -223,64 +341,7 @@ export default {
       this.props.notkey = []
       this.props.notkey = []
       this.props.appendkey = []
       this.props.appendkey = []
     },
     },
-    // 修改关键词dialog 保存数据
-    submitKeywords () {
-      const data = JSON.parse(JSON.stringify(this.datas.keyList))
-      const refs = this.$refs.keyEditRef.cur
-      const keyArr = this.getKeyTotalArray()
-      console.log(keyArr)
-      const type = this.props.ways
-      const obj = {
-        key: refs.key.split(' '),
-        notkey: refs.notkey,
-        appendkey: refs.appendkey
-      }
-      if (refs.key === '') {
-        return this.$message({
-          type: 'warning',
-          message: '关键词不能为空'
-        })
-      }
-      // 判断附加词是否重复
-      if (this.getArrIsRepeat(refs.appendkey)) {
-        return this.$message({
-          type: 'warning',
-          message: '设置的附加词重复,请调整后再添加'
-        })
-      }
-      // 判断排除词是否重复
-      if (this.getArrIsRepeat(refs.notkey)) {
-        return this.$message({
-          type: 'warning',
-          message: '设置的排除词重复,请调整后再添加'
-        })
-      }
-      if (type === 'add') {
-        if (keyArr.indexOf(refs.key) > -1) {
-          return this.$message({
-            type: 'warning',
-            message: '关键词不能重复'
-          })
-        }
-        data.forEach((v) => {
-          if (v.s_item === this.props.className) {
-            v.a_key.push(obj)
-          }
-        })
-      } else if (type === 'edit') {
-        if (!data[this.props.classIndex].a_key) return
-        data[this.props.classIndex].a_key.splice(this.props.keyIndex, 1)
-        data.forEach((v) => {
-          if (v.s_item === this.props.className) {
-            v.a_key.push(obj)
-          }
-        })
-      }
-      // 通知父组件发请求修改并更新
-      this.$emit('updateKey', data)
-      this.dialog.editKey = false
-    },
-    // 获取所有分类名
+    // 获取所有分类名 用户判断添加分类是否重复
     getClassArray () {
     getClassArray () {
       const data = JSON.parse(JSON.stringify(this.datas.keyList))
       const data = JSON.parse(JSON.stringify(this.datas.keyList))
       const classArr = []
       const classArr = []
@@ -291,23 +352,7 @@ export default {
       })
       })
       return classArr
       return classArr
     },
     },
-    // 获取所有关键词的key的属性,并返回一个数组(主要用于判断添加关键词是否重复)
-    getKeyTotalArray () {
-      const data = JSON.parse(JSON.stringify(this.datas.keyList))
-      const keysArr = []
-      data.forEach((s) => {
-        if (s && s.a_key && Array.isArray(s.a_key)) {
-          s.a_key.forEach((v) => {
-            if (Array.isArray(v.key)) {
-              keysArr.push(v.key.toString().replace(',', ' '))
-            } else {
-              keysArr.push(v.key)
-            }
-          })
-        }
-      })
-      return keysArr
-    },
+    // 添加了多少组关键词
     addTotalCount () {
     addTotalCount () {
       let count = 0
       let count = 0
       const data = JSON.parse(JSON.stringify(this.datas.keyList))
       const data = JSON.parse(JSON.stringify(this.datas.keyList))
@@ -318,30 +363,15 @@ export default {
       })
       })
       return count
       return count
     },
     },
-    // 判断附加词、排除词是否重复
-    getArrIsRepeat (arr) {
-      this.removeEmptyInput(arr)
-      return (new Set(arr)).size !== arr.length
-    },
     // 判断是否超限
     // 判断是否超限
     getIsAdd () {
     getIsAdd () {
       const len = this.addTotalCount()
       const len = this.addTotalCount()
-      console.log(len)
+      // console.log(len)
       if (len >= this.datas.maxCount) {
       if (len >= this.datas.maxCount) {
         return true
         return true
       } else {
       } else {
         return false
         return false
       }
       }
-    },
-    removeEmptyInput (arr) {
-      for (let i = 0; i < arr.length; i++) {
-        // eslint-disable-next-line
-        if (arr[i] === '' || arr[i] === null || typeof (arr[i]) === undefined) {
-          arr.splice(i, 1)
-          i = i - 1
-        }
-      }
-      return arr
     }
     }
   }
   }
 }
 }
@@ -450,8 +480,7 @@ export default {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     .title-text{
     .title-text{
-      font-size: 14px;
-      font-weight: 700;
+      font-size: 16px;
       color: #1d1d1d;
       color: #1d1d1d;
     }
     }
   }
   }
@@ -477,6 +506,66 @@ export default {
     align-items: center;
     align-items: center;
     justify-content: flex-start;
     justify-content: flex-start;
     flex-wrap: wrap;
     flex-wrap: wrap;
+    .add-words-box{
+      width: 100%;
+      height: 38px;
+      margin-top: 20px;
+      line-height: 38px;
+      border: 1px dashed #2cb7ca;
+      border-radius: 6px;
+      color: #2cb7ca;
+      font-size: 14px;
+      text-align: center;
+      cursor: pointer;
+    }
+    .words-list{
+      width: 100%;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 12px 16px;
+      margin-top: 12px;
+      border: 1px solid #ececec;
+      border-radius: 8px;
+      box-sizing: border-box;
+      .list-left{
+        width: 40px;
+        height: 22px;
+        line-height: 22px;
+        margin-right: 12px;
+        font-size: 12px;
+        text-align: center;
+        box-sizing: border-box;
+      }
+      .yellow-box{
+        border: 1px solid #ff9f40;
+        color: #ff9f40;
+        border-radius: 4px;
+      }
+      .blue-box{
+        border: 1px solid #2cb7ca;
+        color: #2cb7ca;
+        border-radius: 4px;
+      }
+      .list-middle{
+        flex: 1;
+      }
+      .list-keywords{
+        color: #1d1d1d;
+        line-height: 22px;
+        font-size: 14px;
+      }
+      .list-addkey,
+      .list-notkey{
+        color: #686868;
+        line-height: 20px;
+        font-size: 12px;
+      }
+      .list-right{
+        margin-left:15px;
+      }
+    }
+
     .list{
     .list{
       padding-top: 10px;
       padding-top: 10px;
       margin-right: 10px;
       margin-right: 10px;

+ 51 - 75
src/views/subscribe/components/key/SubConfig.vue

@@ -9,7 +9,7 @@
         </div>
         </div>
       </div>
       </div>
       <div class="item">
       <div class="item">
-        <div class="item-label item-label-required">采购单位行业:</div>
+        <div class="item-label item-label-required">采购单位类型:</div>
         <div class="item-value" @click="dialog.buyClass = true">
         <div class="item-value" @click="dialog.buyClass = true">
           <el-input class="custom-long-input" v-model="datas.buyClassStr" disabled></el-input>
           <el-input class="custom-long-input" v-model="datas.buyClassStr" disabled></el-input>
         </div>
         </div>
@@ -39,25 +39,6 @@
           <p class="math-tips">开启后,系统将根据你订阅的关键词自动匹配出相关联的项目,并将相关连项目的后续动态在超级订阅内一并推送。</p>
           <p class="math-tips">开启后,系统将根据你订阅的关键词自动匹配出相关联的项目,并将相关连项目的后续动态在超级订阅内一并推送。</p>
         </div>
         </div>
       </div>
       </div>
-      <div class="item-other">
-        <div class="item-label">"其他"采购单位:</div>
-        <div class="item-value item-other-value">
-          <el-switch
-            :disabled="unitSwitch.disabled"
-            v-loading="unitSwitch.loading"
-            @change="switchOtherUnit($event)"
-            v-model="datas.buyclassmatch"
-            :width="44"
-            :active-value="1"
-            :inactive-value="0"
-            active-text="关"
-            inactive-text="开"
-            active-color="#2cb7ca"
-            inactive-color="#bfbfbf">
-          </el-switch>
-          <p class="math-tips">每条信息的采购单位行业属性由机器自动识别,会存在少数无法识别的情况,此时将归类为“其他”。</p>
-        </div>
-      </div>
       <div class="item-other">
       <div class="item-other">
         <div class="item-label">关键词匹配方式:</div>
         <div class="item-label">关键词匹配方式:</div>
         <div class="item-value item-other-value">
         <div class="item-value item-other-value">
@@ -100,8 +81,9 @@
       <BuyClassSelect
       <BuyClassSelect
         :initCate="datas.buyClassArr"
         :initCate="datas.buyClassArr"
         @onCancel="dialog.buyClass = false"
         @onCancel="dialog.buyClass = false"
-        @onConfirm="saveBuyClassData">
-        <div slot="header">选择采购单位行业</div>
+        @onConfirm="saveBuyClassData"
+        @onOtherConfirm="saveOtherBuyClass">
+        <div slot="header">选择采购单位类型</div>
       </BuyClassSelect>
       </BuyClassSelect>
     </el-dialog>
     </el-dialog>
     <!-- 信息类型dialog -->
     <!-- 信息类型dialog -->
@@ -127,7 +109,7 @@ import { Switch, Input, RadioGroup, Radio, Button, Dialog } from 'element-ui'
 import AreaSelect from '@/components/selector/AreaSelector'
 import AreaSelect from '@/components/selector/AreaSelector'
 import BuyClassSelect from '@/components/selector/BuyerclassSelector'
 import BuyClassSelect from '@/components/selector/BuyerclassSelector'
 import InfoTypeSelect from '@/components/selector/InfoTypeSelector'
 import InfoTypeSelect from '@/components/selector/InfoTypeSelector'
-import { updateArea, updateBuyClass, updateInfoType, updateProjectMatch, updateOtherBuyClass, updateMatchType } from '@/api/modules'
+import { setAreaBuyer, setUserInfoVip } from '@/api/modules'
 export default {
 export default {
   name: 'sub-config',
   name: 'sub-config',
   props: {
   props: {
@@ -178,56 +160,33 @@ export default {
       console.log(event)
       console.log(event)
       this.projectSwitch.loading = true
       this.projectSwitch.loading = true
       this.projectSwitch.disabled = true
       this.projectSwitch.disabled = true
-      updateProjectMatch({
-        projectmatch: event
-      }).then((res) => {
-        if (res.data.status === 1) {
-          this.$emit('update')
-          this.projectSwitch.loading = false
-          this.projectSwitch.disabled = false
-          this.$message({
-            message: event === 1 ? '项目匹配已开启' : '项目匹配已关闭',
-            type: 'success'
-          })
-        }
-      })
-    },
-    // 切换其他采购单位按钮
-    switchOtherUnit (state) {
-      this.unitSwitch.loading = true
-      this.unitSwitch.disabled = true
-      updateOtherBuyClass({
-        otherbuyclass: state
-      }).then((res) => {
-        if (res.data.status === 1) {
-          this.$emit('update')
-          this.unitSwitch.loading = false
-          this.unitSwitch.disabled = false
-          this.$message({
-            message: state === 1 ? '"其它"采购单位已开启' : '"其它"采购单位已关闭',
-            type: 'success'
-          })
-        }
+      this.commonSetApi({
+        pageType: 'projectMatch',
+        pmindex: event
+      }, () => {
+        this.projectSwitch.loading = false
+        this.projectSwitch.disabled = false
+        this.$message({
+          message: event === 1 ? '项目匹配已开启' : '项目匹配已关闭',
+          type: 'success'
+        })
       })
       })
     },
     },
+    // 关键词匹配方式
     chooseMathWay (state) {
     chooseMathWay (state) {
-      console.log(state)
-      updateMatchType({
+      this.commonSetApi({
+        pageType: 'saveSeniorset',
         matchtype: state
         matchtype: state
-      }).then((res) => {
-        if (res.data.status === 1) {
-          this.$emit('update')
-        }
       })
       })
     },
     },
-    // 添加附加词
-    addAttachWordsFn () {
-      this.attachWordsData.push('')
-    },
     // 保存区域修改数据
     // 保存区域修改数据
     saveAreaData (data) {
     saveAreaData (data) {
-      updateArea(data).then((res) => {
-        if (res.data.status === 1) {
+      console.log(data)
+      setAreaBuyer({
+        vSwitch: 'm',
+        area: JSON.stringify(data)
+      }).then((res) => {
+        if (res.data.doSuccess) {
           this.$emit('update')
           this.$emit('update')
           this.dialog.area = false
           this.dialog.area = false
         } else {
         } else {
@@ -238,12 +197,14 @@ export default {
         }
         }
       })
       })
     },
     },
-    // 保存采购单位行业数据
+    // 保存采购单位行业数据及匹配未分类行业数据
     saveBuyClassData (data) {
     saveBuyClassData (data) {
-      updateBuyClass({
-        buyclass: data
+      console.log(data)
+      setAreaBuyer({
+        vSwitch: 'm',
+        industry: data.toString()
       }).then((res) => {
       }).then((res) => {
-        if (res.data.status === 1) {
+        if (res.data.doSuccess) {
           this.$emit('update')
           this.$emit('update')
           this.dialog.buyClass = false
           this.dialog.buyClass = false
         } else {
         } else {
@@ -254,14 +215,29 @@ export default {
         }
         }
       })
       })
     },
     },
-    // 保存采购单位行业数据
+    // 保存未匹配采购单位行业
+    saveOtherBuyClass (data) {
+      console.log(data, '其他')
+      this.commonSetApi({
+        pageType: 'other_buyerclass',
+        other: data.selected ? 1 : 0
+      })
+    },
+    // 保存信息类型数据
     saveInfoTypeData (data) {
     saveInfoTypeData (data) {
-      updateInfoType({
-        infotype: data
-      }).then((res) => {
-        if (res.data.status === 1) {
+      this.commonSetApi({
+        pageType: 'infoType',
+        infoTypeArr: data.toString()
+      }, () => {
+        this.dialog.infoType = false
+      })
+    },
+    // 设置接口
+    commonSetApi (data, callback) {
+      setUserInfoVip(data).then((res) => {
+        if (res.flag) {
           this.$emit('update')
           this.$emit('update')
-          this.dialog.infoType = false
+          callback && callback()
         } else {
         } else {
           this.$message({
           this.$message({
             message: '修改失败',
             message: '修改失败',

+ 0 - 289
src/views/subscribe/components/scope/Add.vue

@@ -1,289 +0,0 @@
-<template>
-  <div class="keywords">
-    <div class="key-title">
-      <span>{{datas.title}}</span>
-    </div>
-    <div class="key-content">
-      <div class="item">
-        <div class="item-label item-label-required">关键词:</div>
-        <div class="item-value">
-          <el-input class="custom-long-input" v-model="cur.key" maxlength="20" placeholder="请输入关键词"></el-input>
-        </div>
-      </div>
-      <div class="item">
-        <div class="item-label"></div>
-        <div class="item-value item-keywords-value">
-          <div>
-            <div class="add-word-list" v-for="(add,index) in cur.appendkey" :key="'add' + index">
-              <el-input v-model.trim="cur.appendkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入附加词"></el-input>
-            </div>
-            <div class="add-tag" @click="addAttachWordsFn">+添加附加词</div>
-          </div>
-          <div>
-            <div class="add-word-list" v-for="(not,index) in cur.notkey" :key="'0' + index">
-              <el-input v-model.trim="cur.notkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入排除词"></el-input>
-            </div>
-            <div class="add-tag" @click="addExcludeWordsFn">+添加排除词</div>
-          </div>
-        </div>
-      </div>
-      <div class="item">
-        <div class="item-label"></div>
-        <div class="item-value">
-          <button type="button" :disabled="keyDisabled" class="save-btn" @click="submitKeywords">保存关键词</button>
-          <div class="keywords-help">
-            <p>例:某公司主管业务为软件系统开发 </p>
-            <p>关键词:目标信息钟的关键性词语,如“软件系统” </p>
-            <p>附加词:与关键词形成一体/组合,便于查找准确信息,如“开发”</p>
-            <p>排除词:与关键词互斥,可排除一部分非目标信息,如“运维”</p>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-<script>
-import { Input, Button } from 'element-ui'
-export default {
-  name: 'scope-add',
-  props: {
-    datas: {
-      title: String,
-      keyList: Array
-    }
-  },
-  components: {
-    [Input.name]: Input,
-    [Button.name]: Button
-  },
-  data () {
-    return {
-      // 当前输入框的数据
-      cur: {
-        classify: '',
-        key: '',
-        appendkey: [],
-        notkey: []
-      }
-    }
-  },
-  computed: {
-    keyDisabled () {
-      return !this.cur.key
-    }
-  },
-  mounted () {},
-  methods: {
-    // 输入框为空移除
-    removeEmptyInput (arr) {
-      for (let i = 0; i < arr.length; i++) {
-        // eslint-disable-next-line
-        if (arr[i] === '' || arr[i] === null || typeof (arr[i]) === undefined) {
-          arr.splice(i, 1)
-          i = i - 1
-        }
-      }
-      return arr
-    },
-    // 保存数据
-    saveCurData () {
-      const data = JSON.parse(JSON.stringify(this.datas.keyList))
-      const obj = {
-        key: this.cur.key.split(' '),
-        notkey: this.cur.notkey,
-        appendkey: this.cur.appendkey
-      }
-      data.push(obj)
-      return data
-    },
-    // 提交关键词
-    submitKeywords () {
-      const keyArr = this.getKeyTotalArray()
-      if (keyArr.indexOf(this.cur.key) > -1) {
-        return this.$message({
-          type: 'warning',
-          message: '关键词不能重复'
-        })
-      }
-      // 判断附加词是否重复
-      if (this.getArrIsRepeat(this.cur.appendkey)) {
-        return this.$message({
-          type: 'warning',
-          message: '设置的附加词重复,请调整后再添加'
-        })
-      }
-      // 判断排除词是否重复
-      if (this.getArrIsRepeat(this.cur.notkey)) {
-        return this.$message({
-          type: 'warning',
-          message: '设置的排除词重复,请调整后再添加'
-        })
-      }
-      const params = this.saveCurData()
-      // 通知父组件保存数据
-      this.$emit('updateKey', params)
-    },
-    // 添加附加词
-    addAttachWordsFn () {
-      const arr = this.cur.appendkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入附加词',
-          type: 'warning'
-        })
-      } else {
-        this.cur.appendkey.push('')
-      }
-    },
-    // 添加排除词
-    addExcludeWordsFn () {
-      const arr = this.cur.notkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入排除词',
-          type: 'warning'
-        })
-      } else {
-        this.cur.notkey.push('')
-      }
-    },
-    // 判断附加词排除词输入框是否有空
-    inputIsEmpty (arr) {
-      return arr.some((v) => !v)
-    },
-    // 获取所有关键词
-    getKeyTotalArray () {
-      const keysArr = []
-      this.datas.keyList.forEach((s) => {
-        if (s && s.key) {
-          keysArr.push(s.key.toString().replace(',', ' '))
-        }
-      })
-      console.log(keysArr)
-      return keysArr
-    },
-    // 判断附加词、排除词是否重复
-    getArrIsRepeat (arr) {
-      this.removeEmptyInput(arr)
-      return (new Set(arr)).size !== arr.length
-    },
-    // 清空输入框 供父组件请求成功后刷新
-    clearInputData () {
-      this.cur.key = ''
-      this.cur.appendkey = []
-      this.cur.notkey = []
-    }
-  }
-}
-</script>
-<style lang="scss" scoped>
-.keywords{
-  .key-title{
-    padding-bottom: 8px;
-    font-size: 18px;
-    color: #1d1d1d;
-    line-height: 28px;
-    border-bottom: 1px solid #ececec;
-  }
-  .key-content{
-    padding: 20px 0;
-    .item{
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      margin-bottom: 10px;
-    }
-    .item-label{
-      margin-right: 8px;
-      min-width: 120px;
-      height: 40px;
-      color: #1d1d1d;
-      font-size: 14px;
-      line-height: 40px;
-      text-align: right;
-    }
-    .item-label-required:before{
-      content: "*";
-      color: #f56c6c;
-      margin-right: 2px;
-    }
-    .item-value{
-      width: 352px;
-    }
-    .custom-long-input{
-      width: 352px;
-    }
-    .custom-short-input{
-      width: 170px;
-    }
-    .item-other{
-      display: flex;
-      justify-content: center;
-      margin-bottom: 10px;
-    }
-    .item-other-value{
-      margin-top: 8px;
-    }
-    .math-tips{
-      margin-top: 4px;
-      font-size: 12px;
-      color: #999999;
-      line-height: 20px;
-    }
-    .radio-item{
-      margin-bottom: 10px;
-    }
-    .item-keywords-value{
-      display: flex;
-      justify-content: space-between;
-    }
-    .add-tag{
-      width: 170px;
-      height: 40px;
-      line-height: 40px;
-      color: #2cb7ca;
-      border-radius: 6px;
-      text-align: center;
-      cursor: pointer;
-      color: #2cb7ca;
-      border: 1px dashed #2cb7ca;
-    }
-    .add-word-list{
-      margin-bottom: 6px;
-    }
-    .save-btn{
-      width: 352px;
-      height: 46px;
-      line-height: 46px;
-      font-size: 16px;
-      background: #2cb7ca;
-      border-radius: 6px;
-      color: #fff;
-      &:disabled{
-        opacity: 0.5;
-        cursor: not-allowed;
-      }
-    }
-    .keywords-help{
-      width: 412px;
-      margin-top: 20px;
-      font-size: 12px;
-      color: #999999;
-      line-height: 20px;
-      text-align: justify;
-    }
-  }
-  // element-ui样式修改
-  ::v-deep.el-input__inner{
-    font-size: 14px;
-    color: #1D1D1D;
-    border-color: #ececec;
-  }
-  ::v-deep.el-input__inner:hover{
-    border-color: #ececec;
-  }
-  ::v-deep.el-input__inner:focus{
-    border-color: #2cb7ca;
-  }
-}
-</style>

+ 416 - 64
src/views/subscribe/components/scope/Edit.vue

@@ -1,98 +1,346 @@
 <template>
 <template>
-  <div class="keywords-edit">
-    <div class="item">
-      <div class="item-label item-label-required">关键词:</div>
-      <div class="item-value">
-        <el-input class="custom-long-input" v-model="cur.key" maxlength="20" placeholder="请输入关键词"></el-input>
+  <div class="edit-form">
+    <div class="input-box">
+      <div class="input-box-title">{{title}}关键词组</div>
+      <div class="item">
+        <div class="item-label">关键词:</div>
+        <div class="item-value">
+          <el-input
+            type="textarea"
+            autosize
+            resize="none"
+            placeholder="请输入关键词,多个关键词用空格隔开,例如:税务局 软件"
+            debounce="600"
+            maxlength="200"
+            @input="keywordsInput"
+            v-model="cur.key">
+          </el-input>
+        </div>
       </div>
       </div>
-    </div>
-    <div class="item">
-      <div class="item-label"></div>
-      <div class="item-value item-keywords-value">
-        <div>
-          <div class="add-word-list" v-for="(add,index) in cur.appendkey" :key="'add' + index">
-            <el-input v-model.trim="cur.appendkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入附加词"></el-input>
+      <div class="item" v-if="sameWordsList && sameWordsList.length > 0">
+        <div class="item-label"></div>
+        <div class="item-value">
+          <div class="recommend">
+            <div class="recommend-title">
+              <span>相似推荐</span>
+              <span class="batch-btn" v-if="sameWordsList && sameWordsList.length > 9" @click="getRecommendFn"><i class="el-icon-refresh"></i> 换一批</span>
+            </div>
+            <div class="recommend-main">
+              <div class="r-list"
+              :class="{active: s.selected}"
+              v-for="(s, j) in sameWordsList.slice(0, 9)"
+              :key="'00' + j"
+              @click="addSameItem($event, s)">{{s.word}}</div>
+            </div>
           </div>
           </div>
-          <div class="add-tag" @click="addAppendKey">+添加附加词</div>
         </div>
         </div>
-        <div>
-          <div class="add-word-list" v-for="(not,index) in cur.notkey" :key="'0' + index">
-            <el-input v-model.trim="cur.notkey[index]" class="custom-short-input" maxlength="20" placeholder="请输入排除词"></el-input>
-          </div>
-          <div class="add-tag" @click="addNotKey">+添加排除词</div>
+      </div>
+      <div class="item">
+        <div class="item-label">匹配模式:</div>
+        <div class="item-value">
+          <el-radio-group v-model="cur.matchway" @change="chooseMatchWay($event)">
+            <el-radio name="matchway" :label="1">模糊<span class="math-tips">(包含其中1个关键词即可)</span></el-radio>
+            <el-radio  name="matchway" :label="0">精准<span class="math-tips">(同时包含所有关键词)</span></el-radio>
+          </el-radio-group>
+        </div>
+      </div>
+      <div class="item" v-if="showNotWay" key="notway">
+        <div class="item-label">排除词:</div>
+        <div class="item-value">
+          <el-input
+            type="textarea"
+            autosize
+            resize="none"
+            placeholder="请输入排除词,多个排除词用空格隔开,不希望接收,与关键词互斥"
+            v-model="cur.notkey">
+          </el-input>
+        </div>
+      </div>
+      <div class="item" v-else key="notway">
+        <div class="item-label"></div>
+        <div class="item-value">
+          <div class="add-words-btn" @click="showNotWay = true">+添加排除词(不希望接收,与关键词互斥)</div>
         </div>
         </div>
       </div>
       </div>
+      <div>
+        <button type="button" :disabled="keyDisabled" class="save-btn" @click="submitKeywords">保存</button>
+      </div>
     </div>
     </div>
+    <!-- 关键词重复提示 -->
+    <el-dialog
+      custom-class="small-dialog"
+      title="新增关键词组"
+      :visible.sync="dialog.repeat"
+      width="380px"
+      center
+      :show-close="false"
+      append-to-body
+      >
+      <p class="dialog-text">该组关键词已存在,请勿重复添加</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button class="know-btn" type="primary" @click="dialog.repeat = false">我知道了</el-button>
+      </span>
+    </el-dialog>
   </div>
   </div>
 </template>
 </template>
 <script>
 <script>
-import { Input } from 'element-ui'
+import { Input, Button, RadioGroup, Radio, Dialog } from 'element-ui'
+import { getRecommend } from '@/api/modules'
+import { debounce } from '@/utils/'
+import $bus from '@/utils/bus'
+/* eslint-disable */
+Array.prototype.indexOf = function (val) {
+  for (var i = 0; i < this.length; i++) {
+    if (this[i] === val) return i
+  }
+  return -1
+}
+Array.prototype.remove = function (val) {
+  var index = this.indexOf(val)
+  if (index > -1) {
+    this.splice(index, 1)
+  }
+}
+/* eslint-disable */
 export default {
 export default {
   name: 'keywords-edit',
   name: 'keywords-edit',
   components: {
   components: {
-    [Input.name]: Input
+    [Input.name]: Input,
+    [Button.name]: Button,
+    [RadioGroup.name]: RadioGroup,
+    [Radio.name]: Radio,
+    [Dialog.name]: Dialog
   },
   },
   props: {
   props: {
-    datas: {
-      key: Array,
-      notkey: Array,
-      appendkey: Array
-    }
+    datas: Array,
+    title: String,
+    keywords: Object,
+    keyIndex: Number
   },
   },
   data () {
   data () {
     return {
     return {
       cur: {
       cur: {
         key: '',
         key: '',
-        appendkey: [],
-        notkey: []
-      }
+        appendkey: '',
+        notkey: '',
+        matchway: 1
+      },
+      dialog: {
+        repeat: false
+      },
+      showNotWay: false, // 是否展示添加排除词输入框
+      sameWordsList: [] // 相似推荐数据
     }
     }
   },
   },
   watch: {},
   watch: {},
-  computed: {},
+  computed: {
+    keyDisabled () {
+      return !this.cur.key
+    }
+  },
   created () {
   created () {
-    this.getParentData()
+    if (this.keywords) {
+      if (this.keywords.key && this.keywords.key.length > 0) {
+        if (this.keywords.appendkey) {
+          this.cur.key = this.keywords.key.join(' ') + ' ' + this.keywords.appendkey.join(' ')
+        } else {
+          this.cur.key = this.keywords.key.join(' ')
+        }
+      }
+      if (this.keywords.notkey && this.keywords.notkey.length > 0) {
+        this.showNotWay = true
+        this.cur.notkey = this.keywords.notkey.join(' ')
+      }
+      if (this.keywords.matchway) {
+        this.cur.matchway = 1
+      } else {
+        this.cur.matchway = 0
+      }
+    }
   },
   },
   mounted () {},
   mounted () {},
   methods: {
   methods: {
-    // 获取父组件keyList传来的数据 并进行格式转化渲染到输入框
-    getParentData () {
-      // 需用JSON.parse、JSON.stringify深拷贝原数组数据
-      const datas = JSON.parse(JSON.stringify(this.datas))
-      this.cur.key = datas.key.join(' ')
-      this.cur.notkey = datas.notkey
-      this.cur.appendkey = datas.appendkey
+    // 关键词推荐数量查询
+    keywordsInput () {
+      if (this.cur.key) {
+        setTimeout(() => {
+          this.getRecommendFn()
+        }, 1000)
+      }
     },
     },
-    addAppendKey () {
-      const arr = this.cur.appendkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入附加词',
-          type: 'warning'
-        })
+    // 获取相似项目推荐
+    getRecommendFn: debounce(function () {
+      getRecommend({
+        count: 20,
+        value: this.cur.key
+      }).then(res => {
+        this.sameWordsList = []
+        if (res.length > 0) {
+          res.forEach(item => {
+            item.selected = false
+          })
+          const m = this.cur.key.split(' ')
+          m.forEach(v => {
+            res.forEach(s => {
+              if (v === s.word) {
+                s.selected = true
+              }
+            })
+          })
+        }
+        this.sameWordsList = res || []
+      })
+    }, 500),
+    chooseMatchWay () {
+      // console.log(state, this.cur.matchway)
+    },
+    // 添加订阅推荐到输入框
+    addSameItem (e, item) {
+      item.selected = !item.selected
+      const m = this.cur.key.split(' ')
+      if (item.selected) {
+        m.push(item.word)
+        this.cur.key = m.join(' ')
       } else {
       } else {
-        this.cur.appendkey.push('')
+        m.remove(item.word)
+        this.cur.key = m.join(' ')
       }
       }
     },
     },
-    addNotKey () {
-      const arr = this.cur.notkey
-      if (this.inputIsEmpty(arr)) {
-        this.$message({
-          message: '请输入排除词',
-          type: 'warning'
-        })
+    // 保存数据
+    saveCurData () {
+      const data = JSON.parse(JSON.stringify(this.datas)) || []
+      const arr = this.cur.key.split(' ')
+      const keyArr = arr[0].split(',')
+      const appendArr = arr.length > 1 ? arr.slice(1) : null
+      if (this.title === '新增') {
+        const obj = {
+          key:  keyArr,
+          notkey:  this.cur.notkey ? this.cur.notkey.split(' ') : [],
+          appendkey: appendArr,
+          matchway: this.cur.matchway,
+          updatetime: Math.round(new Date() / 1000)
+        }
+        data.push(obj)
       } else {
       } else {
-        this.cur.notkey.push('')
+        data[this.keyIndex].key = []
+        data[this.keyIndex].notkey = null
+        data[this.keyIndex].appendkey = null
+        data[this.keyIndex].key = keyArr
+        data[this.keyIndex].appendkey = appendArr
+        data[this.keyIndex].notkey = this.cur.notkey ? this.cur.notkey.split(' ') : null,
+        data[this.keyIndex].matchway = this.cur.matchway,
+        data[this.keyIndex].updatetime = Math.round(new Date() / 1000)
+      }
+      return data
+    },
+    // 提交数据
+    submitKeywords () {
+      const isChange = this.getISChange()
+      console.log(isChange, '改没改')
+      if (isChange) {
+        const params = this.saveCurData()
+        this.$emit('closeForm', {
+          data: params,
+          index: this.keyIndex
+        })
+        return
+      }
+      const isRepeat = this.getRepeatFn()
+      // console.log(isRepeat)
+      if (isRepeat) {
+        this.dialog.repeat = true
+        return
       }
       }
+      const params = this.saveCurData()
+      this.$emit('closeForm', {
+        data: params,
+        index: this.keyIndex
+      })
+      $bus.$emit('updateScope', params)
     },
     },
-    inputIsEmpty (arr) {
-      return arr.some((v) => !v)
+    // 判断有无重复
+    getRepeatFn () {
+      const datas = JSON.parse(JSON.stringify(this.datas)) || []
+      const arr = this.cur.key.split(' ')
+      const keyArr = arr[0].split(',')
+      const appendArr = arr.length > 1 ? arr.slice(1) : null
+      const notKeyArr = this.cur.notkey ? this.cur.notkey.split(',') : null
+      const matchWay = this.cur.matchway
+      let isRepeat
+      for (let i = 0; i < datas.length; i++) {
+        const v = datas[i]
+        v.appendkey = v.appendkey && v.appendkey.length > 0 ? v.appendkey : null
+        v.notkey = v.notkey && v.notkey.length > 0 ? v.notkey : null
+        const rKey = JSON.stringify(v.key) === JSON.stringify(keyArr)
+        const rAppend = JSON.stringify(v.appendkey) === JSON.stringify(appendArr)
+        const rNot = JSON.stringify(v.notkey) === JSON.stringify(notKeyArr)
+        const rWay = v.matchway === this.cur.matchway
+        // console.log(JSON.stringify(v.key), JSON.stringify(v.appendkey), JSON.stringify(v.notkey), JSON.stringify(keyArr), JSON.stringify(appendArr), JSON.stringify(notKeyArr))
+        // console.log(rKey, rAppend, rNot, rWay)
+        if (rKey && rAppend && rNot && rWay) {
+          isRepeat = rKey && rAppend && rNot && rWay
+          break
+        }
+      }
+      return isRepeat
+    },
+    // 判断是否更改
+    getISChange () {
+      if (this.title === '修改') {
+        const obj = this.keywords
+        obj.notkey = obj.notkey && obj.notkey.length > 0 ? obj.notkey : null
+        obj.appendkey = obj.appendkey && obj.appendkey.length > 0 ? obj.appendkey : null
+        const arr = this.cur.key.split(' ')
+        const keyArr = arr[0].split(',')
+        const appendArr = arr.length > 1 ? arr.slice(1) : null
+        const notKeyArr = this.cur.notkey ? this.cur.notkey.split(',') : null
+        const matchWay = this.cur.matchway
+        const cKey = JSON.stringify(obj.key) === JSON.stringify(keyArr)
+        const cAppend = JSON.stringify(obj.appendkey) === JSON.stringify(appendArr)
+        const cNotKey = JSON.stringify(obj.notkey) === JSON.stringify(notKeyArr)
+        const cWay = obj.matchway === matchWay
+        const notChange = cKey && cAppend && cNotKey && cWay
+        console.log(cKey, cAppend, cNotKey, cWay, JSON.stringify(obj.appendkey), JSON.stringify(appendArr), JSON.stringify(obj.notkey), JSON.stringify(notKeyArr), obj.matchway, matchWay)
+        return notChange
+      }
     }
     }
   }
   }
 }
 }
 </script>
 </script>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-.keywords-edit{
+.edit-form{
+  margin-top: 20px;
+  .classify-title{
+    padding-bottom: 20px;
+    .icon-edit,.icon-delete{
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      background-repeat: no-repeat;
+      background-position: center center;
+      cursor: pointer;
+    }
+    .icon-edit{
+      margin: 0 10px;
+      background-image: url('~@/assets/images/icon-edit.png');
+      background-size: contain;
+    }
+    .icon-delete{
+      background-image: url('~@/assets/images/icon-delete.png');
+      background-size: contain;
+    }
+  }
+  .input-box{
+    padding: 20px 30px;
+    background: #f6f7f9;
+    border-radius: 8px;
+  }
+  .input-box-title{
+    margin-bottom: 16px;
+    font-size: 16px;
+    text-align: center;
+    color: #1d1d1d;
+  }
   .item{
   .item{
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
@@ -113,8 +361,57 @@ export default {
     color: #f56c6c;
     color: #f56c6c;
     margin-right: 2px;
     margin-right: 2px;
   }
   }
-  .item-value{
+  .recommend{
+    padding: 16px;
+    background: #fff;
+    border-radius: 4px;
+    .recommend-title{
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      color: #686868;
+      font-size: 14px;
+      .batch-btn{
+        color: #2cb7ca;
+        font-size: 14px;
+        cursor: pointer;
+      }
+    }
+    .recommend-main{
+      display: flex;
+      align-items: center;
+      flex-wrap: wrap;
+      .r-list{
+        padding: 0 8px;
+        margin: 12px 12px 0 0;
+        height: 22px;
+        line-height: 22px;
+        background: #f5f6f7;
+        border-radius: 4px;
+        color: #1d1d1d;;
+        font-size: 14px;
+        cursor: pointer;
+      }
+      .active{
+        background: #2cb7ca;
+        color: #fff;
+      }
+    }
+  }
+  .add-words-btn{
     width: 352px;
     width: 352px;
+    height: 40px;
+    line-height: 40px;
+    border: 1px dashed #2cb7ca;
+    border-radius: 6px;
+    background: #fff;
+    color: #2cb7ca;
+    text-align: center;
+    cursor: pointer;
+    font-size: 14px;
+  }
+  .item-value{
+    flex: 1;
   }
   }
   .custom-long-input{
   .custom-long-input{
     width: 352px;
     width: 352px;
@@ -122,6 +419,23 @@ export default {
   .custom-short-input{
   .custom-short-input{
     width: 170px;
     width: 170px;
   }
   }
+  .item-other{
+    display: flex;
+    justify-content: center;
+    margin-bottom: 10px;
+  }
+  .item-other-value{
+    margin-top: 8px;
+  }
+  .math-tips{
+    margin-top: 4px;
+    font-size: 12px;
+    color: #999999;
+    line-height: 20px;
+  }
+  .radio-item{
+    margin-bottom: 10px;
+  }
   .item-keywords-value{
   .item-keywords-value{
     display: flex;
     display: flex;
     justify-content: space-between;
     justify-content: space-between;
@@ -140,20 +454,58 @@ export default {
   .add-word-list{
   .add-word-list{
     margin-bottom: 6px;
     margin-bottom: 6px;
   }
   }
-  // element-ui样式修改
-  ::v-deep.el-input__inner{
+  .keywords-help{
+    width: 412px;
+    margin-top: 20px;
+    font-size: 12px;
+    color: #999999;
+    line-height: 20px;
+    text-align: justify;
+  }
+}
+// element-ui样式修改
+::v-deep {
+  .el-input__inner,
+  .el-textarea__inner{
     font-size: 14px;
     font-size: 14px;
     color: #1D1D1D;
     color: #1D1D1D;
     border-color: #ececec;
     border-color: #ececec;
-    padding-left: 16px!important;
-    border-radius: 6px!important;
-    background: #fff!important;
   }
   }
-  ::v-deep.el-input__inner:hover{
+  .el-textarea__inner{
+    padding: 8px 15px;
+  }
+  .el-input__inner:hover,
+  .el-textarea__inner:hover{
     border-color: #ececec;
     border-color: #ececec;
   }
   }
-  ::v-deep.el-input__inner:focus{
+  .el-input__inner:focus,
+  .el-textarea__inner:focus{
     border-color: #2cb7ca;
     border-color: #2cb7ca;
   }
   }
+  .el-radio{
+    color: #1d1d1d;
+    font-size: 14px;
+  }
+  .el-radio__inner{
+    width: 20px;
+    height: 20px;
+  }
+  .el-radio__input.is-checked .el-radio__inner{
+    border: 0;
+    background: transparent;
+    width: 20px;
+    height: 20px;
+    background: url('~@/assets/images/icon-checked.png') no-repeat center center;
+    background-size: contain;
+  }
+  .el-radio__inner::after{
+    background: transparent;
+  }
+  .el-radio__input.is-checked+.el-radio__label{
+    color: #1d1d1d;
+  }
+  .el-radio__inner:hover{
+    border-color: #ececec;
+  }
 }
 }
 </style>
 </style>

+ 205 - 139
src/views/subscribe/components/scope/List.vue

@@ -1,49 +1,83 @@
 <template>
 <template>
   <div class="classify">
   <div class="classify">
+    <div class="key-title">
+      <span>{{datas.title}}</span>
+    </div>
     <div class="classify-list">
     <div class="classify-list">
-      <div class="classify-content">
-        <div class="list" v-for="(v, i) in datas.keyList" :key="'0' + i">
-          <div class="list-box">
-            <div class="list-value">
-              <p>关键词: {{v.key.join(' ') || '--'}}</p>
-              <p>附加词: {{v.appendkey.join('、') || '--'}}</p>
-              <p>排除词: {{v.notkey.join('、') || '--'}}</p>
+      <div class="classify-content" v-if="newWordsList && newWordsList.length > 0">
+        <div class="add-words-box" @click="addKeyFn()" v-if="!addShowForm" key="add">+新增关键词组</div>
+        <div style="width:100%;" v-else key="add">
+          <Edit
+            :datas="newWordsList"
+            title="新增"
+            :keywords="{key:[], appendkey:[],notkey:[],showForm: true,matchway: 1}"
+            :keyIndex="newWordsList.length"
+            @closeForm="addShowForm = false"
+          >
+          </Edit>
+        </div>
+        <div class="list" v-for="(v, i) in newWordsList" :key="'0' + i" style="width:100%;">
+          <Edit
+            :datas="newWordsList"
+            title="修改"
+            :keywords="v"
+            :keyIndex="i"
+            @closeForm="onCloseForm"
+            v-if="v.showForm" key="edit">
+          </Edit>
+          <div class="list-box" v-else key="edit">
+            <div class="list-left yellow-box" v-if="v.matchway">模糊</div>
+            <div class="list-left blue-box" v-else>精准</div>
+            <div class="list-middle">
+              <div class="list-keywords" v-if="v.appendkey">
+                {{v.key.join(' ') + ' ' +  v.appendkey.join(' ')}}
+              </div>
+              <div class="list-keywords" v-else >{{v.key.join(' ')}}</div>
+              <p class="list-notkey" v-if="v.notkey && v.notkey.length > 0">排除词: {{v.notkey.join(' ')}}</p>
             </div>
             </div>
-            <div class="list-icon" @click="deleteKeyFn(i)"></div>
-            <div class="list-edit" @click="editKeyFn(v, i)">
-              编辑
-              <i class="tri-down"></i>
+            <div class="list-right">
+              <span class="icon-edit" @click="editKeyFn(v, i)"></span>
+              <span class="icon-delete" @click="deleteKeyFn(i)"></span>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
-        <div class="words-add" @click="addKeyFn()">
-          <i class="el-icon-plus"></i>
-        </div>
+      </div>
+      <div v-else>
+        <Edit
+            title="新增"
+            :datas="newWordsList"
+            :keywords="{key:[], appendkey:[],notkey:[],showForm: true,matchway:1}"
+            :keyIndex="newWordsList.length"
+            @closeForm="addShowForm = false"
+          >
+          </Edit>
       </div>
       </div>
     </div>
     </div>
-    <!-- 关键词dialog -->
+    <!-- 删除关键词dialog -->
     <el-dialog
     <el-dialog
-      custom-class="sub-dialog"
-      :visible.sync="dialog.editKey"
+      custom-class="sub-dialog small-dialog"
+      :visible.sync="dialog.delKey"
       :close-on-click-modal="false"
       :close-on-click-modal="false"
       :show-close="false"
       :show-close="false"
+      top="30vh"
       center
       center
-      width="800px"
-      v-if="dialog.editKey"
+      width="380px"
+      v-if="dialog.delKey"
     >
     >
-      <KeyCard @onCancel="cancelEdit" @onConfirm="submitKeywords">
-        <div slot="header">修改关键词</div>
-        <div class="key-edit-content">
-          <ScopeEdit ref="keyEditRef" :datas="props"></ScopeEdit>
+      <div class="delete-class">
+        <div class="delete-class-header">删除关键词组</div>
+        <div class="delete-class-content">确定删除该关键词组吗?</div>
+        <div class="delete-class-footer">
+          <el-button type="primary" class="confirm" @click="confirmDeleteKeyFn">确定</el-button>
+          <el-button class="cancel" @click="dialog.delKey = false">取消</el-button>
         </div>
         </div>
-      </KeyCard>
+      </div>
     </el-dialog>
     </el-dialog>
   </div>
   </div>
 </template>
 </template>
 <script>
 <script>
 import { Tooltip, Dialog, Input, Button } from 'element-ui'
 import { Tooltip, Dialog, Input, Button } from 'element-ui'
-import KeyCard from '@/components/selector/SelectorCard'
-import ScopeEdit from './Edit'
+import Edit from './Edit'
 export default {
 export default {
   name: 'scope-list',
   name: 'scope-list',
   components: {
   components: {
@@ -51,11 +85,11 @@ export default {
     [Dialog.name]: Dialog,
     [Dialog.name]: Dialog,
     [Input.name]: Input,
     [Input.name]: Input,
     [Button.name]: Button,
     [Button.name]: Button,
-    KeyCard,
-    ScopeEdit
+    Edit
   },
   },
   props: {
   props: {
     datas: {
     datas: {
+      title: String,
       from: String,
       from: String,
       keyList: Array
       keyList: Array
     }
     }
@@ -63,7 +97,8 @@ export default {
   data () {
   data () {
     return {
     return {
       dialog: {
       dialog: {
-        editKey: false // 修改关键词弹框
+        editKey: false, // 修改关键词弹框
+        delKey: false
       },
       },
       // 传给dialog子组件的数据
       // 传给dialog子组件的数据
       props: {
       props: {
@@ -72,41 +107,74 @@ export default {
         key: [], // 关键词
         key: [], // 关键词
         notkey: [], // 排除词
         notkey: [], // 排除词
         appendkey: [] // 附加词
         appendkey: [] // 附加词
-      }
+      },
+      newWordsList: [],
+      addShowForm: false,
+      delIndex: null
     }
     }
   },
   },
   computed: {},
   computed: {},
-  mounted () {},
+  mounted () {
+    this.formatDatasList()
+  },
+  watch: {
+    'datas.keyList': function (newVal) {
+      if (newVal) {
+        this.formatDatasList()
+      }
+    }
+  },
   methods: {
   methods: {
+    // 排序
+    sortData (arr) {
+      return arr.sort((a, b) => {
+        return b.updatetime - a.updatetime
+      })
+    },
+    // 整理数据
+    formatDatasList () {
+      const lists = JSON.parse(JSON.stringify(this.datas.keyList))
+      this.sortData(lists)
+      lists.forEach(v => {
+        v.showForm = false
+      })
+      this.newWordsList = lists
+    },
+    onCloseForm (data) {
+      this.newWordsList[this.props.keyIndex].showForm = false
+    },
     // 取消编辑
     // 取消编辑
     cancelEdit () {
     cancelEdit () {
       this.dialog.editKey = false
       this.dialog.editKey = false
     },
     },
     // 添加关键词
     // 添加关键词
     addKeyFn () {
     addKeyFn () {
-      this.dialog.editKey = true
-      this.props.ways = 'add'
-      this.props.key = []
-      this.props.notkey = []
-      this.props.appendkey = []
+      this.addShowForm = true
     },
     },
-    // 删除单个关键词
+    // 删除单个关键词(打开确认弹框)
     deleteKeyFn (i) {
     deleteKeyFn (i) {
+      this.dialog.delKey = true
+      this.delIndex = i
+    },
+    confirmDeleteKeyFn () {
       // 需用JSON.parse、JSON.stringify深拷贝原数组数据
       // 需用JSON.parse、JSON.stringify深拷贝原数组数据
-      const data = JSON.parse(JSON.stringify(this.datas.keyList))
+      const data = this.newWordsList
       if (!data) return
       if (!data) return
-      data.splice(i, 1)
+      data.splice(this.delIndex, 1)
       this.$emit('updateKey', data)
       this.$emit('updateKey', data)
+      this.dialog.delKey = false
     },
     },
     // 编辑单个关键词
     // 编辑单个关键词
     editKeyFn (v, i) {
     editKeyFn (v, i) {
+      console.log(v, i)
       this.props.ways = 'edit'
       this.props.ways = 'edit'
-      this.dialog.editKey = true
+      // this.dialog.editKey = true
       // 把当前关键词传到dialog key,notkey,appendKey都为数组
       // 把当前关键词传到dialog key,notkey,appendKey都为数组
       this.props.key = v.key
       this.props.key = v.key
       this.props.notkey = v.notkey
       this.props.notkey = v.notkey
       this.props.appendkey = v.appendkey
       this.props.appendkey = v.appendkey
       this.props.keyIndex = i
       this.props.keyIndex = i
+      v.showForm = true
     },
     },
     // 清空传值
     // 清空传值
     clearPropsData () {
     clearPropsData () {
@@ -297,112 +365,110 @@ export default {
     }
     }
   }
   }
 }
 }
-.classify-list{
-  margin-bottom: 30px;
-  .icon-edit,.icon-delete{
-    display: inline-block;
-    width: 16px;
-    height: 16px;
-    background-repeat: no-repeat;
-    background-position: center center;
-    cursor: pointer;
-  }
-  .icon-edit{
-    margin: 0 10px;
-    background-image: url('~@/assets/images/icon-edit.png');
-    background-size: contain;
-  }
-  .icon-delete{
-    background-image: url('~@/assets/images/icon-delete.png');
-    background-size: contain;
+.classify{
+  .key-title{
+    padding-bottom: 8px;
+    font-size: 18px;
+    color: #1d1d1d;
+    line-height: 28px;
+    border-bottom: 1px solid #ececec;
   }
   }
-  .classify-content{
-    display: flex;
-    align-items: center;
-    justify-content: flex-start;
-    flex-wrap: wrap;
-    .list{
-      padding-top: 10px;
-      margin-right: 10px;
-      margin-bottom: 10px;
-      width: 160px;
-      height: 80px;
-      box-sizing: border-box;
-      &:hover .list-edit{
-        transform: scaleY(1);
-        transition: transform .1s;
-      }
-      &:hover .list-box{
-        border: 1px solid #2cb7ca;
-        box-sizing: border-box;
-      }
-    }
-    .list:nth-child(6n) {
-      margin-right: 0;
-    }
-    .list-box{
-      position: relative;
-      display: flex;
-      padding: 10px 10px 10px 16px;
-      border: 1px solid #ececec;
-      border-radius: 9px;
-      box-sizing: border-box;
-      cursor: pointer;
-    }
-    .list-edit{
-      transform: scaleY(0);
-      transition: transform .1s;
-      position: absolute;
-      top: -40px;
-      left: 50%;
-      margin-left: -22px;
-      padding: 10px;
-      color: #fff;
-      background: #1d1d1d;
-      border-radius: 4px;
-      font-size: 12px;
+  .classify-list{
+    margin-bottom: 30px;
+    .icon-edit,.icon-delete{
+      display: inline-block;
+      width: 16px;
+      height: 16px;
+      background-repeat: no-repeat;
+      background-position: center center;
       cursor: pointer;
       cursor: pointer;
-      .tri-down{
-        position: absolute;
-        bottom: -6px;
-        left: 50%;
-        margin-left: -5px;
-        width: 0;
-        height: 0;
-        border-left: 6px solid transparent;
-        border-right: 6px solid transparent;
-        border-top: 6px solid #1d1d1d;
-      }
     }
     }
-    .list-value{
-      width: 124px;
-      font-size: 12px;
-      line-height: 20px;
-      color: #000;
-      text-overflow: ellipsis;
-      overflow: hidden;
-      white-space: nowrap;
-      p{
-        @extend .list-value
-      }
+    .icon-edit{
+      margin: 0 10px;
+      background-image: url('~@/assets/images/icon-edit.png');
+      background-size: contain;
     }
     }
-    .list-icon{
-      @extend .icon-delete;
-      width: 12px;
-      height: 12px;
+    .icon-delete{
+      background-image: url('~@/assets/images/icon-delete.png');
+      background-size: contain;
     }
     }
-    .words-add{
-      width: 162px;
-      height: 80px;
+    .classify-content{
       display: flex;
       display: flex;
       align-items: center;
       align-items: center;
-      justify-content: center;
-      font-size: 24px;
-      border: 1px solid #ececec;
-      margin: 10px 0 0 0;
-      border-radius: 9px;
-      color: #c4c4c4;
-      cursor: pointer;
+      justify-content: flex-start;
+      flex-wrap: wrap;
+      .add-words-box{
+        width: 100%;
+        height: 38px;
+        margin-top: 20px;
+        line-height: 38px;
+        border: 1px dashed #2cb7ca;
+        border-radius: 6px;
+        color: #2cb7ca;
+        font-size: 14px;
+        text-align: center;
+        cursor: pointer;
+      }
+      .list-box{
+        width: 100%;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 12px 16px;
+        margin-top: 12px;
+        border: 1px solid #ececec;
+        border-radius: 8px;
+        box-sizing: border-box;
+        .list-left{
+          width: 40px;
+          height: 22px;
+          line-height: 22px;
+          margin-right: 12px;
+          font-size: 12px;
+          text-align: center;
+          box-sizing: border-box;
+        }
+        .yellow-box{
+          border: 1px solid #ff9f40;
+          color: #ff9f40;
+          border-radius: 4px;
+        }
+        .blue-box{
+          border: 1px solid #2cb7ca;
+          color: #2cb7ca;
+          border-radius: 4px;
+        }
+        .list-middle{
+          flex: 1;
+        }
+        .list-keywords{
+          color: #1d1d1d;
+          line-height: 22px;
+          font-size: 14px;
+        }
+        .list-addkey,
+        .list-notkey{
+          color: #686868;
+          line-height: 20px;
+          font-size: 12px;
+        }
+        .list-right{
+          margin-left:15px;
+        }
+      }
+      .words-add{
+        width: 162px;
+        height: 80px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 24px;
+        border: 1px solid #ececec;
+        margin: 10px 0 0 0;
+        border-radius: 9px;
+        color: #c4c4c4;
+        cursor: pointer;
+      }
     }
     }
   }
   }
 }
 }

+ 8 - 1
vue.config.js

@@ -48,7 +48,8 @@ module.exports = {
         }
         }
       },
       },
       '^/jypay': {
       '^/jypay': {
-        target: 'https://web2-jytest.jydev.jianyu360.com',
+        // target: 'https://web2-jytest.jydev.jianyu360.com',
+        target: 'http://127.0.0.1:86',
         changeOrigin: true,
         changeOrigin: true,
         logLevel: 'debug'
         logLevel: 'debug'
       },
       },
@@ -58,6 +59,12 @@ module.exports = {
         changeOrigin: true,
         changeOrigin: true,
         logLevel: 'debug'
         logLevel: 'debug'
       },
       },
+      '^/subscribepay': {
+        // target: 'https://web2-jytest.jydev.jianyu360.com',
+        target: 'http://127.0.0.1:86',
+        changeOrigin: true,
+        logLevel: 'debug'
+      },
       '^/salesLeads': {
       '^/salesLeads': {
         // target: 'https://web2-jytest.jydev.jianyu360.com',
         // target: 'https://web2-jytest.jydev.jianyu360.com',
         target: 'http://127.0.0.1:8881',
         target: 'http://127.0.0.1:8881',

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно