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

Merge branch 'feature/v1.0.36' into dev/v1.0.36_tsz

Signed-off-by: tangshizhe <48740614+tangshizhe@users.noreply.github.com>
tangshizhe 1 жил өмнө
parent
commit
4a9c064f9d
100 өөрчлөгдсөн 4504 нэмэгдсэн , 902 устгасан
  1. 1 1
      apps/bigmember_pc/index.html
  2. 8 0
      apps/bigmember_pc/src/api/modules/pay.js
  3. 25 0
      apps/bigmember_pc/src/api/modules/search.js
  4. 19 0
      apps/bigmember_pc/src/api/modules/user.js
  5. BIN
      apps/bigmember_pc/src/assets/images/article-mask/vip-expire-dialog-bg.png
  6. BIN
      apps/bigmember_pc/src/assets/images/icon/close-icon2x.png
  7. BIN
      apps/bigmember_pc/src/assets/images/icon/tip2.png
  8. 1 0
      apps/bigmember_pc/src/assets/style/common.scss
  9. 7 1
      apps/bigmember_pc/src/assets/style/pic-icon.scss
  10. 10 0
      apps/bigmember_pc/src/components/ad/activity-dialog.vue
  11. 20 8
      apps/bigmember_pc/src/components/article-item/ArticleItem.vue
  12. 7 1
      apps/bigmember_pc/src/components/collect-info/CollectInfo.vue
  13. 121 0
      apps/bigmember_pc/src/components/dialog/CheckPowerAndSwitch.vue
  14. 41 0
      apps/bigmember_pc/src/components/dialog/CheckUserDialog.vue
  15. 287 0
      apps/bigmember_pc/src/components/dialog/CheckVipExpire.vue
  16. 10 0
      apps/bigmember_pc/src/components/dialog/Dialog.vue
  17. 12 8
      apps/bigmember_pc/src/components/filter-items/AmountRangeSelector.vue
  18. 1 1
      apps/bigmember_pc/src/components/filter-items/AttachmentSelector.vue
  19. 1 4
      apps/bigmember_pc/src/components/filter-items/BasePowerLayout.vue
  20. 0 1
      apps/bigmember_pc/src/components/filter-items/CascadeContent.vue
  21. 2 2
      apps/bigmember_pc/src/components/filter-items/ChangeHandsDropdown.vue
  22. 2 2
      apps/bigmember_pc/src/components/filter-items/CheckboxGroupSelector.vue
  23. 3 4
      apps/bigmember_pc/src/components/filter-items/CommonCheckboxSelector.vue
  24. 11 10
      apps/bigmember_pc/src/components/filter-items/KeywordTagsSelector.vue
  25. 8 1
      apps/bigmember_pc/src/components/filter-items/KeywordTagsSelectorContent.vue
  26. 76 18
      apps/bigmember_pc/src/components/filter-items/OnecascadeContent.vue
  27. 2 1
      apps/bigmember_pc/src/components/filter-items/RegionCollapseSelector.vue
  28. 16 1
      apps/bigmember_pc/src/components/filter-items/SearchRangeDropdown.vue
  29. 22 14
      apps/bigmember_pc/src/components/filter-items/SearchScopeSelector.vue
  30. 32 5
      apps/bigmember_pc/src/components/forecast/ForeCast.vue
  31. 1 1
      apps/bigmember_pc/src/components/search-input/SearchInput.vue
  32. 286 0
      apps/bigmember_pc/src/components/selector-cascader/MoneySelection.vue
  33. 13 3
      apps/bigmember_pc/src/components/selector/InfoTypeSelector.vue
  34. 21 10
      apps/bigmember_pc/src/components/selector/InfoTypeSelectorContent.vue
  35. 16 3
      apps/bigmember_pc/src/components/selector/SearchTimeScopeSelector.vue
  36. 79 23
      apps/bigmember_pc/src/components/selector/TimeSelectorContent.vue
  37. 0 3
      apps/bigmember_pc/src/components/selector/timeDropdown.vue
  38. 1 1
      apps/bigmember_pc/src/router/modules/search.js
  39. 6 1
      apps/bigmember_pc/src/router/router-interceptors.js
  40. 9 0
      apps/bigmember_pc/src/utils/common.js
  41. 26 0
      apps/bigmember_pc/src/utils/format/info-type-transform.js
  42. 67 10
      apps/bigmember_pc/src/utils/format/search-bid-filter.js
  43. 2 0
      apps/bigmember_pc/src/views/article-content/pages/Article.vue
  44. 3 0
      apps/bigmember_pc/src/views/portrayal/EntPortrayal.vue
  45. 3 0
      apps/bigmember_pc/src/views/portrayal/EntSearchPortrayal.vue
  46. 9 6
      apps/bigmember_pc/src/views/portrayal/UnitPortrayal.vue
  47. 20 3
      apps/bigmember_pc/src/views/potential-for/PotenTial.vue
  48. 7 1
      apps/bigmember_pc/src/views/push-setting/index.vue
  49. 1 6
      apps/bigmember_pc/src/views/search/bidding/components/history-filter-dialog.vue
  50. 612 0
      apps/bigmember_pc/src/views/search/bidding/components/recommend-card.vue
  51. 33 13
      apps/bigmember_pc/src/views/search/bidding/components/search-bid-filter.vue
  52. 26 6
      apps/bigmember_pc/src/views/search/bidding/components/search-bid-header.vue
  53. 3 1
      apps/bigmember_pc/src/views/search/bidding/components/search-filter-header.vue
  54. 14 8
      apps/bigmember_pc/src/views/search/bidding/components/search-list-table.vue
  55. 14 12
      apps/bigmember_pc/src/views/search/bidding/constant/search-filters-bi.js
  56. 122 7
      apps/bigmember_pc/src/views/search/bidding/constant/search-filters.js
  57. 271 139
      apps/bigmember_pc/src/views/search/bidding/index.vue
  58. 550 146
      apps/bigmember_pc/src/views/search/bidding/model/base.js
  59. 101 0
      apps/bigmember_pc/src/views/search/bidding/model/modules/before-search.js
  60. 6 1
      apps/bigmember_pc/src/views/search/bidding/model/modules/data-add-actions.js
  61. 17 3
      apps/bigmember_pc/src/views/search/bidding/model/modules/data-export-actions.js
  62. 7 1
      apps/bigmember_pc/src/views/search/bidding/model/modules/filter-keywords.js
  63. 124 26
      apps/bigmember_pc/src/views/search/bidding/model/modules/filter.js
  64. 1 1
      apps/bigmember_pc/src/views/search/bidding/model/modules/join-bid-actions.js
  65. 305 0
      apps/bigmember_pc/src/views/search/bidding/model/modules/recommend-card.js
  66. 6 1
      apps/bigmember_pc/src/views/search/bidding/model/modules/save-filter-actions.js
  67. 16 4
      apps/bigmember_pc/src/views/search/bidding/model/modules/tabs.js
  68. 15 8
      apps/bigmember_pc/src/views/search/components/SearchHeader.vue
  69. 1 1
      apps/bigmember_pc/src/views/search/components/keyword-tags.vue
  70. 7 2
      apps/bigmember_pc/src/views/search/components/search-header-card.vue
  71. 42 34
      apps/bigmember_pc/src/views/search/components/search-schema-filter.vue
  72. 3 3
      apps/bigmember_pc/src/views/search/ent/model/base.js
  73. 81 175
      apps/bigmember_pc/src/views/subscribe/SubPush.vue
  74. 2 2
      apps/bigmember_pc/src/views/subscribe/constant/search-filters.js
  75. 20 2
      apps/bigmember_pc/src/views/workspace/dashboard.vue
  76. 2 2
      apps/bigmember_pc/vite.config.js
  77. 11 1
      apps/mobile/src/api/modules/ent.js
  78. 8 0
      apps/mobile/src/api/modules/pay.js
  79. BIN
      apps/mobile/src/assets/image/tip/vip-expire-dialog-bg.png
  80. 55 32
      apps/mobile/src/components/ad/pop-screen/index.vue
  81. 13 21
      apps/mobile/src/components/search/bidding/filters.vue
  82. 7 32
      apps/mobile/src/components/treasure-box/CommonUse.vue
  83. 3 0
      apps/mobile/src/views/article/content.vue
  84. 106 0
      apps/mobile/src/views/identity/components/CheckPowerAndSwitch.vue
  85. 43 0
      apps/mobile/src/views/identity/components/CheckUserDialog.vue
  86. 275 0
      apps/mobile/src/views/identity/components/CheckVipExpire.vue
  87. 3 0
      apps/mobile/src/views/search/middle/bidding/index.vue
  88. 148 47
      apps/mobile/src/views/search/result/bidding/index.vue
  89. 3 0
      apps/mobile/src/views/tabbar/Box.vue
  90. 15 2
      apps/mobile/src/views/tabbar/Home.vue
  91. 3 0
      apps/mobile/src/views/tabbar/Layout.vue
  92. 3 0
      apps/mobile/src/views/tabbar/Message.vue
  93. 3 0
      apps/mobile/src/views/tabbar/Mine.vue
  94. 3 0
      apps/mobile/src/views/tabbar/Subscribe.vue
  95. 15 9
      data/data-models/modules/article/model/content.js
  96. 0 1
      data/data-models/modules/quick-search/plugins/search-bid.js
  97. 1 1
      data/data-models/package.json
  98. 61 3
      packages/util/modules/format/str.js
  99. 1 1
      packages/util/package.json
  100. 8 0
      plugins/login-auth/example/index.html

+ 1 - 1
apps/bigmember_pc/index.html

@@ -24,7 +24,6 @@
       <link ignore href='https://jybx2-webtest.jydev.jianyu360.com/css/pc.css?v=6302' rel="stylesheet"/>
       <link ignore href='https://jybx2-webtest.jydev.jianyu360.com/pccss/public-nav-1200.css?v=6302' rel="stylesheet" type="text/css"/>
 
-      <script src=//cdn-common.jianyu360.com/cdn/lib/jquery/3.6.0/jquery.min.js></script>
       <link ignore rel="stylesheet" href="https://at.alicdn.com/t/c/font_624651_o2us2uwpt6b.css">
     <% } %>
 
@@ -33,6 +32,7 @@
       <link rel="stylesheet" href="<%= cdn.css[i] %>" />
       <% } %>
       <!-- 使用CDN的JS文件 -->
+      <script ignore src=//cdn-common.jianyu360.com/cdn/lib/jquery/3.6.0/jquery.min.js></script>
       <% for (var i in cdn && cdn.js) { %>
       <script type="text/javascript" src="<%= cdn.js[i] %>"></script>
       <% } %>

+ 8 - 0
apps/bigmember_pc/src/api/modules/pay.js

@@ -50,3 +50,11 @@ export function getUserAccountShow() {
     method: 'POST'
   })
 }
+
+export function vipRenewReminder(data) {
+  return request({
+    url: '/subscribepay/vipsubscribe/vipRenewReminder',
+    method: 'post',
+    data: qs.stringify(data)
+  })
+}

+ 25 - 0
apps/bigmember_pc/src/api/modules/search.js

@@ -34,3 +34,28 @@ export function deleteBiddingFilter(data) {
     data
   })
 }
+
+// 获取中国移动融创
+export function getCMCustomInfo () {
+  return request({
+    url: '/jylab/supsearch/searchPower',
+    method: 'post'
+  })
+}
+
+/**
+ * 检测当前账号是否在反爬虫白名单中
+ * 如果在反爬白名单,则空搜索刷新搜索结果(即允许空搜索)
+ * 不在,则不允许空搜索(此处空搜索指的是主搜索框是否为空)
+ *
+ * 该接口也返回一些校验关键词输入规范的正则,
+ * 需要在搜索前进行校验是否可以进行搜索
+ * @returns {*}
+ */
+
+export function getInAntiSpiderWhiteList () {
+  return request({
+    url: '/publicapply/userbase/whitelist',
+    method: 'post'
+  })
+}

+ 19 - 0
apps/bigmember_pc/src/api/modules/user.js

@@ -149,3 +149,22 @@ export function readMark(data) {
     data
   })
 }
+
+// 获取用户最高权限身份
+export function getUserHighestPowerIdentity(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/publicapply/identity/newProduct',
+    method: 'post',
+    data
+  })
+}
+
+export function changeUserIdentity(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/publicapply/identity/switch',
+    method: 'post',
+    data
+  })
+}

BIN
apps/bigmember_pc/src/assets/images/article-mask/vip-expire-dialog-bg.png


BIN
apps/bigmember_pc/src/assets/images/icon/close-icon2x.png


BIN
apps/bigmember_pc/src/assets/images/icon/tip2.png


+ 1 - 0
apps/bigmember_pc/src/assets/style/common.scss

@@ -1,4 +1,5 @@
 @import './mixin';
+@import './pic-icon.scss';
 
 html {
   height: 100%;

+ 7 - 1
apps/bigmember_pc/src/assets/style/pic-icon.scss

@@ -3,7 +3,10 @@
   width: 20px;
   height: 20px;
 }
-
+.wh24 {
+  width: 24px;
+  height: 24px;
+}
 .j-icon-base {
   background-color: transparent;
   background-repeat: no-repeat;
@@ -54,3 +57,6 @@
 .icon-help-img {
   background-image: url(~@/assets/images/icon/help.png);
 }
+.icon-img-close {
+  background-image: url(@/assets/images/icon/close-icon2x.png);
+}

+ 10 - 0
apps/bigmember_pc/src/components/ad/activity-dialog.vue

@@ -148,9 +148,13 @@ export default {
         const needShow = this.checkDialogShow()
         if (this.hasAdPic && needShow) {
           this.showDialog(needShow)
+        } else {
+          // 无广告位或者展示过了,可以初始化下一个广告位
+          this.initNextDialog()
         }
       } catch (e) {
         console.warn('获取广告位信息异常:', e)
+        this.initNextDialog()
       }
     },
     async getAdInfoFromRequest(codes) {
@@ -203,11 +207,16 @@ export default {
     loadSuccess() {
       this.$emit('loaded')
     },
+    // 初始化下一个dialog
+    initNextDialog() {
+      this.$emit('initNext')
+    },
     onDialogClose() {
       // 刷新localStorage的值
       const { week } = this.dateInfo
       const { storageKey } = this
       localStorage.setItem(storageKey, week)
+      this.initNextDialog()
     }
   }
 }
@@ -215,6 +224,7 @@ export default {
 
 <style scoped lang="scss">
 .activity-dialog {
+  z-index: 2045 !important;
   &.support-full {
     .activity-content-container {
       position: fixed;

+ 20 - 8
apps/bigmember_pc/src/components/article-item/ArticleItem.vue

@@ -243,6 +243,16 @@
             >获取更多</em
           >
         </span>
+        <span v-if="!article.winnerTel && article.winnerInfo && article.winnerInfo.length === 1">
+          <i class="l-d-item-label">中标单位联系方式:</i>
+          {{ article.winnerInfo[0].winnerPerson }}
+          <em v-if="article.winnerInfo[0].winnerPerson">,</em>
+          {{ article.winnerInfo[0].winnerTel }}
+           <em
+             class="more-tel"
+             @click="goPortrayal('entDesc', w.winnerId, 'contact')"
+           >获取更多</em>
+        </span>
         <span v-if="article.bidAmount">
           <i class="l-d-item-label">中标金额:</i>
           {{ moneyUnit(article.bidAmount) }}
@@ -363,18 +373,20 @@ export default {
       return vipPower === 1 || memberPower === 1 || entPower === 1
     },
     calcBudget() {
-      if (this.article.budget) {
-        if(isNaN(this.article.budget) && this.article.budget.indexOf('免费注册') > -1) {
-          return this.article.budget
-        } else {
-          return moneyUnit(this.article.budget)
-        }
-      } else if (this.article.bidAmount) {
-        if(isNaN(this.article.bidAmount) && this.article.bidAmount.indexOf('免费注册') > -1) {
+      // 先展示中标金额
+      if (this.article.bidAmount) {
+        if(isNaN(this.article.bidAmount) && this.article.bidAmount.indexOf('登录') > -1) {
           return this.article.bidAmount
         } else {
           return moneyUnit(this.article.bidAmount)
         }
+      } else if (this.article.budget) {
+        // 无中标金额展示预算金额
+        if(isNaN(this.article.budget) && this.article.budget.indexOf('登录') > -1) {
+          return this.article.budget
+        } else {
+          return moneyUnit(this.article.budget)
+        }
       } else {
         return this.article.budget
       }

+ 7 - 1
apps/bigmember_pc/src/components/collect-info/CollectInfo.vue

@@ -565,6 +565,7 @@ export default {
         member_attach:
           '请留下联系方式,我们会尽快联系您!体验附件下载特权,挖掘更多项目情报!',
         member_freeuse: '请留下联系方式,我们会尽快联系您体验大会员全部功能!',
+        jylab_see500_plus: '请留下联系方式,我们会尽快联系您!开通大会员可查看更多招标项目,实时监控更多潜在商机!'
       },
       sourceDescMap: {
         pc_buyer_monitor_more: '采购单位画像页-超级订阅用户申请监控更多业主',
@@ -775,6 +776,11 @@ export default {
           break
         }
       }
+
+      // 免费用户市场分析报告,不展示agree模块
+      if (source.includes('pc_analysis_')) {
+        this.moduleShow.agree = false
+      }
     },
     // 特殊字段参数处理
     addMoreParams (source, params, type = true) {
@@ -876,7 +882,7 @@ export default {
           ]
           var isCollect = sourceList.includes(source)
           _this.source = source
-          _this.isForce = res.data.fource
+          _this.isForce = res.data?.fource
           if (result) {
             if (isCollect) {
               callback && callback()

+ 121 - 0
apps/bigmember_pc/src/components/dialog/CheckPowerAndSwitch.vue

@@ -0,0 +1,121 @@
+<template>
+  <CustomDialog
+    show-close
+    width="336px"
+    top="20vh"
+    class="check-power-and-switch-dialog"
+    title="身份切换提醒"
+    :visible.sync="visible"
+    @close="onClose"
+  >
+    <div class="content-text" v-html="messageText"></div>
+    <template #footer>
+      <button
+        class="action-button confirm"
+        @click="onClickConfirm"
+        v-loading="loading"
+      >
+        立即切换
+      </button>
+      <button class="action-button cancel" @click="onClickCancel">
+        暂不切换
+      </button>
+    </template>
+  </CustomDialog>
+</template>
+<script>
+import Dialog from './Dialog.vue'
+import { getUserHighestPowerIdentity, changeUserIdentity } from '@/api/modules/'
+
+export default {
+  name: 'CheckPowerAndSwitch',
+  components: {
+    [Dialog.name]: Dialog
+  },
+  data() {
+    return {
+      visible: false,
+      loading: false,
+      targetIdentity: {
+        currentIdentity: '',
+        entId: '',
+        entName: '',
+        productType: '',
+        token: ''
+      }
+    }
+  },
+  computed: {
+    messageText() {
+      const { entName, productType, currentIdentity } = this.targetIdentity
+      const arr = [
+        '当前访问身份为',
+        `<span class="highlight-text">“${currentIdentity || ''}”</span>`,
+        ',系统识别您在',
+        `<span class="highlight-text">“${entName || ''}”</span>身份下有`,
+        `<span class="highlight-text">“${productType || ''}”</span>权限`,
+        ',建议您切换到该身份下使用剑鱼标讯。'
+      ]
+      return arr.join('')
+    }
+  },
+  created() {
+    this.getUserHighestIdentity()
+  },
+  methods: {
+    onClose() {
+      this.getUserHighestIdentity(true)
+    },
+    onClickCancel() {
+      this.getUserHighestIdentity(true)
+      this.showDialog(false)
+    },
+    async onClickConfirm() {
+      this.switchNow()
+    },
+    showDialog(f = false) {
+      this.visible = f
+    },
+    async getUserHighestIdentity(clear) {
+      let payload = {
+        isClear: clear ? true : undefined
+      }
+      const { error_code: code, data } = await getUserHighestPowerIdentity(payload)
+      if (code === 0 && data) {
+        this.targetIdentity = data
+        if (data.token) {
+          this.showDialog(true)
+        }
+      }
+    },
+    async switchNow() {
+      const p = this.targetIdentity
+      if (!p.token) return
+      try {
+        this.loading = true
+        const { error_code: code, data } = await changeUserIdentity({
+          token: p.token
+        })
+        if (code === 0 && data === 1) {
+          location.reload()
+        } else {
+          this.$toast('身份切换失败')
+        }
+        this.loading = false
+      } catch (error) {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>
+<style scoped lang="scss">
+.content-text {
+  text-align: center;
+}
+.action-button {
+  &.cancel {
+    color: #1d1d1d;
+  }
+}
+</style>

+ 41 - 0
apps/bigmember_pc/src/components/dialog/CheckUserDialog.vue

@@ -0,0 +1,41 @@
+<template>
+  <div class="check-user-dialog">
+    <CheckVipExpire @loaded="onVipCheckLoaded" @change="onVipCheckChange" />
+    <CheckPowerAndSwitch v-if="checkPowerShow" />
+  </div>
+</template>
+<script>
+import CheckPowerAndSwitch from '@/components/dialog/CheckPowerAndSwitch'
+import CheckVipExpire from '@/components/dialog/CheckVipExpire'
+
+export default {
+  name: 'CheckUserDialog',
+  components: {
+    CheckVipExpire,
+    CheckPowerAndSwitch
+  },
+  data() {
+    return {
+      vipCheck: {
+        loaded: false,
+        show: false
+      }
+    }
+  },
+  computed: {
+    checkPowerShow() {
+      const { loaded, show } = this.vipCheck
+      return loaded && !show
+    }
+  },
+  methods: {
+    onVipCheckLoaded({ show }) {
+      this.vipCheck.loaded = true
+      this.vipCheck.show = show
+    },
+    onVipCheckChange({ show }) {
+      this.vipCheck.show = show
+    }
+  }
+}
+</script>

+ 287 - 0
apps/bigmember_pc/src/components/dialog/CheckVipExpire.vue

@@ -0,0 +1,287 @@
+<template>
+  <CustomDialog
+    width="336px"
+    top="20vh"
+    class="check-vip-expire-dialog"
+    :visible.sync="show"
+    @close="closeDialog"
+  >
+    <div class="check-vip-expire-container">
+      <div class="check-vip-expire-header" v-html="titleText"></div>
+      <div class="check-vip-expire-footer">
+        <div
+          class="text-container"
+          :class="{ 'expire-text-center': !activityText }"
+        >
+          <p>为避免遗漏重大项目,请及时续费</p>
+          <p class="activity-tip" v-if="activityText">{{ activityText }}</p>
+        </div>
+        <button class="confirm-button clickable" @click="rechargeNow">
+          立即续费
+        </button>
+      </div>
+    </div>
+    <div
+      class="j-icon j-icon-base icon-img-close pointer"
+      @click="closeDialog"
+    ></div>
+    <template #footer>
+      <div></div>
+    </template>
+  </CustomDialog>
+</template>
+<script>
+import Dialog from './Dialog.vue'
+import { vipRenewReminder } from '@/api/modules/'
+
+export default {
+  name: 'CheckVipExpire',
+  components: {
+    [Dialog.name]: Dialog
+  },
+  data() {
+    return {
+      show: false,
+      pInfo: {
+        activityInfo: {},
+        endDays: 0
+      }
+    }
+  },
+  computed: {
+    titleText() {
+      const { endDays } = this.pInfo
+      const t = endDays
+      let arr = []
+      if (t <= 1) {
+        arr = [
+          '您的超级订阅将于',
+          `<strong class="time"> 今天 </strong>`,
+          '到期'
+        ]
+      } else {
+        arr = [
+          '您的超级订阅还有',
+          `<strong class="time"> ${t}天 </strong>`,
+          '到期'
+        ]
+      }
+      return arr.join('')
+    },
+    activityText() {
+      const { activityInfo } = this.pInfo
+      if (!activityInfo) return ''
+      const { activity } = activityInfo
+      if (!activity) return ''
+      let arr = []
+      if (Array.isArray(activity) && activity.length >= 1) {
+        const activity1 = activity[0]
+        const discount = activity1.discount
+        if (Array.isArray(discount) && discount.length >= 1) {
+          arr.push('限时活动:')
+          const discount1 = discount[0]
+          // type 0满减、1折扣券、2满赠、3促销、4限时折扣、5限时减免
+          // type  035满减  14满折  2满赠
+          const lotteryType = discount1.type
+          if (lotteryType === 0 || lotteryType === 3 || lotteryType === 5) {
+            // 满减:续费最高立减1999元(变量,小数不展示,不四舍五入)
+            const reduce = discount1.reduce
+            const text = `续费最高立减${reduce}元`
+            arr.push(text)
+          } else if (lotteryType === 1 || lotteryType === 4) {
+            // 满折:续费1年(变量)立享6折(变量)优惠
+            const zhe = Math.round(discount1.discount * 10 * 10) / 10
+            const text = `续费${activityInfo.info}立享${zhe}折优惠`
+            arr.push(text)
+          } else if (lotteryType === 2) {
+            // 满赠:现在续费1年(变量)再送1年(注“12个月”按照1年展示)
+            // timeType 时间类型:1/天、2/月,3/年
+            const zengTime = discount1.time
+            const zengTimeType = discount1.timeType
+            const timeTypeMap = {
+              1: '天',
+              2: '个月',
+              3: '年'
+            }
+            const suffixText = timeTypeMap[zengTimeType]
+            let zeng = `${zengTime}${suffixText}`
+            if (zeng === '12个月') {
+              zeng = '1年'
+            }
+            const text = `现在续费${activityInfo.info}再送${zeng}`
+            arr.push(text)
+          } else {
+            arr = []
+          }
+        }
+      }
+      return arr.join('')
+    }
+  },
+  created() {
+    this.getVipRenewReminder()
+  },
+  methods: {
+    initNextDialog() {
+      this.$emit('initNext')
+    },
+    showDialog() {
+      this.show = true
+      this.onChange()
+    },
+    async closeDialog(type = false) {
+      this.show = false
+      this.$nextTick(() => {
+        this.onChange()
+      })
+      if (!type) {
+        // 弹窗关闭,可以初始化下个弹窗
+        this.initNextDialog()
+      }
+      await this.getVipRenewReminder(true)
+    },
+    onChange() {
+      this.$emit('change', { show: this.show })
+    },
+    loaded() {
+      this.$emit('loaded', { show: this.show })
+    },
+    async getVipRenewReminder(clear) {
+      let payload = {
+        clearRedis: clear ? true : undefined
+      }
+      try {
+        const { data } = await vipRenewReminder(payload)
+        if (data && data.endDays !== undefined) {
+          this.pInfo = data
+          if (data.endDays >= 0) {
+            this.showDialog()
+          } else {
+            // 未满足展示条件,可以初始化下个弹窗
+            if (!clear) {
+              this.initNextDialog()
+            }
+          }
+        } else {
+          // 未满足展示条件,可以初始化下个弹窗
+          if (!clear) {
+            this.initNextDialog()
+          }
+        }
+      } catch (error) {
+        console.log(error)
+        // 未满足展示条件,可以初始化下个弹窗
+        if (!clear) {
+          this.initNextDialog()
+        }
+      } finally {
+        if (!clear) {
+          this.loaded()
+        }
+      }
+    },
+    async rechargeNow() {
+      this.closeDialog(true)
+      setTimeout(() => {
+        window.top.location.href =
+          '/swordfish/page_big_pc/free/svip/buy?type=renew'
+      }, 50)
+    }
+  }
+}
+</script>
+<style scoped lang="scss">
+.check-vip-expire-dialog {
+  ::v-deep {
+    .el-dialog {
+      width: 368px;
+      height: 460px;
+      background: transparent
+        url(@/assets/images/article-mask/vip-expire-dialog-bg.png) no-repeat
+        center;
+      background-size: contain;
+      box-shadow: none;
+    }
+    .el-dialog__header,
+    .el-dialog__body,
+    .el-dialog__footer {
+      padding: 0;
+    }
+    .el-dialog__body {
+      height: 100%;
+    }
+  }
+}
+
+.icon-img-close {
+  position: absolute;
+  bottom: -20px;
+  left: 50%;
+  transform: translateX(-50%);
+}
+
+.check-vip-expire-container {
+  position: relative;
+  height: 340px;
+  width: 100%;
+  left: 0;
+  top: 100px;
+}
+
+.check-vip-expire-header,
+.check-vip-expire-footer {
+  position: absolute;
+  left: 0;
+  right: 0;
+  font-size: 16px;
+  line-height: 24px;
+  color: #5f391a;
+  text-align: center;
+  padding: 0 20px;
+}
+
+.check-vip-expire-header {
+  top: 20px;
+  ::v-deep {
+    .time {
+      font-size: 18px;
+      font-weight: bold;
+    }
+  }
+}
+.check-vip-expire-footer {
+  bottom: 18px;
+}
+.text-container {
+  margin-bottom: 8px;
+  &.expire-text-center {
+    margin-bottom: 20px;
+  }
+}
+
+.activity-tip {
+  margin-top: 4px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 2px 0;
+  font-size: 14px;
+  line-height: 20px;
+  color: #fff;
+  border-radius: 4px;
+  background: linear-gradient(90deg, #ff9347 0%, #ff4236 100%);
+}
+
+.confirm-button {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  padding: 8px 0;
+  font-size: 16px;
+  line-height: 24px;
+  color: #fae7ca;
+  border-radius: 8px;
+  background: linear-gradient(117.49deg, #3e3835 8.4%, #242120 100%);
+}
+</style>

+ 10 - 0
apps/bigmember_pc/src/components/dialog/Dialog.vue

@@ -5,6 +5,7 @@
     v-bind="$props"
     :show-close="showClose"
     :visible="visible"
+    :before-close="beforeClose"
     @update:visible="update"
     v-component-change-mount="{ selector: comMount }"
     @open="$emit('open')"
@@ -42,6 +43,7 @@ export default {
   props: {
     visible: Boolean,
     showClose: Boolean,
+    beforeClose: Function,
     comMount: {
       type: String,
       default: ''
@@ -99,6 +101,14 @@ export default {
     font-size: 14px;
     line-height: 22px;
   }
+  .el-dialog__body,
+  .el-dialog__footer {
+    padding-left: 32px;
+    padding-right: 32px;
+  }
+  .el-dialog__footer {
+    padding-top: 6px;
+  }
   .dialog-footer {
     display: flex;
     align-items: center;

+ 12 - 8
apps/bigmember_pc/src/components/filter-items/AmountRangeSelector.vue

@@ -19,7 +19,7 @@
       >
         <el-popover
           v-if="item.disabled"
-          class="custom-popover"
+          class="custom-popover amount-range-popover"
           :append-to-body="false"
           placement="right-end"
           :trigger="popoverTrigger"
@@ -118,10 +118,10 @@ export default {
             return ''
           }
         } else {
-          return '全部金额'
+          return ''
         }
       } else {
-        return price === '' ? '全部金额' : ''
+        return ''
       }
     }
   },
@@ -143,7 +143,7 @@ export default {
             setTimeout(() => {
               // popover在下拉框展示时需要重新计算位置,通过先将popover弹框透明度将为0等位置计算完成后再恢复
               this.$refs.customPricePopover[0].updatePopper()
-              const $popover = this.$root.$el.querySelector('.custom-popover > .el-popover')
+              const $popover = this.$root.$el.querySelector('.amount-range-popover > .el-popover')
               $popover.style.opacity = '1'
             }, 300)
           }
@@ -199,6 +199,7 @@ export default {
         const valueArr = this.options.filter(v => !v.disabled).map(t => t.value)
         if (valueArr.includes(data)) {
           this.activeValue = data
+          this.showPopover = false
         } else {
           const priceArr = data.split('-')
           const min = priceArr[0]
@@ -208,12 +209,15 @@ export default {
           this.price.max = max
           this.activeValue = data
           this.showPopover = true
-          this.$nextTick(() => {
-            const $popover = this.$root.$el.querySelector('.custom-popover > .el-popover')
-            $popover.style.opacity = '0'
-          })
+          // this.$nextTick(() => {
+          //   const $popover = this.$root.$el.querySelector('.amount-range-popover > .el-popover')
+          //   $popover.style.opacity = '0'
+          // })
         }
       } else {
+        this.price.min = ''
+        this.price.max = ''
+        this.showPopover = false
         this.activeValue = data
       }
     }

+ 1 - 1
apps/bigmember_pc/src/components/filter-items/AttachmentSelector.vue

@@ -63,7 +63,7 @@ export default {
   },
   computed: {
     computedVal () {
-      if (this.selected.value) {
+      if (this.selected.value && this.selected.value !== '0') {
         return this.selected.label
       } else {
         return ''

+ 1 - 4
apps/bigmember_pc/src/components/filter-items/BasePowerLayout.vue

@@ -55,14 +55,11 @@ $gold: #c98f37;
   position: relative;
   display: flex;
   align-items: center;
-  padding: 0 12px 0 6px;
   border-radius: 4px;
 }
-.default-module {
-  margin-right: 8px;
-}
 .vip-module {
   border: 1px dashed $gold;
+  padding: 1px 12px 1px 8px;
   background: linear-gradient(90deg, #fff7dC 0%, rgba(255, 247, 220, 0) 100%);
   &::after {
     content: '';

+ 0 - 1
apps/bigmember_pc/src/components/filter-items/CascadeContent.vue

@@ -88,7 +88,6 @@ export default {
   },
   watch: {
     value (val) {
-      console.log(val)
       this.setState(val)
     }
   },

+ 2 - 2
apps/bigmember_pc/src/components/filter-items/ChangeHandsDropdown.vue

@@ -60,11 +60,11 @@ export default {
       options: [
         {
           label: "不限",
-          value: "1"
+          value: 0
         },
         {
           label: "到期换手率高",
-          value: "2"
+          value: 1
         }
       ],
       selected: {

+ 2 - 2
apps/bigmember_pc/src/components/filter-items/CheckboxGroupSelector.vue

@@ -6,9 +6,9 @@
         :key="index"
         @click="onClick(item)"
       >
-        <span class="j-checkbox checkbox-item-icon" :class="{checked: value.includes(item.value)}"></span>
+        <span class="j-checkbox checkbox-item-icon" :class="{checked: value.includes(item.value), gold: item.power}"></span>
         <span class="checkbox-item-label">{{ item.label }}</span>
-        <slot name="tips" :prop="item.value"></slot>
+        <slot name="tips" :prop="item"></slot>
       </div>
       <slot name="endOther"></slot>
     </div>

+ 3 - 4
apps/bigmember_pc/src/components/filter-items/CommonCheckboxSelector.vue

@@ -1,4 +1,3 @@
-import { onMounted, reactive } from 'vue';
 <template>
   <div class="common-checkbox-selector">
     <el-checkbox-group
@@ -73,7 +72,7 @@ function onIssueStateChange(value) {
         value = ['全部']
       }
     }
-  } 
+  }
   list.value = value
   if (value.length === 1 && value[0] === '全部') {
     emit('input', [])
@@ -101,13 +100,13 @@ function onIssueStateChange(value) {
         }
       }
     }
-    
+
     &.is-active {
       .el-checkbox-button__inner {
         border-color: #2abed1;
       }
     }
-    
+
   }
   .j-checkbox-button:first-child,
   .j-checkbox-button:last-child {

+ 11 - 10
apps/bigmember_pc/src/components/filter-items/KeywordTagsSelector.vue

@@ -1,19 +1,20 @@
 <template>
   <Layout
-    :value='computedVal'
-    :type='type'
-    :trigger="trigger"
+    :value="computedVal"
+    :type="type"
+    trigger="hover"
     :placeholder="placeholder"
+    placement="bottom"
   >
     <KeywordTagsSelectorContent
       slot="empty"
-      :list='value'
-      :placeholder='inputPlaceholder'
-      :max-tip='maxTip'
-      :disabled='disabled'
-      :max-list-length='maxListLength'
-      :max-length='maxLength'
-      @change='keywordTagsChange'>
+      :list="value"
+      :placeholder="inputPlaceholder"
+      :max-tip="maxTip"
+      :disabled="disabled"
+      :max-list-length="maxListLength"
+      :max-length="maxLength"
+      @change="keywordTagsChange">
     </KeywordTagsSelectorContent>
   </Layout>
 </template>

+ 8 - 1
apps/bigmember_pc/src/components/filter-items/KeywordTagsSelectorContent.vue

@@ -9,6 +9,8 @@
         :maxlength="maxLength"
         :disabled="disabled || list.length >= maxListLength"
         @keyup.native="onKeyup"
+        @focus.native="onKeydown"
+        @keydown.native="onKeydown"
       ></el-input>
       <span class="add-keyword-btn" :class="{'focus': addKeywordVal}" @click="addKeyTags">添加</span>
     </div>
@@ -83,6 +85,12 @@ export default {
     removeTag (tag) {
       this.list.splice(this.list.indexOf(tag), 1)
       this.$emit('change', this.list)
+    },
+    // dropdown的坑点,微软输入法,input输入的时候会失去焦,dropdown会收起,暂且如此处理
+    onKeydown () {
+      if(this.$parent.$parent && this.$parent.$parent.show) {
+        this.$parent.$parent.show()
+      }
     }
   }
 }
@@ -175,5 +183,4 @@ export default {
     cursor: pointer;
   }
 }
-
 </style>

+ 76 - 18
apps/bigmember_pc/src/components/filter-items/OnecascadeContent.vue

@@ -2,7 +2,7 @@
   <Layout :type="type" :placeholder="placeholder" :trigger="trigger" :value="computedVal">
     <div class="cascade-content" slot="empty">
       <div class="cascade-content-module">
-        <header class="module-header">{{ placeholder }}</header>
+        <header class="module-header" v-if="showHeader">{{ placeholder }}</header>
         <div class="module-main">
           <ul>
             <li class="module-item" :class="{ 'active': fActive === fIndex }" v-for="(first, fIndex) in firstList"
@@ -46,8 +46,14 @@ export default {
       default: () => []
     },
     value: {
-      type: [Object, Array],
-      default: () => []
+      type: [Array,Object],
+      default: () => {
+        return null
+      }
+    },
+    showHeader: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -64,13 +70,37 @@ export default {
     event: 'change'
   },
   computed: {
+    // 源数据是否带有【全部】选项
+    hadAll () {
+      return this.options.find(item => item.label === '全部') || false
+    },
+    // 源数据带有选项【全部】选项的值
+    hadAllValue () {
+      let result = ''
+      if(this.hadAll) {
+        this.options.forEach(item => {
+          if( item.label === '全部') {
+            result = item.value
+          }
+        })
+      }
+      return result
+    },
     computedVal() {
-      return this.value.length ? `${this.placeholder}${this.value.length}个` : ''
+      let result = ''
+      if(Array.isArray(this.value) && this.value.length) {
+        if(this.hadAllValue && this.value.includes(this.hadAllValue)) {
+          result = ''
+        } else {
+          result = `${this.placeholder}${this.value.length}个`
+        }
+      }
+      return result
     }
   },
   watch: {
     value(val) {
-      // this.setState(val)
+      this.setState(val)
     }
   },
   mounted() {
@@ -80,39 +110,57 @@ export default {
     getArray() {
       const options = this.options
       const toArray = options.map(item => {
-        return {
+        const resultObj = {
           value: item.value,
           label: item.label,
           checked: false,
           indeterminate: false,
           disabled: false
         }
+        if(this.hadAll && item.label === '全部') {
+          resultObj.all = true
+        }
+        return  resultObj
       })
       return toArray
     },
     initData() {
       const sourceList = this.getArray()
-      sourceList.unshift({
-        label: '全部',
-        value: '全部',
-        checked: false,
-        disabled: false,
-        indeterminate: false,
-        all: true
-      })
+      // 数据源无全部的时候,手动在数据头部插入一组全部数据
+      if(!this.hadAll) {
+        sourceList.unshift({
+          label: '全部',
+          value: '全部',
+          checked: false,
+          disabled: false,
+          indeterminate: false,
+          all: true
+        })
+      }
       this.firstList = sourceList
-      this.setState(this.value)
+      if(!this.value || this.value?.length === 0) {
+        this.setState(null)
+      } else {
+        this.setState(this.value)
+      }
     },
     onFirstChange(checked, first, fIndex) {
       if (first.all) {
         this.firstList.forEach(item => {
           item.checked = checked
+          item.indeterminate = false
         })
       } else {
         first.checked = checked
       }
       this.checkFirstAllStatus()
-      this.$emit('change', this.getState())
+
+      // 会返回null和array两种数据类型!!!!!!
+      if (first.all) {
+        this.$emit('change', first.checked ? this.getState() : null)
+      } else {
+        this.$emit('change', this.getState())
+      }
     },
     restState() {
       this.firstList.forEach(item => {
@@ -140,7 +188,9 @@ export default {
           arr.push(item.value)
         }
       })
-      if (arr.includes('全部')) {
+      if(this.hadAll && arr.includes(this.hadAllValue)){
+        return [this.hadAllValue]
+      } else if (arr.includes('全部')) {
         return []
       } else {
         return arr
@@ -148,11 +198,18 @@ export default {
     },
     setState(value) {
       this.restState()
-      if (!value || value.length === 0) {
+      if (!value) {
         this.firstList.forEach(item => {
           item.checked = false
           item.indeterminate = false
         })
+        return
+      }
+      if(Array.isArray(value) && value.length === 0) {
+        this.firstList.forEach(item => {
+          item.checked = true
+          item.indeterminate = false
+        })
       } else {
         this.firstList.forEach(item => {
           if (value.includes(item.value)) {
@@ -162,6 +219,7 @@ export default {
         })
       }
       this.checkFirstAllStatus()
+      this.firstLoad = false
     }
   }
 }

+ 2 - 1
apps/bigmember_pc/src/components/filter-items/RegionCollapseSelector.vue

@@ -68,7 +68,7 @@
         </div>
       </div>
       <div slot="expand" class="is-expand" @click="toggleMoreStatus">
-        <span>{{ moreStatus ? '收起' : '展开' }}</span>
+        <span>{{ moreStatus ? '收起' : '更多' }}</span>
         <i class="el-icon-arrow-down" :class="{'is-reverse': moreStatus}"></i>
       </div>
     </div>
@@ -744,6 +744,7 @@ export default {
     border-radius: 4px;
     border: 1px solid #e0e0e0;
     .city-item {
+      font-size: 14px;
       display: inline-block;
       margin: 0 4px 4px;
       padding: 4px 8px;

+ 16 - 1
apps/bigmember_pc/src/components/filter-items/SearchRangeDropdown.vue

@@ -8,7 +8,8 @@
     <div slot="empty" class="search-range-container">
       <el-checkbox-group
         v-model="selectVal"
-        @change="checkboxChange">
+        @change="checkboxChange"
+        :min="min">
         <el-checkbox
           v-for="oItem in options"
           :label="oItem.key"
@@ -57,6 +58,10 @@ export default {
     value: {
       type: Array,
       default: () => []
+    },
+    min: {
+      type: Number,
+      default: 1
     }
   },
   model: {
@@ -134,6 +139,16 @@ export default {
     display: flex;
     align-items: center;
   }
+  .el-checkbox__input.is-disabled+span.el-checkbox__label{
+    color: #2CB7CA;
+  }
+  .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner {
+    background-color: #2cb7ca;
+    border-color: #2cb7ca;
+    &::after{
+      border-color: #fff;
+    }
+  }
 }
 .icon-help-img {
   width: 18px;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 22 - 14
apps/bigmember_pc/src/components/filter-items/SearchScopeSelector.vue


+ 32 - 5
apps/bigmember_pc/src/components/forecast/ForeCast.vue

@@ -34,8 +34,9 @@
         <div class="filter-label" slot="header">合同到期时间:</div>
       </TimeSelector>
       <!-- 地区 -->
-      <div class="filter-region" v-show="tabActive === 'fwl'">
+      <div class="filter-line" v-show="tabActive === 'fwl'">
         <AreaCityCountryCascader
+          class="filter-line-item"
           ref="areaCityCountry"
           :showTags="false"
           :isHaveAll="isShowAllCountry"
@@ -46,6 +47,12 @@
         >
           <div class="filter-label" slot="header">项目地区:</div>
         </AreaCityCountryCascader>
+        <div class="filter-line-item">
+          <div class="filter-label">价格区间:</div>
+          <div class="filter-content">
+            <MoneySelection v-model="filterPrice" @change="onPriceChange" />
+          </div>
+        </div>
       </div>
     </div>
     <!-- 中标企业预测-->
@@ -531,6 +538,7 @@ import {
   changeEntGroup,
   forecastArea
 } from '@/api/modules'
+import MoneySelection from '@/components/selector-cascader/MoneySelection.vue'
 import AreaCityCountryCascader from '@/components/selector-cascader/AreaCityCountryCascader.vue'
 export default {
   props: [
@@ -558,6 +566,7 @@ export default {
     [Dialog.name]: Dialog,
     [Button.name]: Button,
     [Checkbox.name]: Checkbox,
+    MoneySelection,
     Empty,
     GroupCard,
     Tips,
@@ -589,6 +598,10 @@ export default {
         group: '', // 当前点击编辑的分组信息
         fid: ''
       },
+      filterPrice: {
+        min: '',
+        max: ''
+      },
       tabActive: 'fwl',
       regionMapData: '',
       sourceAreaMap: {},
@@ -1170,6 +1183,11 @@ export default {
       this.$emit('changeArea', regionMap)
       this.$emit('changeId', this.selectedId)
     },
+    onPriceChange() {
+      this.selectedId = []
+      this.$emit('changePrice', this.filterPrice)
+      this.$emit('changeId', this.selectedId)
+    },
     // 重置筛选(目前需求只有潜在项目预测-服务类项目有)
     goResetFilter() {
       this.regionMapData = ''
@@ -1178,6 +1196,8 @@ export default {
       this.$refs.timeSelector.setState({
         exact: 'all'
       })
+      this.filterPrice = { min: '', max: '' }
+      this.$emit('changePrice', this.filterPrice)
       this.$emit('onChange', { start: 0, end: 0 })
     }
   }
@@ -1211,6 +1231,7 @@ export default {
   }
 
   .filtrate_box {
+    padding: 0 40px;
     border-bottom: 1px dashed #e0e0e0;
 
     // min-height: 123px;
@@ -1228,16 +1249,22 @@ export default {
       color: #686868;
     }
 
-    .filter-region {
+    .filter-line {
+      display: flex;
+      align-items: center;
       border-top: 1px dashed #e0e0e0;
-      background: #f2f2f4;
+      background: #fff;
       .area-city-country {
         border-radius: 0 0 8px 8px;
       }
     }
+    .filter-line-item {
+      display: flex;
+      align-items: center;
+    }
 
     .tab_box {
-      padding: 0 0 4px 40px;
+      padding-bottom: 4px;
     }
 
     ::v-deep {
@@ -1246,7 +1273,7 @@ export default {
         background-color: #e0e0e0;
       }
       .selector-card.s-line {
-        padding: 16px 40px;
+        padding: 16px 0;
       }
     }
   }

+ 1 - 1
apps/bigmember_pc/src/components/search-input/SearchInput.vue

@@ -79,7 +79,7 @@ export default {
       this.$emit('input', e)
     },
     onSearch() {
-      this.$emit('onSearch')
+      this.$emit('onSearch', this.value)
     }
   }
 }

+ 286 - 0
apps/bigmember_pc/src/components/selector-cascader/MoneySelection.vue

@@ -0,0 +1,286 @@
+<template>
+  <div class="selector-money select-input-container select-common-inputs">
+    <el-select
+      ref="selectprice"
+      :placeholder="placeholder"
+      :value="inputValue"
+      :popper-append-to-body="false"
+      class="select_common"
+      popper-class="select_common_data"
+    >
+      <div slot="empty" class="select_box_container">
+        <div class="price-content">
+          <el-checkbox :value="checkedAll" @click.native="clickAll">全部</el-checkbox>
+          <div class="price-exact-container">
+            <div class="price-input-group" :class="{ active: !checkedAll }">
+              <div class="price-input">
+                <div class="price-input-group-item">
+                  <input
+                    type="text"
+                    class="min-input"
+                    name="minprice"
+                    maxlength="9"
+                    oninput="value=value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1')"
+                    v-model.number="inputGroupCache.min"
+                  />
+                  <span class="unit">万元</span>
+                </div>
+                <span class="splitter"></span>
+                <div class="price-input-group-item">
+                  <input
+                    type="text"
+                    class="max-input"
+                    name="maxprice"
+                    maxlength="9"
+                    oninput="value=value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1')"
+                    v-model.number="inputGroupCache.max"
+                  />
+                  <span class="unit">万元</span>
+                </div>
+              </div>
+            </div>
+            <button class="select-btn" @click="onConfirm">确定</button>
+          </div>
+        </div>
+      </div>
+    </el-select>
+  </div>
+</template>
+
+<script>
+import { Select, Checkbox, CheckboxGroup } from 'element-ui'
+import { selectorVModelMixin } from '@/utils/mixins/selector-v-model'
+
+export default {
+  name: 'MoneySelection',
+  mixins: [selectorVModelMixin],
+  components: {
+    [CheckboxGroup.name]: CheckboxGroup,
+    [Checkbox.name]: Checkbox,
+    [Select.name]: Select
+  },
+  props: {
+    placeholder: {
+      type: String,
+      default: '价格区间'
+    },
+    allRequired: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      checkedAll: true,
+      inputGroupCache: {
+        min: '',
+        max: ''
+      },
+      price: {
+        min: '',
+        max: ''
+      }
+    }
+  },
+  computed: {
+    inputValue() {
+      const { min, max } = this.price
+      if (min !== '' && max !== '') {
+        return `${min || 0}万~${max || ''}万`
+      } else {
+        return `${this.placeholder}`
+      }
+    }
+  },
+  methods: {
+    resetStatePrice() {
+      this.checkedAll = true
+      this.inputGroupCache.min = ''
+      this.inputGroupCache.max = ''
+      this.price.min = ''
+      this.price.max = ''
+    },
+    clickAll() {
+      this.resetStatePrice()
+      // this.onChange()
+    },
+    compareMinMax() {
+      const { min, max } = this.inputGroupCache
+      const hasMinAndMax = String(min).length && String(max).length
+      if (hasMinAndMax && min > max) {
+        this.inputGroupCache.max = min
+        this.inputGroupCache.min = max
+      }
+    },
+    setState({ min = '', max = '' }) {
+      this.price.min = min
+      this.price.max = max
+      if (min !== '') {
+        this.checkedAll = false
+        this.inputGroupCache.min = min
+      }
+      if (max !== '') {
+        this.checkedAll = false
+        this.inputGroupCache.max = max
+      }
+      if (min !== '' && max !== '') {
+        this.compareMinMax()
+        this.syncToPrice()
+      }
+
+      if (min === '' && max === '') {
+        this.resetStatePrice()
+      }
+    },
+    getState() {
+      if (this.checkedAll) {
+        return { min: '', max: '' }
+      } else {
+        return this.inputGroupCache
+      }
+    },
+    syncToPrice() {
+      Object.assign(this.price, this.inputGroupCache)
+    },
+    onConfirm() {
+      this.compareMinMax()
+      const { min, max } = this.inputGroupCache
+      if (min || max) {
+        this.checkedAll = false
+      }
+      this.$nextTick(() => {
+        this.$refs.selectprice.blur()
+        this.syncToPrice()
+        this.onChange()
+      })
+    },
+    onChange() {
+      const state = this.getState()
+      const { min, max } = state
+      if (this.allRequired) {
+        if (min === '' || max === '') {
+          return this.$toast('最小值和最大值均为必填')
+        }
+      }
+      this.$emit('change', state)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+$hover-border-color: #e0e0e0;
+
+.select-common-inputs {
+  position: relative;
+  ::v-deep {
+    .el-input__inner {
+      height: 30px;
+      width: 200px;
+      background-color: transparent;
+
+      // ellipsis
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      text-align: justify;
+    }
+    .el-select__caret {
+      color: #aaa;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    .el-input__icon {
+      line-height: normal;
+    }
+  }
+}
+
+::v-deep {
+  .el-select-dropdown.el-popper {
+    &.select_common_data {
+      left: 0 !important;
+      margin: 0;
+      margin-top: 5px;
+      border: 1px solid #2cb7ca;
+      min-width: 425px;
+      // height: 104px;
+      // box-sizing: border-box;
+      z-index: 99 !important;
+    }
+
+    .popper__arrow {
+      display: none;
+    }
+  }
+}
+
+@media only screen and (max-width: 1230px),
+  only screen and (max-device-width: 1230px) {
+  ::v-deep {
+    .el-select-dropdown.el-popper.select_common_data {
+      left: -225px !important;
+    }
+  }
+}
+
+.select_box_container {
+  min-width: 425px;
+  padding: 18px 12px;
+  box-sizing: border-box;
+}
+
+.price-exact-container {
+  margin-top: 4px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between
+}
+.price-input-group {
+  border: 1px solid #f5f6f7;
+  background-color: #f5f6f7;
+  padding: 5px 5px;
+  border-radius: 2px;
+  &.active {
+    border-color: #2cb7ca;
+    background-color: #2cb7ca;
+  }
+}
+.price-input-group-item {
+  display: flex;
+  align-items: center;
+  padding: 0 10px;
+  background-color: #fff;
+  border: 1px solid #2cb7ca;
+  border-radius: 4px;
+  input {
+    width: 106px;
+    height: 28px;
+    padding-left: 0;
+    border: none;
+  }
+}
+
+.splitter {
+  margin: 0 6px;
+  width: 14px;
+  height: 1px;
+  line-height: 15px;
+  background-color: $hover-border-color;
+}
+.unit {
+  color: #1d1d1d;
+  font-size: 14px;
+}
+
+.price-input {
+  display: flex;
+  align-items: center;
+}
+
+.select-btn {
+  color: #2cb7ca;
+  background-color: transparent;
+}
+</style>

+ 13 - 3
apps/bigmember_pc/src/components/selector/InfoTypeSelector.vue

@@ -19,6 +19,7 @@
       :beforeChange="beforeChange"
       :oneLevelSelected="oneLevelSelected"
       :showDataType="showDataType"
+      :options="options"
       @onChange="onChange"
     />
   </selector-card>
@@ -62,6 +63,12 @@ export default {
     showDataType: {
       type: String,
       default: 'all'
+    },
+    options: {
+      type: Array,
+      default() {
+        return []
+      }
     }
   },
   model: {
@@ -69,9 +76,12 @@ export default {
     event: 'change'
   },
   watch: {
+    showDataType (newVal, oldVal) {
+      this.initList([], newVal)
+    },
     value (val) {
       if(!val || val?.length === 0) {
-        this.initList()
+        this.initList([], this.showDataType)
       } else {
         this.setInfoTypeState(val)
       }
@@ -82,8 +92,8 @@ export default {
   },
   created() {},
   methods: {
-    initList(data = []) {
-      return this.$refs.content.initInfoTypeFn(data)
+    initList(data = [], type) {
+      return this.$refs.content.initInfoTypeFn(data, type)
     },
     setInfoTypeState(data) {
       return this.$refs.content.setInfoTypeState(data)

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


+ 16 - 3
apps/bigmember_pc/src/components/selector/SearchTimeScopeSelector.vue

@@ -7,6 +7,8 @@
       :selectorTime="type"
       selectorType="line"
       :defaultSelectedKey="value"
+      :exactCanHalf="exactCanHalf"
+      :showConfirmButton="showConfirmButton"
       @onChange="onChange"
     />
   </div>
@@ -43,6 +45,16 @@ export default {
     type: {
       type: String,
       default: 'bidSearch'
+    },
+    // 自定义选项是否可以选择一半值
+    exactCanHalf: {
+      type: Boolean,
+      default: false
+    },
+    // 是否展示自定义自动按钮
+    showConfirmButton: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -143,7 +155,7 @@ export default {
       border-radius: 4px;
       font-size: 14px;
       text-align: center;
-      background-color: #fff;
+      background-color: transparent;
       cursor: pointer;
       &:first-child {
         margin-left: 0;
@@ -190,7 +202,7 @@ export default {
     }
 
     .date-time-container .date-time-item {
-      width: 104px;
+      width: 130px;
       height: 24px;
       line-height: 24px;
     }
@@ -202,8 +214,9 @@ export default {
     }
     .date-time-container {
       padding: 0;
-      background-color: transparent;
+      background-color: transparent !important;
       &.active {
+        background-color: transparent !important;
         input,
         .date-time-item.left::after {
           background-color: $color_main;

+ 79 - 23
apps/bigmember_pc/src/components/selector/TimeSelectorContent.vue

@@ -23,7 +23,7 @@
     <div
       v-if="showExactConf"
       class="date-time-container"
-      :class="{ active: state.exact === 'exact' }"
+      :class="{ active: showActive}"
     >
       <el-date-picker
         v-model="dateTimePickerState.start"
@@ -37,6 +37,7 @@
         :picker-options="startPickerOptions"
         :append-to-body="false"
         @change="startDatePickerChange"
+        @focus="showConfirmHandle"
       >
       </el-date-picker>
       <el-date-picker
@@ -51,8 +52,10 @@
         :picker-options="endPickerOptions"
         :append-to-body="false"
         @change="endDatePickerChange"
+        @focus="showConfirmHandle"
       >
       </el-date-picker>
+      <el-button class="confirm-btn" v-if="showConfirmButton && showConBtn" @click="confirmHandle" >确定</el-button>
     </div>
   </div>
 </template>
@@ -285,6 +288,16 @@ export default {
     showExact: {
       type: Boolean,
       default: true
+    },
+    // 自定义组件可以搬选返回
+    exactCanHalf: {
+      type: Boolean,
+      default: false
+    },
+    // 是否展示确认按钮
+    showConfirmButton: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -319,7 +332,8 @@ export default {
             return time.getTime() < +new Date(start)
           }
         }
-      }
+      },
+      showConBtn: false
     }
   },
   computed: {
@@ -333,6 +347,15 @@ export default {
     },
     state() {
       return this.getState()
+    },
+    showActive () {
+      let result = false
+      if(this.showConfirmButton) {
+        result = this.state.exact === 'exact' && (this.dateTimePickerState.start || this.dateTimePickerState.end) && !this.showConBtn
+      } else {
+        let result = this.state.exact === 'exact'
+      }
+      return result
     }
   },
   created() {
@@ -407,12 +430,12 @@ export default {
             break
           }
           case 'exact': {
-            if (!data.start || !data.end) break
-            if (data.start < data.end) {
-              this.timeSelectList.forEach((v) => (v.selected = false))
-              this.dateTimePickerState.start = new Date(data.start)
-              this.dateTimePickerState.end = new Date(data.end)
+            if(!this.exactCanHalf && !this.showConfirmButton) {
+              if (!data.start || !data.end) break
             }
+            this.timeSelectList.forEach((v) => (v.selected = false))
+            this.dateTimePickerState.start = data.start ? new Date(data.start) : null
+            this.dateTimePickerState.end =data.end ?  new Date(data.end) : null
             break
           }
           default: {
@@ -591,27 +614,48 @@ export default {
     },
     startDatePickerChange(start) {
       const { end } = this.dateTimePickerState
-      if (start && end) {
-        // start和end都有值
-        this.setTimeSelectListState()
-        this.onChange()
-      } else if (!start && !end) {
-        // start和end都没值
-        this.setTimeSelectListState(this.defaultSelectedKey)
-        this.onChange()
+      if(!this.exactCanHalf && !this.showConfirmButton) {
+        if (start && end) {
+          // start和end都有值
+          this.setTimeSelectListState()
+          this.onChange()
+        } else if (!start && !end) {
+          // start和end都没值
+          this.setTimeSelectListState(this.defaultSelectedKey)
+          this.onChange()
+        }
+      }
+      if(this.showConfirmButton) {
+        this.showConBtn = true
       }
     },
     endDatePickerChange(end) {
       const { start } = this.dateTimePickerState
-      if (start && end) {
-        // start和end都有值
-        this.setTimeSelectListState()
-        this.onChange()
-      } else if (!start && !end) {
-        // start和end都没值
-        this.setTimeSelectListState(this.defaultSelectedKey)
-        this.onChange()
+      if(!this.exactCanHalf && !this.showConfirmButton) {
+        if (start && end) {
+          // start和end都有值
+          this.setTimeSelectListState()
+          this.onChange()
+        } else if (!start && !end) {
+          // start和end都没值
+          this.setTimeSelectListState(this.defaultSelectedKey)
+          this.onChange()
+        }
+      }
+      if(this.showConfirmButton) {
+        this.showConBtn = true
       }
+    },
+    // 展示确定按钮
+    showConfirmHandle () {
+      this.showConBtn = true
+    },
+
+    // 确定操作
+    confirmHandle () {
+      this.setTimeSelectListState()
+      this.onChange()
+      this.showConBtn = false
     }
   }
 }
@@ -678,4 +722,16 @@ export default {
     }
   }
 }
+.confirm-btn{
+  width:48px;
+  height:24px;
+  border-radius: 2px;
+  font-size: 14px;
+  text-align: center;
+  color: #fff;
+  background: #2ABED1;
+  padding: 0;
+  line-height: 22px;
+  margin-left: 8px;
+}
 </style>

+ 0 - 3
apps/bigmember_pc/src/components/selector/timeDropdown.vue

@@ -231,7 +231,6 @@ export default {
       this.isCustom = true
       this.$refs.selectSelector.toggleMenu()
       this.$refs.customPopover[0].doClose()
-      this.$emit('input', this.getState())
       this.$emit('change', this.getState())
     },
     handleChange(item) {
@@ -243,7 +242,6 @@ export default {
         this.time.end = ''
         this.$refs.selectSelector.toggleMenu()
         this.$refs.customPopover[0].doClose()
-        this.$emit('input', this.getState())
         this.$emit('change', this.getState())
       } else {
         this.isCustom = true
@@ -283,7 +281,6 @@ export default {
       } else {
         this.activeValue = data
       }
-      this.$emit('input', this.getState())
     }
   }
 }

+ 1 - 1
apps/bigmember_pc/src/router/modules/search.js

@@ -3,7 +3,7 @@ export default [
   // 标讯搜索
   {
     path: '/search/bidding',
-    alias: ['/jylab/supsearch/index.html'],
+    alias: ['/jylab/supsearch/index.html', '/jylab/bi/index.html', '/jylab/medical/index.html'],
     name: 'bidding-search',
     component: () => import('@/views/search/bidding/index.vue')
   },

+ 6 - 1
apps/bigmember_pc/src/router/router-interceptors.js

@@ -44,7 +44,12 @@ const powerCheckWhiteList = [
   'portrayal_loading',
   'article_detail',
   'recommen-list',
-  'business_detail'
+  'business_detail',
+  'bidding-search',
+  'ent-search',
+  'purchase-search',
+  'supply-search',
+  'nzj-search'
 ]
 
 const regListCheck = function (regList, path) {

+ 9 - 0
apps/bigmember_pc/src/utils/common.js

@@ -14,6 +14,15 @@ Vue.prototype.$checkLogin = function () {
   }
 }
 
+Vue.prototype.$showLoginDialog = function () {
+  try {
+    openLoginDig && openLoginDig(...arguments)
+  } catch (error) {
+    console.log(error)
+    location.href = '/notin/page?close_goBack=1'
+  }
+}
+
 Vue.prototype.contactCustomer = function (vm) {
   tryCallHooks({
     fn: () => {

+ 26 - 0
apps/bigmember_pc/src/utils/format/info-type-transform.js

@@ -131,4 +131,30 @@ export class InfoTypeTransform {
     }
     return map
   }
+  /**
+   * 输入参数
+   * {
+   *   招标公告: ['询价'],
+   *   招标预告: ['预告', '预审', '预审结果', '论证意见', '需求公示'],
+   * }
+   * 输出格式:一级分类为空数组则只显示一级分类,二级分类长度不同长度只显示二级分类
+   * ['询价', '招标预告']
+   */
+
+  static formatMoreMapToList (map) {
+    let resultArr = []
+    if(!map) return resultArr
+    if(typeof map === 'object') {
+      for (const key in infoTypeMapExp) {
+        if(map[key] && Array.isArray(map[key])) {
+          if(map[key].length === infoTypeMapExp[key].length) {
+            resultArr.push(key)
+          } else {
+            resultArr = resultArr.concat(map[key])
+          }
+        }
+      }
+    }
+    return resultArr
+  }
 }

+ 67 - 10
apps/bigmember_pc/src/utils/format/search-bid-filter.js

@@ -297,7 +297,7 @@ export class FilterHistoryAjaxModel2ViewModel {
    * 参数val示例:'1653321600_1654012800'
    * 参数val示例:'fiveyear'
    */
-  static formatTime(val) {
+  static formatTime(val, spl= '_') {
     const result = {
       publishTime: undefined,
       publishTimeText: undefined
@@ -313,10 +313,10 @@ export class FilterHistoryAjaxModel2ViewModel {
       exact: 'exact'
     }
     // 如果是精确时间
-    if (val.includes('_')) {
-      const split = val.split('_')
-      const start = split[0] * 1000
-      const end = split[1] * 1000
+    if (val.indexOf(spl) > -1 && val.indexOf('lately') === -1) {
+      const split = val.split(spl)
+      const start = split[0].toString().length > 10 ? Number(split[0]) :  split[0] * 1000
+      const end = split[1].toString().length > 10 ? Number(split[1]) : split[1] * 1000
       const textArr = []
       if (start && !isNaN(start)) {
         t.start = start
@@ -326,22 +326,26 @@ export class FilterHistoryAjaxModel2ViewModel {
         t.end = end
         textArr[1] = dateFormatter(end, 'yyyy/MM/dd')
       }
-
+      let publishTime = ''
       let publishTimeText = ''
       if (textArr[0] && textArr[1]) {
         publishTimeText = textArr.join('-')
+        publishTime = val
       } else if (textArr[0] && !textArr[1]) {
         publishTimeText = `${textArr[0]}以后`
+        publishTime = split[0] + '_' + '0'
       } else if (!textArr[0] && textArr[1]) {
         publishTimeText = `${textArr[1]}以前`
+        publishTime = '0' + '_' + split[1]
       }
       // 计算text
       result.publishTimeText = publishTimeText
+      result.publishTime = publishTime
     } else {
       t.exact = val
       result.publishTimeText = map[val] || ''
+      result.publishTime = val
     }
-    result.publishTime = val
     return result
   }
 
@@ -491,7 +495,7 @@ export class FilterHistoryViewModel2AjaxModel {
     // 金额筛选整理
     const { minPrice, maxPrice } = this.formatPrice(map.price)
     // 时间筛选整理
-    // const publishTime = this.formatTime(map.publishTime)
+    const publishTime = map.publishTime?.includes('_') ? this.formatExactTime(map.publishTime, '_', '_') : this.formatTime(map.publishTime)
     // 信息类型
     let subtype = this.formatInfoType(map.subtype)
     if (subtype) {
@@ -503,14 +507,13 @@ export class FilterHistoryViewModel2AjaxModel {
     const { additionalWords, wordsMode } = this.formatSelectMoreKey(map.selectMoreKey, map.additionalWords, map.wordsMode)
     // tab值
     const searchGroup = this.formatSearchGroup(map.searchGroup)
-
     const formatted = {
       searchvalue: map.input,
       selectType,
       industry,
       minprice: minPrice,
       maxprice: maxPrice,
-      publishtime: map.publishTime, // 发布时间
+      publishtime: publishTime, // 发布时间
       subtype,   // 信息类型
       buyerclass: buyerClass, // 采购单位类型
       buyertel: map.buyertel,  // 采购单位联系方式
@@ -652,6 +655,24 @@ export class FilterHistoryViewModel2AjaxModel {
     return sortedTime
   }
 
+  // 格式化自定义时间
+  static formatExactTime (time, join = '-', split='_') {
+    if(!time) return ''
+    let result = ''
+    let arr = []
+    if(time.indexOf(split) > -1) {
+      arr = time.split(split)
+    }
+    if(Number(arr[0]) === 0 && Number(arr[1]) > 0) {
+      result = `${join}${arr[1]}`
+    } else if(Number(arr[1]) === 0 && Number(arr[0]) > 0) {
+      result = `${arr[0]}${join}`
+    } else {
+      result = `${arr[0]}${join}${arr[1]}`
+    }
+    return result
+  }
+
   /**
    * 信息类型整理
    * @param String val
@@ -999,3 +1020,39 @@ export class FilterHistoryAjaxModelRestore {
     return result
   }
 }
+
+/**
+ * 二级地区对象转换成,单个area、city、district
+ */
+export function areaObjTwoToSingle(obj, split = ',') {
+  const map = {
+    area: '',
+    city: ''
+  }
+  if (!obj) return map
+  const area = []
+  let city = []
+  for (const key in obj) {
+    if (Array.isArray(obj[key])) {
+      if (obj[key].length === 0) {
+        area.push(key)
+      } else {
+        city = city.concat(obj[key])
+      }
+    }
+  }
+  return {
+    area: area.join(split),
+    city: city.join(split)
+  }
+}
+
+// 信息类型 map
+/**
+ * 信息类型格式化,将map类型数据最终格式化成接口所需
+ * @param info
+ */
+export function infoTypeMapFormat (infoType = {}) {
+  const resultType = InfoTypeTransform.formatMoreMapToList(infoType)
+  return resultType ? resultType.join(',') : ''
+}

+ 2 - 0
apps/bigmember_pc/src/views/article-content/pages/Article.vue

@@ -24,6 +24,7 @@ import RecommendCustomersList from '@/views/article-content/components/Recommend
 import ContentSummary from '@/views/article-content/components/ContentSummary.vue'
 import RecommendEnt from '@/views/article-content/components/RecommendEnt.vue'
 import QuickMonitor from '@/composables/quick-monitor/component/QuickMonitor.vue'
+import CheckUserDialog from '@/components/dialog/CheckUserDialog'
 import {
   useContentStore,
   ContentExpandsModel,
@@ -415,6 +416,7 @@ function doClickFreeView() {
             @doOpenCollect="doOpenCollectDialog"
             @doOpenCustomer="doOpenCustomer"
           ></content-mask>
+          <CheckUserDialog />
 
           <div v-if="!canShowMask.show">
             <!--  顶部提示 -->

+ 3 - 0
apps/bigmember_pc/src/views/portrayal/EntPortrayal.vue

@@ -249,6 +249,7 @@
         </div>
       </div>
     </div>
+    <CheckUserDialog />
     <!-- 分享弹窗 -->
     <shareBox ref="shareBox"></shareBox>
   </Layout>
@@ -270,6 +271,7 @@ import WorkspaceButtonGroup from '@/components/dialog/WorkspaceButtonGroup.vue'
 import { mapState } from 'vuex'
 import { Dialog, Input, TabPane, Tabs } from 'element-ui'
 import { dateFormatter, getAssetsFile, moneyUnit } from '@/utils'
+import CheckUserDialog from '@/components/dialog/CheckUserDialog'
 import tdk from '@/utils/mixins/set-tdk.js'
 import {
   getEntWinnerSelect,
@@ -297,6 +299,7 @@ export default {
     Layout,
     BidInfoActive,
     WorkspaceButtonGroup,
+    CheckUserDialog,
     ContactList,
     MaskCard,
     Empty,

+ 3 - 0
apps/bigmember_pc/src/views/portrayal/EntSearchPortrayal.vue

@@ -371,6 +371,7 @@
       <!-- 留资弹窗 -->
       <CollectInfo ref="collectRef"></CollectInfo>
     </div>
+    <CheckUserDialog />
     <!-- 分享弹窗 -->
     <shareBox ref="shareBox"></shareBox>
     <!-- 右侧未登录模块 -->
@@ -401,6 +402,7 @@ import EntFollowStar from './components/EntFollowStar.vue'
 import shareBox from '@/components/shareBox/index.vue'
 import AsideNewscards from './components/AsideNewscards.vue'
 import WorkspaceButtonGroup from '@/components/dialog/WorkspaceButtonGroup.vue'
+import CheckUserDialog from '@/components/dialog/CheckUserDialog'
 import Empty from '@/components/common/Empty'
 import { mapState } from 'vuex'
 import { TabPane, Tabs } from 'element-ui'
@@ -442,6 +444,7 @@ export default {
     DynamicList,
     AsideNewscards,
     WorkspaceButtonGroup,
+    CheckUserDialog,
     shareBox,
     crmAction,
     Empty

+ 9 - 6
apps/bigmember_pc/src/views/portrayal/UnitPortrayal.vue

@@ -455,6 +455,7 @@
       </template>
       您可联系客服,申请升级产品套餐,监控更多业主
     </common-dialog>
+    <CheckUserDialog />
     <CollectInfo ref="collectRef"></CollectInfo>
     <!-- 分享弹窗 -->
     <shareBox ref="shareBox"></shareBox>
@@ -483,6 +484,7 @@ import vPopper from '@/components/common/Popper.vue'
 import shareBox from '@/components/shareBox/index.vue'
 import AsideNewscards from './components/AsideNewscards.vue'
 import WorkspaceButtonGroup from '@/components/dialog/WorkspaceButtonGroup.vue'
+import CheckUserDialog from '@/components/dialog/CheckUserDialog'
 import monitorCom from '@/components/common/Monitor.vue'
 import { Button, Dialog, Popover } from 'element-ui'
 import { getAssetsFile, openSelfLink } from '@/utils/'
@@ -520,6 +522,7 @@ export default {
     [Dialog.name]: Dialog,
     [Button.name]: Button,
     [Popover.name]: Popover,
+    CheckUserDialog,
     Empty,
     vPopper,
     shareBox,
@@ -2049,14 +2052,14 @@ export default {
       left: 50%;
       margin-top: 0;
       transform: translate(-50%, -50%);
-    }
 
-    .el-dialog {
-      border-radius: 8px;
-    }
+      &.el-dialog {
+        border-radius: 8px;
+      }
 
-    .el-dialog__header {
-      padding: 0;
+      .el-dialog__header {
+        padding: 0;
+      }
     }
   }
 

+ 20 - 3
apps/bigmember_pc/src/views/potential-for/PotenTial.vue

@@ -16,6 +16,7 @@
         @tabChange="tabChange"
         @export="onExport"
         @changeArea="onChangeArea"
+        @changePrice="changePrice"
         @changeId="onChangeSelectedId"
         ref="myList"
         :showFiltrate="true"
@@ -59,6 +60,7 @@ import { dateFormatter } from '@/utils/'
 import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
 import ExportTip from '@/views/portrayal/components/DataExportTip.vue'
 // import { Loading } from 'element-ui'
+import { debounce } from 'lodash'
 export default {
   name: 'potential',
   components: {
@@ -88,6 +90,7 @@ export default {
       ],
       Loading: false,
       area: {},
+      price: {},
       selectIds: [],
       showDataExportDialog: false,
       checkNum: 0,
@@ -121,6 +124,8 @@ export default {
         startTime: this.startTime,
         endTime: this.endTime,
         area: areaParams,
+        minprice: this.price.min || undefined,
+        maxprice: this.price.max || undefined,
         total: this.filterCount
       }
       getPotenList(params).then((res) => {
@@ -175,7 +180,7 @@ export default {
         this.startTime = parseInt(item.start / 1000)
         this.endTime = parseInt(item.end / 1000)
       }
-      this.getnewList()
+      this.doSearch()
     },
     tabChange(item) {
       this.pageNum = 1
@@ -186,11 +191,21 @@ export default {
       this.potenObj.list = []
       this.getnewList()
     },
-    onChangeArea(regionMap) {
+    doSearch() {
       this.pageNum = 1
       this.$refs.myList.listState.pageNum = 1
-      this.area = regionMap || {}
+      this.getnewListDebounce()
+    },
+    getnewListDebounce: debounce(function() {
       this.getnewList()
+    }, 1000),
+    changePrice(v) {
+      this.price = v
+      this.doSearch()
+    },
+    onChangeArea(regionMap) {
+      this.area = regionMap || {}
+      this.doSearch()
     },
     async checkBoxChange(data) {
       const params = {
@@ -225,6 +240,8 @@ export default {
         dataType: this.dataType,
         startTime: this.startTime,
         endTime: this.endTime,
+        minprice: this.price.min || undefined,
+        maxprice: this.price.max || undefined,
         area:
           Object.keys(this.area).length === 0 ? '' : JSON.stringify(this.area),
         count:

+ 7 - 1
apps/bigmember_pc/src/views/push-setting/index.vue

@@ -668,7 +668,13 @@ export default {
           if (this.free) {
             return '每日上午、下午各推送一次'
           } else {
-            return `每日推送:${times.toString().replace(/,/g, '、')}`
+            const suffix = times?.toString().replace(/,/g, '、')
+            return `每日推送:${suffix}`
+            if (suffix) {
+              return `每日推送:${suffix}`
+            } else {
+              return '每日推送'
+            }
           }
         case 3:
           return `每周推送(周五 ${str.toString().replace(/,/g, '、')})`

+ 1 - 6
apps/bigmember_pc/src/views/search/bidding/components/history-filter-dialog.vue

@@ -121,15 +121,10 @@ function beforeClose () {
     width: 686px;
     overflow: hidden;
   }
-  .filter-data-container{
-    max-height: 400px;
-    overflow-y: scroll;
-    width: 694px;
-  }
   .filter-data-container {
     max-height: 400px;
     overflow-y: scroll;
-    width: 694px;
+    //width: 694px;
 
     .f-l-title{
       padding: 8px 0 2px;

+ 612 - 0
apps/bigmember_pc/src/views/search/bidding/components/recommend-card.vue

@@ -0,0 +1,612 @@
+<script setup>
+import { getCurrentInstance, watch} from 'vue'
+import MarketUserScatter from '@/views/analysisReport/components/MarketUserScatter'
+import BuyerScaleScatter from '@/views/analysisReport/components/BuyerScaleScatter'
+import { SearchBidModel } from '../model'
+const {
+  recommendCardCircleModel
+} = SearchBidModel
+
+const that = getCurrentInstance().proxy
+
+const {
+  toggleAdvancedContent,
+  advancedInfo,
+  onClickInterested,
+  goToContent,
+  getProjectTitle,
+  goToReport,
+  getShowChart,
+  getNotModuleDataStatus,
+  getNowInfo,
+  nowModuleName,
+  chartCustomData,
+  showModuleChart,
+  chart,
+  test
+} = recommendCardCircleModel
+</script>
+
+<template>
+  <!-- 超前项目推荐&&市场分析报告 -->
+  <div id="jyChartCom">
+    <div class="advanced-pro-rec" v-show="advancedInfo.show">
+      <div class="p-lr-32">
+        <div class="c-a-r-top">
+          <div class="c-a-r-title">
+            <div class="r-title-text">{{ getNowInfo.title }}</div>
+            <div class="r-title-tip">
+                  <span
+                    v-if="advancedInfo.showContent || getNotModuleDataStatus"
+                  >{{ getNowInfo.desc }}</span
+                  >
+              <span
+                v-else
+                class="total-item"
+                v-for="(item, index) in advancedInfo.briefList"
+                :key="index"
+              >
+                    {{ item.key }}:<span class="highlight-text"
+              ><em>{{ item.value }}</em
+              >条</span
+              >
+                  </span>
+            </div>
+          </div>
+          <div class="c-a-r-option">
+            <div
+              v-if="nowModuleName === '市场分析报告'"
+              class="c-view-report c-view-common"
+              @click="goToReport"
+            >
+              查看完整报告
+            </div>
+            <div
+              class="c-view-interest c-view-common"
+              @click="
+                    onClickInterested(
+                      nowModuleName === '市场分析报告' ? 'B' : 'A'
+                    )
+                  "
+            >
+              感兴趣点我
+            </div>
+            <div class="c-up-or-down" @click="toggleAdvancedContent()">
+              <el-button type="text">
+                {{ advancedInfo.showContent ? '收起' : '展开' }}
+                <i
+                  class="el-icon--right"
+                  :class="
+                        'el-icon-arrow-' +
+                        (advancedInfo.showContent ? 'up' : 'down')
+                      "
+                ></i>
+              </el-button>
+            </div>
+          </div>
+        </div>
+      </div>
+      <el-collapse-transition>
+
+        <div v-show="advancedInfo.showContent">
+          <!-- 超前项目 -->
+          <div
+            class="project-module"
+            v-if="nowModuleName === '超前项目推荐'"
+            :class="{ 'remove-bl': !getShowChart }"
+          >
+            <div class="project-item">
+              <div class="left-tag total-color">累计发布</div>
+              <div>
+                    <span
+                      class="total-item"
+                      v-for="(item, index) in advancedInfo.briefList"
+                      :key="index"
+                    >
+                      {{ item.key }}:<span class="highlight-text"
+                    ><em>{{ item.value }}</em
+                    >条</span
+                    >
+                    </span>
+              </div>
+            </div>
+            <div class="project-item">
+              <div class="left-tag new-color">最新项目</div>
+              <div class="new-group">
+                    <span
+                      class="ellipsis new-item"
+                      @click="goToContent(item)"
+                      v-for="(item, index) in advancedInfo.projectList"
+                      :key="index"
+                      v-html="getProjectTitle(item)"
+                    ></span>
+              </div>
+            </div>
+          </div>
+          <!-- 市场分析报告 -->
+          <div class="custom-report" v-if="getShowChart">
+            <div class="c-a-r-top" v-if="nowModuleName === '超前项目推荐'">
+              <div class="c-a-r-title">
+                <div class="r-title-text">市场分析报告</div>
+                <div class="r-title-tip">
+                  量身定制个性化报告,分析市场竞争格局,为企业找准市场机会!
+                </div>
+              </div>
+              <div class="c-a-r-option">
+                <div
+                  class="c-view-report c-view-common"
+                  @click="goToReport"
+                >
+                  查看完整报告
+                </div>
+                <div
+                  class="c-view-interest c-view-common"
+                  @click="onClickInterested('B')"
+                >
+                  感兴趣点我
+                </div>
+              </div>
+            </div>
+            <div class="c-a-r-chart">
+              <div
+                class="chart-common"
+                id="customerChart"
+                v-show="showModuleChart !== 'customer_scale'"
+              >
+                <div class="chart-title">客户分布:</div>
+                <div class="c-c-content">
+                  <!-- <div id="chartTreeMap"></div> -->
+                  <MarketUserScatter
+                    min-height="min-height: 211px"
+                    top="-8px"
+                    :key="chart.treeMapKey"
+                    ref="treeMap"
+                    :chartData="chart.treeMapData"
+                  />
+                </div>
+              </div>
+              <div
+                class="chart-common"
+                id="winnerChart"
+                v-show="showModuleChart !== 'winner_time_distribution'"
+              >
+                <div class="chart-title">中标规模分布:</div>
+                <div class="c-c-content chart-line">
+                  <!-- <div id="chartLineChart"></div> -->
+                  <BuyerScaleScatter
+                    :key="chart.winnerLineKey"
+                    height="211px"
+                    :chartData="chart.winnerLineData"
+                  />
+                </div>
+              </div>
+              <div
+                class="chart-common"
+                id="buyerChart"
+                v-show="showModuleChart !== 'buyer_time_distribution'"
+              >
+                <div class="chart-title">采购规模分布:</div>
+                <div class="c-c-content chart-line">
+                  <!-- <div id="chartLineChartBuyer"></div> -->
+                  <BuyerScaleScatter
+                    :key="chart.buyLineKey"
+                    height="211px"
+                    :chartData="chart.buyLineData"
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+          <!-- <custom-report :show-title="nowModuleName === '超前项目推荐'" @onReport="goToReport" @onInterest="onClickInterested('B')" v-if="getShowChart" :chartCustomData="chartCustomData"></custom-report> -->
+        </div>
+      </el-collapse-transition>
+    </div>
+    <el-dialog
+      custom-class="advanced-dialog"
+      :visible.sync="advancedInfo.showDialog"
+    >
+      <img
+        class="advanced-dialog--head"
+        src="@/assets/images/advanced/dialog-head.png"
+        alt="剑鱼标讯"
+      />
+      <img
+        class="advanced-dialog--qrcode"
+        src="@/assets/images/advanced/dialog-qrcode.png"
+        alt="扫码联系客服"
+      />
+      <div class="advanced-dialog--info">
+        <h4>扫码联系客服</h4>
+        <h4>{{ advancedInfo.dialogContent }}</h4>
+        <p>专业招投标大数据服务平台丨国家信息中心大数据战略合作商</p>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+#jyChartCom {
+  background: #fff;
+  border-radius: 8px;
+
+  .advanced-pro-rec {
+    margin-top: 16px;
+    margin-bottom: 16px;
+
+    .custom-report {
+      padding: 0 16px 0 32px;
+    }
+
+    // 超前项目模块
+    $total-color: #2abed1;
+    $new-color: #ff9f40;
+
+    .total-item {
+      line-height: 24px;
+
+      em {
+        font-weight: 700;
+        font-size: 20px;
+      }
+
+      & + .total-item {
+        margin-left: 72px;
+      }
+    }
+
+    .project-module {
+      font-weight: 400;
+      font-size: 14px;
+      line-height: 22px;
+      color: #1d1d1d;
+      padding: 24px 32px;
+      border-bottom: 1px solid #ececec;
+
+      &.remove-bl {
+        border-bottom-color: transparent;
+      }
+
+      .left-tag {
+        flex-shrink: 0;
+        display: inline-block;
+        font-size: 14px;
+        line-height: 24px;
+        color: #ffffff;
+        padding: 0 9px;
+        border-radius: 0 12px 12px 0;
+        margin-right: 24px;
+
+        &.total-color {
+          background: $total-color;
+        }
+
+        &.new-color {
+          background: $new-color;
+        }
+      }
+
+      .project-item {
+        width: 100%;
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+
+        & + .project-item {
+          margin-top: 18px;
+        }
+      }
+
+      .new-group {
+        display: flex;
+        flex-direction: row;
+        width: 100%;
+      }
+
+      .new-item {
+        cursor: pointer;
+        font-size: 16px;
+        line-height: 24px;
+        max-width: calc(50% - 72px);
+
+        & + .new-item {
+          margin-left: 36px;
+        }
+
+        &::before {
+          content: '';
+          display: inline-block;
+          vertical-align: middle;
+          width: 7px;
+          height: 7px;
+          border-radius: 50%;
+          margin-right: 8px;
+          background: $new-color;
+        }
+      }
+    }
+
+    .p-lr-32 {
+      padding: 0 16px 0 32px;
+    }
+
+    .c-a-r-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      height: 60px;
+      border-bottom: 1px dashed #e0e0e0;
+
+      .c-a-r-title {
+        display: flex;
+        align-items: center;
+        height: 100%;
+        line-height: 21px;
+        font-family: 'Microsoft YaHei';
+        font-style: normal;
+        font-weight: 400;
+      }
+
+      .r-title-text {
+        display: flex;
+        align-items: center;
+        width: fit-content;
+        height: 95%;
+        margin-top: 5px;
+        color: #2cb7ca;
+        font-size: 16px;
+        border-bottom: 2px solid #2cb7ca;
+      }
+
+      .r-title-tip {
+        margin-top: 5px;
+        margin-left: 32px;
+        color: #686868;
+        font-size: 14px;
+      }
+    }
+
+    .c-a-r-option {
+      display: flex;
+      align-items: center;
+    }
+
+    .c-view-common {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 0 17px;
+      height: 30px;
+      font-family: 'Microsoft YaHei';
+      font-style: normal;
+      font-weight: 400;
+      font-size: 14px;
+      line-height: 22px;
+      border-radius: 4px;
+      cursor: pointer;
+    }
+
+    .c-view-report {
+      border: 1px solid #2abed1;
+      color: #2abed1;
+    }
+
+    .chart-common {
+      /* width: 590px; */
+      height: 280px;
+    }
+
+    .c-c-content {
+      width: 383px;
+      height: 211px;
+    }
+
+    .c-c-content.chart-line {
+      width: 558px;
+    }
+
+    .c-up-or-down {
+      margin-left: 32px;
+
+      .el-icon--right {
+        margin-left: 2px;
+      }
+
+      .el-button--text {
+        padding: 0;
+        font-weight: 400;
+        font-size: 14px;
+        line-height: 19px;
+        color: #686868;
+      }
+    }
+
+    .c-view-interest {
+      margin-left: 36px;
+      background: #2abed1;
+      color: #fff;
+      border: 1px solid #2abdd1;
+    }
+
+    .c-a-r-chart {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-top: 16px;
+    }
+
+    .chart-title {
+      padding: 16px 0 12px 0;
+      font-family: 'Microsoft YaHei';
+      font-style: normal;
+      font-weight: 400;
+      font-size: 16px;
+      line-height: 24px;
+      color: #1d1d1d;
+    }
+  }
+}
+.custom-report {
+  padding: 0 32px;
+
+  .c-a-r-top {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 60px;
+    border-bottom: 1px dashed #e0e0e0;
+
+    .c-a-r-title {
+      display: flex;
+      align-items: center;
+      height: 100%;
+      line-height: 21px;
+      font-family: 'Microsoft YaHei';
+      font-style: normal;
+      font-weight: 400;
+    }
+
+    .r-title-text {
+      display: flex;
+      align-items: center;
+      width: fit-content;
+      height: 95%;
+      margin-top: 5px;
+      color: #2cb7ca;
+      font-size: 16px;
+      border-bottom: 2px solid #2cb7ca;
+    }
+
+    .r-title-tip {
+      margin-top: 5px;
+      margin-left: 32px;
+      color: #686868;
+      font-size: 14px;
+    }
+  }
+
+  .c-a-r-option {
+    display: flex;
+    align-items: center;
+  }
+
+  .c-view-common {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 0 17px;
+    height: 30px;
+    font-family: 'Microsoft YaHei';
+    font-style: normal;
+    font-weight: 400;
+    font-size: 14px;
+    line-height: 22px;
+    border-radius: 4px;
+    cursor: pointer;
+  }
+
+  .c-view-report {
+    border: 1px solid #2abed1;
+    color: #2abed1;
+  }
+
+  .chart-common {
+    /* width: 590px; */
+    height: 280px;
+  }
+
+  .c-c-content {
+    width: 383px;
+    height: 211px;
+  }
+
+  .c-c-content.chart-line {
+    width: 558px;
+  }
+
+  .c-view-interest {
+    margin-left: 36px;
+    background: #2abed1;
+    color: #fff;
+    border: 1px solid #2abdd1;
+  }
+
+  .c-a-r-chart {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-top: 16px;
+  }
+
+  .chart-title {
+    padding: 16px 0 12px 0;
+    font-family: 'Microsoft YaHei';
+    font-style: normal;
+    font-weight: 400;
+    font-size: 16px;
+    line-height: 24px;
+    color: #1d1d1d;
+  }
+}
+::v-deep {
+  .advanced-dialog {
+    width: 370px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    border-radius: 8px;
+
+    .el-dialog__body {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+    }
+
+    .el-dialog__close {
+      font-size: 20px;
+      font-weight: bold;
+      color: #2abdd1;
+      cursor: pointer;
+    }
+
+    .el-dialog__header,
+    .el-dialog__body {
+      padding: 0;
+    }
+
+    &--head {
+      margin-top: -58px;
+      max-width: 100%;
+    }
+
+    &--qrcode {
+      margin-top: 26px;
+      margin-bottom: 22px;
+      width: 154px;
+      height: 154px;
+    }
+
+    &--info {
+      padding: 24px 20px;
+      padding-top: 0;
+
+      h4 {
+        font-weight: 400;
+        font-size: 16px;
+        line-height: 24px;
+        text-align: center;
+        color: #1d1d1d;
+      }
+
+      p {
+        margin-top: 16px;
+        font-weight: 400;
+        font-size: 12px;
+        line-height: 18px;
+        text-align: center;
+        color: #999999;
+      }
+    }
+  }
+}
+</style>
+

+ 33 - 13
apps/bigmember_pc/src/views/search/bidding/components/search-bid-filter.vue

@@ -2,6 +2,7 @@
 import { computed, watch } from 'vue'
 import { SearchBidModel } from '../model/index'
 import { getPropertyFilters } from '@/api/modules/bi'
+import { getCMCustomInfo } from '@/api/modules/'
 import SearchSchemaFilter from '@/views/search/components/search-schema-filter.vue'
 import SelectorWithBasePower from '@/components/filter-items/SelectorWithBasePower.vue'
 import $bus from '@/utils/bus'
@@ -12,9 +13,11 @@ const {
   isInApp,
   isInWeb,
   inBIPropertyIframe,
+  inResourceBIIframe,
   activeTab,
   guideGoWorkSpace,
   filterState,
+  updateFilterBase,
   doQuery,
   showFilter,
   goLogin,
@@ -57,6 +60,31 @@ if(inBIPropertyIframe) {
   getBiPropertyFilters()
 }
 
+// 中国移动定制搜索条件-融创
+async function getCustomInfo () {
+  const { error_code:code, data = [] } = await getCMCustomInfo()
+  if(code === 0) {
+    if (data) {
+      const expandSearchParams = {}
+      data.forEach(item => {
+        if(item.key && item.defaultVal) {
+          const par = {
+            key: item.key,
+            value: item.defaultVal
+          }
+          expandSearchParams[item.key] = item.defaultVal
+          updateFilterBase(par)
+        }
+      })
+      doUpdateData(data, 'more')
+      doQuery(expandSearchParams, 'firstPage')
+    }
+  }
+}
+if(!inBIPropertyIframe) {
+  getCustomInfo()
+}
+
 const customMoreSchema = computed(() => {
   return {
     vipModuleShow: true,
@@ -131,7 +159,7 @@ function doChangeFilter() {
             v-model="filterState"
             vipMaskShow
             :baseMaskShow="!isLogin"
-            :vipModuleShow="isInApp && isVip"
+            :vipModuleShow="isInApp && !inBIPropertyIframe"
             @clickVipMask="noPower"
             @clickBaseMask="toLogin"
             @change="doChangeFilter"
@@ -143,24 +171,16 @@ function doChangeFilter() {
 </template>
 
 <style lang="scss" scoped>
-.in-app {
-  .search-bid-filter {
-    .wrap-line {
-      ::v-deep {
-        .vip-module {
-          margin-left: 0;
-        }
-      }
-    }
-  }
-}
 .search-bid-filter {
   position: relative;
   padding: 16px 32px;
   .wrap-line {
     ::v-deep {
+      .filter-layout .select-prefix {
+        background: transparent;
+      }
       .vip-module {
-        margin-left: 105px;
+        margin-left: 92px;
       }
     }
   }

+ 26 - 6
apps/bigmember_pc/src/views/search/bidding/components/search-bid-header.vue

@@ -4,12 +4,14 @@ import SearchHeaderCard from '@/views/search/components/search-header-card.vue'
 import KeywordTagsPc from '@/views/search/components/keyword-tags.vue'
 import CommonSingleChoice from '@/components/filter-items/CommonSingleChoice.vue'
 import { SearchBidModel } from '../model/index'
+import $bus from '@/utils/bus'
 
 const {
   isInApp,
   isLogin,
   inResourceBIIframe,
   inBIPropertyIframe,
+  isBidField,
   cooperateCode,
   inputKeywordsState,
   doQuery,
@@ -23,8 +25,9 @@ const { searchTabs } = SearchTabsModel
 
 const that = getCurrentInstance().proxy
 
-function doSearch() {
-  return doQuery()
+function doSearch($event) {
+  const firstSearch = !$event ? 'firstSearch' : undefined
+  return doQuery({}, firstSearch)
 }
 // 跳转信息发布
 function goToPublish () {
@@ -44,6 +47,21 @@ function goToPublish () {
     window.open('/swordfish/page_web_pc/issued/info')
   }
 }
+
+function checkPower ($event) {
+  if(!isLogin.value) {
+    $bus.$emit('bidding:goLogin')
+  } else {
+    return doSearch($event)
+  }
+}
+// 未登录--多个关键词切换处理
+function onSelectMoreKey () {
+  if(!isLogin.value) {
+    inputKeywordsState.value.selectMoreKey = false
+    $bus.$emit('bidding:goLogin')
+  }
+}
 </script>
 
 <template>
@@ -51,11 +69,12 @@ function goToPublish () {
     <search-header-card
       v-model="inputKeywordsState.input"
       :tabs="searchTabs"
-      placeholder="招标信息搜索"
+      placeholder="请输入项目名称等关键词,例如:医疗设备"
       @change-tab="onChangeTab"
       @search="doSearch"
-      :show-wx-qr="!cooperateCode"
+      :show-wx-qr="!cooperateCode && !inBIPropertyIframe && !isBidField"
       :show-workspace-button="isLogin && !isInApp"
+      :showTab="!inBIPropertyIframe"
       @goWorkSpace="goWorkSpace"
     >
       <div class="flex flex-(row items-center)">
@@ -66,6 +85,7 @@ function goToPublish () {
             placeholder="精准搜索"
             :options="searchModelOptions"
             @change="doSearch"
+            :beforeChange="checkPower"
           ></common-single-choice>
 
           <el-tooltip
@@ -93,11 +113,11 @@ function goToPublish () {
         <el-checkbox
           class="m-l-16px"
           v-model="inputKeywordsState.selectMoreKey"
-          @change="doSearch"
+          @input="onSelectMoreKey"
         >
           多个关键词
         </el-checkbox>
-        <div class="m-l-24px" v-if="!inResourceBIIframe && !inBIPropertyIframe">
+        <div class="m-l-24px" v-if="!inResourceBIIframe && !inBIPropertyIframe && !isBidField">
           <el-button class="use-badge" data-badge="限免" type="primary" @click='goToPublish'>
             {{ isLogin ? '信息发布': '免费发布信息'}}
           </el-button>

+ 3 - 1
apps/bigmember_pc/src/views/search/bidding/components/search-filter-header.vue

@@ -5,6 +5,8 @@ import HistoryFilterDialog from '../components/history-filter-dialog.vue'
 import { SearchBidModel } from '../model/index'
 const {
   isLogin,
+  isInApp,
+  inBIPropertyIframe,
   disposeFilterActionModel,
   onSaveFilter,
   onResetFilter,
@@ -38,7 +40,7 @@ function closeHistoryFilterDialog () {
           <span>筛选条件</span>
           <i class="iconfont icon-xiala highlight-text" :class="{ 'is-reverse': showFilter}"></i>
         </div>
-        <div class="f-h-action" v-if="isLogin">
+        <div class="f-h-action" v-if="isLogin && isInApp && !inBIPropertyIframe">
           <span class="action-item reset-item" @click="onResetFilter">重置筛选</span>
           <span class="action-item has-item" @click="onHasFilter">已存筛选 {{ historyFilterCount || ''}}</span>
           <span class="action-item save-item" @click="onSaveFilter">保存筛选</span>

+ 14 - 8
apps/bigmember_pc/src/views/search/bidding/components/search-list-table.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { computed } from 'vue'
+import { ref, computed } from 'vue'
 import { SearchBidModel } from '../model'
 const { onClickDataExport } = SearchBidModel
 import {
@@ -35,7 +35,7 @@ const props = defineProps({
 })
 
 const  showTableMore = computed(() => {
-  return props.list.length > 20 && props.listState.pageNum === 1 && props.listState.total > 20
+  return props.list.length >= 20 && props.listState.total > 20
 })
 
 function calcTitle (item)  {
@@ -52,8 +52,13 @@ function calcTitle (item)  {
 }
 
 function calcMoney(budget) {
-  if (budget && budget !== '0') {
-    return moneyUnit(budget, 'fixUnit')
+  if(budget && isNaN(budget)) {
+    return budget
+  } else if (budget && !isNaN(budget) && budget !== '0') {
+    return ( budget / 10000).toFixed(2).replace(
+      '.00',
+      ''
+    )
   } else {
     return ''
   }
@@ -107,14 +112,15 @@ function toDetail (item) {
         </thead>
         <tbody>
           <tr
-            v-for="(item, index) in list.slice(0,20)"
+            v-for="(item, index) in list"
             :class="{ visited: item.visited }"
-            :key="index + '_' + item._id"
+            :key="index + '_' + item.id"
             @click="toDetail(item)"
+            v-visited:articleContent="item.id"
           >
             <td width="48">{{ index + 1 }}</td>
             <td width="315" class="tt-l" v-html="calcTitle(item, index)"></td>
-            <td width="84">{{ item.subtype }}</td>
+            <td width="84">{{ item.subtype ? item.subtype + '公告' : ''}}</td>
             <td width="73" class="tt-r">{{ calcMoney(item.budget) }}</td>
             <td width="181" class="tt-l">{{ item.buyer }}</td>
             <td width="103">
@@ -132,7 +138,7 @@ function toDetail (item) {
         </tbody>
       </table>
       <div class="shade_table" v-if="showTableMore">
-        <div class="more" data-need-bind-phone="" @click="onClickDataExport">
+        <div class="more" data-need-bind-phone="" @click="onClickDataExport('table')">
           查看更多&gt;
         </div>
       </div>

+ 14 - 12
apps/bigmember_pc/src/views/search/bidding/constant/search-filters-bi.js

@@ -39,22 +39,24 @@ function createBiSearchBidBaseSchema(propertyListData = {}) {
     {
       key: 'expireTime',
       label: '到期时间:',
-      defaultVal: '',
+      defaultVal: 'all',
       _name: 'expireTime',
       _type: 'component',
       expand: {
         component: SearchTimeScopeSelector,
         props: {
-          type: 'expire'
+          type: 'expire',
+          showConfirmButton: true,
+          exactCanHalf: true
         },
         hooks: {}
       }
     },
     {
-      key: 'subinformation',
+      key: 'subInformation',
       label: '业务类型:',
       defaultVal: [],
-      _name: 'subinformation',
+      _name: 'subInformation',
       _type: 'component',
       expand: {
         component: CommonCheckboxSelector,
@@ -138,7 +140,7 @@ function createBiSearchBidMoreSchema() {
       }
     },
     {
-      key: 'subtype',
+      key: 'subType',
       label: '信息类型',
       defaultVal: '',
       _name: 'subtype',
@@ -189,10 +191,10 @@ function createBiSearchBidMoreSchema() {
       }
     },
     {
-      key: 'changehand',
+      key: 'changeHand',
       label: '换手率',
       defaultVal: '',
-      _name: 'changehand',
+      _name: 'changeHand',
       _type: 'component',
       expand: {
         component: ChangeHandsDropdown,
@@ -211,10 +213,10 @@ function createBiSearchBidMoreSchema() {
       }
     },
     {
-      key: 'buyertel',
+      key: 'buyerTel',
       label: '采购单位联系方式',
       defaultVal: '',
-      _name: 'buyertel',
+      _name: 'buyerTel',
       _type: 'component',
       expand: {
         component: ContactSelector,
@@ -225,10 +227,10 @@ function createBiSearchBidMoreSchema() {
       }
     },
     {
-      key: 'winnertel',
+      key: 'winnerTel',
       label: '中标企业联系方式',
       defaultVal: '',
-      _name: 'winnertel',
+      _name: 'winnerTel',
       _type: 'component',
       expand: {
         component: ContactSelector,
@@ -239,7 +241,7 @@ function createBiSearchBidMoreSchema() {
       }
     },
     {
-      key: 'notkey',
+      key: 'notKey',
       label: '排除词',
       defaultVal: [],
       _name: 'notKeyComponent',

+ 122 - 7
apps/bigmember_pc/src/views/search/bidding/constant/search-filters.js

@@ -9,8 +9,10 @@ import IndustrySelector from '@/components/filter-items/IndustrySelector.vue'
 import KeywordTagsSelector from '@/components/filter-items/KeywordTagsSelector'
 import RegionSelector from '@/components/filter-items/RegionSelector'
 import SelectorWithBasePower from '@/components/filter-items/SelectorWithBasePower.vue'
+import OnecascadeContent from '@/components/filter-items/OnecascadeContent.vue'
 import { calcSearchScope } from '@/assets/js/selector/scope.js'
 import $bus from '@/utils/bus'
+import { findIndex } from 'lodash'
 
 function noPower() {
   $bus.$emit('search:filter:no-power')
@@ -54,10 +56,59 @@ const moreFiltersNeedVipKeyList = [
   'buyer',
   'winner',
   'agency',
-  'buyer',
-  'buyer',
 ]
 
+const BIInfoTypeOptions = [
+  {
+    name: '招标结果',
+    value: '招标结果',
+    level: 1,
+    children: [
+      {
+        name: '中标',
+        value: '中标',
+        level: 2
+      },
+      {
+        name: '成交',
+        value: '成交',
+        level: 2
+      },
+      {
+        name: '废标',
+        value: '废标',
+        level: 2
+      },
+      {
+        name: '流标',
+        value: '流标',
+        level: 2
+      }
+    ]
+  },
+  {
+    name: '招标信用信息',
+    value: '招标信用信息',
+    level: 1,
+    children: [
+      {
+        name: '合同',
+        value: '合同',
+        level: 2
+      },
+      {
+        name: '验收',
+        value: '验收',
+        level: 2
+      },
+      {
+        name: '违规',
+        value: '违规',
+        level: 2
+      }
+    ]
+  }
+]
 
 function createSearchBidBaseSchema(conf = {}) {
   const isLogin = conf.isLogin || false
@@ -65,6 +116,7 @@ function createSearchBidBaseSchema(conf = {}) {
   const oldUser = conf.oldUser || false
   const showVip = conf.showVip || false
   const infoType = conf.infoType
+  const inInjectBI = conf.inInjectBI || false
 
   // 发布时间
   const publishTimeExpandFree = {
@@ -74,12 +126,16 @@ function createSearchBidBaseSchema(conf = {}) {
       vipMaskShow: true,
       vipModuleShow: showVip,
       freeConf: {
+        exactCanHalf: true,
+        showConfirmButton: true,
         beforeChange ($event) {
           return beforeChangeHandle($event, 'publishTime', isLogin)
         },
         options: ['lately7', 'lately30', 'sinceLastYear'],
       },
       vipConf: {
+        exactCanHalf: true,
+        showConfirmButton: true,
         options: ['sinceLastThreeYear', 'sinceLastFiveYear', 'exact'],
       }
     },
@@ -89,7 +145,10 @@ function createSearchBidBaseSchema(conf = {}) {
   }
   const publishTimeExpandVip = {
     component: SearchTimeScopeSelector,
-    props: {},
+    props: {
+      exactCanHalf: true,
+      showConfirmButton: true,
+    },
     hooks: {}
   }
   // 搜索范围
@@ -104,16 +163,19 @@ function createSearchBidBaseSchema(conf = {}) {
       component: SearchScopeSelector,
       options: defaultScopeOptions,
       freeConf: {
+        isOld: oldUser && !vipUser,
         beforeChange($event) {
           return beforeChangeHandle($event, 'selectType', isLogin)
         },
         options: freeOptions,
+        keepOne: true
       },
       vipConf: {
         options: vipOptions,
         beforeChange() {
           noPower()
-        }
+        },
+        keepOne: true
       }
     },
     hooks: {
@@ -122,7 +184,10 @@ function createSearchBidBaseSchema(conf = {}) {
   }
   const searchScopeExpandVip = {
     component: SearchScopeSelector,
-    props: {},
+    props: {
+      isOld: oldUser && !vipUser,
+      keepOne: true
+    },
     hooks: {}
   }
 
@@ -152,6 +217,7 @@ function createSearchBidBaseSchema(conf = {}) {
       expand: {
         component: InfoTypeSelector,
         props: {
+          options: inInjectBI ? BIInfoTypeOptions : [],
           showLabel: false,
           selectorType: 'line',
           showDataType: infoType,
@@ -167,8 +233,8 @@ function createSearchBidBaseSchema(conf = {}) {
   return SearchBidBaseSchema
 }
 
-function createSearchBidMoreSchema() {
-  const SearchBidMoreSchema = [
+function createSearchBidMoreSchema(filterItems) {
+  let SearchBidMoreSchema = [
     {
       key: 'regionMap',
       label: '地区',
@@ -177,6 +243,9 @@ function createSearchBidMoreSchema() {
       _type: 'component',
       expand: {
         component: RegionSelector,
+        props: {
+          showCount: false
+        },
         hooks: {}
       }
     },
@@ -321,6 +390,52 @@ function createSearchBidMoreSchema() {
     }
   ]
 
+  // 移动端融创,动态添加筛选条件(支持多个筛选条件插入)
+  if(Array.isArray(filterItems)) {
+    const resultFilter = []
+    filterItems.forEach(fTemp => {
+      const { key, defaultVal, label, options, type } = fTemp
+      let obj  = {}
+      if(type === 'multiple') {
+       const formatOptions = options.map(o => {
+         return {
+           ...o,
+           label: o.label,
+           value: o.key,
+         }
+       })
+        obj = {
+          key: key,
+          label: label,
+          defaultVal: defaultVal,
+          _name: 'type',
+          _type: 'component',
+          expand: {
+          component: OnecascadeContent,
+            props: {
+            options: formatOptions,
+              placeholder: label
+            },
+            hooks: {}
+          }
+        }
+      }
+      resultFilter.push(obj)
+    })
+    const index = findIndex(SearchBidMoreSchema, function (o) {
+      return o.label === '采购单位类型'
+    })
+    SearchBidMoreSchema.splice(index, 0, ...resultFilter)
+  }  else if (typeof filterItems === 'object') {
+    const conf = filterItems
+    const { isBidField } = conf
+    if(isBidField) {
+     const newArr = SearchBidMoreSchema.filter(item => {
+        return item.key !== 'industry' && item.key !== 'buyerclass'
+      })
+      SearchBidMoreSchema = newArr
+    }
+  }
   SearchBidMoreSchema.forEach((schema) => {
     const key = schema.key
     if (moreFiltersNeedVipKeyList.includes(key)) {

+ 271 - 139
apps/bigmember_pc/src/views/search/bidding/index.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { computed, reactive, ref } from 'vue'
+import { ref } from 'vue'
 import SearchBidHeader from '@/views/search/bidding/components/search-bid-header.vue'
 import SearchBidFilter from '@/views/search/bidding/components/search-bid-filter.vue'
 import searchFilterHeader from '@/views/search/bidding/components/search-filter-header.vue'
@@ -12,6 +12,8 @@ import CustomDialog from '@/components/dialog/Dialog.vue'
 import ExportTip from '@/views/portrayal/components/DataExportTip.vue'
 import powerPerson from '@/components/subscribe-manager/powerPerson.vue'
 import BidrenewalDialog from '@/views/BidrenewalDialog/index.vue'
+import Empty from '@/components/common/Empty.vue'
+import recommendCard from '@/views/search/bidding/components/recommend-card.vue'
 // 导入业务模型
 import { useSearchBidModel, SearchBidModel } from './model/index'
 import { getMsgDistributor } from '@/api/modules/'
@@ -27,6 +29,8 @@ const {
   inResourceBIIframe,
   inInjectBI,
   isInBI,
+  isVip,
+  isFree,
   vt,
   filterState,
   inputKeywordsState,
@@ -41,6 +45,7 @@ const {
   doChangePageSize,
   toDetail,
   list,
+  tableList,
   tableFixedTop,
   tagToDetail,
   setExport,
@@ -57,7 +62,13 @@ const {
   onAddInfoOfBI,
   onSingleAddInfo,
   vipDialogConf,
-  closeVipDialog
+  closeVipDialog,
+  timeSelectorText,
+  collectElementRef,
+  onFreeTaste,
+  interceptKeywords,
+  toggleBlurModeTip,
+  doToggleSearchBlurMode
 } = SearchBidModel
 
 const {
@@ -86,13 +97,20 @@ const articleRef = ref({
 
 <template>
   <div class="search-bidding-page">
-    <div class="search-bidding-header-container b-rd-8px m-t-24px">
+    <div class="search-bidding-header-container b-rd-8px">
       <search-bid-header></search-bid-header>
     </div>
+    <div class="search-bidding-keywords-tip intercept" v-if="interceptKeywords.interceptOtherWords">
+      <img src="@/assets/images/icon/tip2.png" alt="">
+      “<span>{{interceptKeywords.interceptOtherWords}}</span>“及其后面的字词均被忽略,因为剑鱼标讯的查询限制在<span class="interceptLimit">{{interceptKeywords.interceptLimit}}</span>个汉字以内。
+    </div>
     <div class="search-bidding-filter-container b-rd-8px">
       <search-filter-header></search-filter-header>
       <search-bid-filter></search-bid-filter>
     </div>
+    <div >
+      <recommend-card></recommend-card>
+    </div>
     <div class='search-bidding-list-container'>
       <search-list
         class="b-rd-8px"
@@ -100,8 +118,6 @@ const articleRef = ref({
         @doAction="doListHeaderAction"
         @doChangeAllSelect="doChangeAllSelect"
         @doChangeSelect="doChangeSelect"
-        @current-change="doChangePageNum"
-        @size-change="doChangePageSize"
         :show-pagination="activeItemStyleType !== 'T'"
         :is-table="activeItemStyleType === 'T'"
         :table-fixed-top='tableFixedTop'
@@ -118,7 +134,7 @@ const articleRef = ref({
         </template>
         <template v-slot:table='{ list }' v-if="activeItemStyleType === 'T'">
           <search-list-table
-            :list='list'
+            :list="tableList"
             :list-state='listState'
             :match-keys="inputKeywordsState.matchKeys"
             @to-detail='toDetail'
@@ -134,13 +150,17 @@ const articleRef = ref({
               :match-keys="inputKeywordsState.matchKeys"
               :article="item"
               :index="index"
-              :tag-can-click='true'
+              :tag-can-click="true"
               @onClick="toDetail(item)"
-              @tag-click='tagToDetail(item, $event)'
-              @onCollect='onClickSingleCollect'
-              @onJoinBid='onJoinBid'
+              @tag-click="tagToDetail(item, $event)"
+              @onCollect="onClickSingleCollect"
+              @onJoinBid="onJoinBid"
               :config="articleRef"
+              v-visited:articleContent="item.id"
             >
+              <template #bi-slot=" { item }">
+
+              </template>
               <template #right-handle-container>
                 <div
                   v-if="inBIPropertyIframe || inResourceBIIframe"
@@ -164,17 +184,59 @@ const articleRef = ref({
             </article-item>
           </div>
         </template>
-
-        <!--      <template-->
-        <!--        #list-after-->
-        <!--        v-if="listState.pageNum === 1 && listState.total > 500"-->
-        <!--      >-->
-        <!--        <div class="p-16px text-right">-->
-        <!--          为您展示前500条,-->
-        <!--          <span class="highlight-text">点击免费查看更多信息</span>-->
-        <!--        </div>-->
-        <!--      </template>-->
+        <template #empty>
+          <empty :mtb60="false"  images="jy-back.png">
+            <div class="hasNoData_tiptext">
+              <p>对不起,没有找到 <span class="highlight-text">{{ timeSelectorText }}</span> 相关匹配的信息,</p>
+              <p>修改时间范围或换个搜索词试试吧</p>
+            </div>
+          </empty>
+        </template>
+        <template #list-after v-if="listState.finished && isInApp && activeItemStyleType !== 'T' && isLogin">
+          <div class="p-16px text-right over-run-tips" v-if="isFree && listState.total >= 500">
+            为您展示前500条,
+            <span class="highlight-text" @click="onFreeTaste">点击免费查看更多信息</span>
+          </div>
+          <div class="text-right over-run-tips" v-if="isVip && listState.total >= 5000">为您展示前5000条,可细化筛选条件查看更多信息</div>
+        </template>
+        <template #pagination>
+          <el-pagination
+            background
+            popper-class="pagination-custom-select"
+            layout="prev, pager, next, sizes, jumper"
+            :current-page="listState.pageNum"
+            :page-size="listState.pageSize"
+            :page-sizes="[5, 10, 50, 100]"
+            :total="listState.pageTotal"
+            :show-confirm-btn="true"
+            @current-change="doChangePageNum($event)"
+            @size-change="doChangePageSize($event)"
+          >
+          </el-pagination>
+        </template>
       </search-list>
+      <!-- 手动切换筛选模式提示 -->
+      <div class="tip-toggle-search-mode-container"  v-show="toggleBlurModeTip.show">
+        <div>
+          如需查看更多相关信息,建议您将搜索模式切换为
+          <div class="tip-action highlight-text">
+            “模糊搜索”
+            <el-tooltip popper-class="tooltip-help-class" effect="dark" placement="bottom">
+              <i class="iconfont icon-help" style="font-size:18px;"></i>
+              <template slot="content">
+                <div class="tooltip-slot-content w-360px">
+                  精准搜索: 搜索结果必须完全包含完整的关键词。如搜索"医疗设备" ,搜索结果一定完整包含“医疗设备”才能被搜索到,而“医疗的设备”或“设备医疗”的项目不会被搜索到。
+                  <br>
+                  <br>
+                  模糊搜索: 系统会先自动智能分词然后再进行搜索。如搜索"医疗设备" ,系统会自动分成“医疗”“设备”然后进行搜索,只要项目中出现“医疗”和“设备”都会被搜索到,前提是两个词必须一同出现在一则公告内,不分先后顺序。
+                </div>
+              </template>
+            </el-tooltip>
+          </div>
+          ,按照当前条件共匹配到{{ toggleBlurModeTip.count }}条公告。
+        </div>
+        <button @click="doToggleSearchBlurMode">立即切换查看</button>
+      </div>
 
       <div class="sub-collection tags-box" style="display: none">
         <div class="tags-inputs">
@@ -259,19 +321,19 @@ const articleRef = ref({
 
 <style lang='scss'>
 .open-vip-dialog {
-  width: 380px;
+  width: 380px !important;
 
   .el-dialog__header {
-    padding: 32px 32px 20px;
+    padding: 32px 32px 20px !important;
   }
 
   .el-dialog__body {
-    padding: 0 32px 0;
-    text-align: center;
+    padding: 0 32px 0 !important;
+    text-align: center !important;
   }
 
   .el-dialog__footer {
-    padding: 32px;
+    padding: 32px !important;
   }
 
   .el-button {
@@ -280,6 +342,21 @@ const articleRef = ref({
   }
 }
 .search-bidding-page .sub-collection.tags-box {
+  display: flex;
+  flex-direction: column;
+  min-height: 340px;
+  max-height: 360px;
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 332px;
+  padding: 20px 16px;
+  background: #ffffff;
+  border: 1px solid #ececec;
+  box-sizing: border-box;
+  border-radius: 8px;
+  box-shadow: 0px 0px 28px 0px rgba(0, 0, 0, 0.08);
+  z-index: 99;
   .tags-list .tags-item {
     float: left;
     min-width: 44px;
@@ -354,16 +431,128 @@ const articleRef = ref({
     background-repeat: no-repeat;
     background-size: contain;
   }
+  .tags-inputs {
+    position: relative;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    .tag-input {
+      width: 100%;
+      padding: 0;
+      min-height: 34px;
+      max-height: 74px;
+      overflow-y: scroll;
+      display: inline-block;
+      border: 1px solid #ccc;
+      border-radius: 4px;
+      background-color: #fff;
+      cursor: text;
+      text-align: left;
+
+      .tag-labels {
+        display: inline;
+        vertical-align: middle;
+
+        .tag-label {
+          display: inline-block;
+          padding: 5px 12px;
+          font-size: 14px;
+          line-height: 1.2;
+          margin: 5px;
+          cursor: pointer;
+          border: 1px solid #ececec;
+          box-sizing: border-box;
+          border-radius: 4px;
+          background: #f5f6f7;
+          color: #1d1d1d;
+        }
+      }
+
+      .clear-input {
+        display: inline-block;
+        padding: 0 10px;
+        width: 160px;
+        height: 36px;
+        line-height: 1;
+        background: #fff;
+        border-radius: 2px;
+        vertical-align: middle;
+        border: none;
+        background-color: transparent;
+        box-shadow: none;
+        box-sizing: border-box;
+        font-size: 14px;
+        color: #1d1d1d;
+      }
+      }
+  }
+
+    .tags-list {
+      margin-top: 12px;
+      overflow-y: auto;
+      flex: 1;
+    }
+
+
+    .add-tag-button {
+      margin-left: 16px;
+      color: #2cb7ca;
+      font-size: 14px;
+      line-height: 22px;
+      white-space: nowrap;
+      cursor: pointer;
+    }
+
+    .tags-footer {
+      margin-top: 20px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .tags-button {
+      padding: 3px 17px;
+      color: #1d1d1d;
+      font-size: 14px;
+      line-height: 22px;
+      border-radius: 4px;
+      border: 1px solid #e0e0e0;
+      text-shadow: 0px 0px 28px 0px rgba(0, 0, 0, 0.08);
+      cursor: pointer;
+    }
+
+    .button-confirm {
+      margin-right: 16px;
+      color: #fff;
+      background: #2cb7ca;
+      border-color: #2cb7ca;
+    }
+
+    .tag-placeholder {
+      position: absolute;
+      top: 12px;
+      left: 16px;
+      color: #bbb;
+      font-size: 14px;
+    }
 }
 </style>
 
 <style lang="scss" scoped>
 .in-app {
   .search-bidding-page{
+    margin-top:0;
     width: 100%;
     padding:24px;
   }
 }
+.in-web{
+  .search-bidding-page{
+    margin-top: 24px;
+  }
+}
 .search-bidding-page {
   width: 1200px;
   margin: 0 auto;
@@ -426,130 +615,73 @@ const articleRef = ref({
     box-sizing: content-box;
     min-width: 42px;
   }
-}
-
-.sub-collection.tags-box {
-  display: flex;
-  flex-direction: column;
-  min-height: 340px;
-  max-height: 360px;
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 332px;
-  padding: 20px 16px;
-  background: #ffffff;
-  border: 1px solid #ececec;
-  box-sizing: border-box;
-  border-radius: 8px;
-  box-shadow: 0px 0px 28px 0px rgba(0, 0, 0, 0.08);
-  z-index: 99;
-
-  .tags-inputs {
-    position: relative;
-    width: 100%;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-
-    .tag-input {
-      width: 100%;
-      padding: 0;
-      min-height: 34px;
-      max-height: 74px;
-      overflow-y: scroll;
-      display: inline-block;
-      border: 1px solid #ccc;
-      border-radius: 4px;
-      background-color: #fff;
-      cursor: text;
-      text-align: left;
-
-      .tag-labels {
-        display: inline;
-        vertical-align: middle;
-
-        .tag-label {
-          display: inline-block;
-          padding: 5px 12px;
-          font-size: 14px;
-          line-height: 1.2;
-          margin: 5px;
-          cursor: pointer;
-          border: 1px solid #ececec;
-          box-sizing: border-box;
-          border-radius: 4px;
-          background: #f5f6f7;
-          color: #1d1d1d;
-        }
-      }
-
-      .clear-input {
-        display: inline-block;
-        padding: 0 10px;
-        width: 160px;
-        height: 36px;
-        line-height: 1;
-        background: #fff;
-        border-radius: 2px;
-        vertical-align: middle;
-        border: none;
-        background-color: transparent;
-        box-shadow: none;
-        box-sizing: border-box;
-        font-size: 14px;
-        color: #1d1d1d;
+  ::v-deep{
+    .a-i-detail{
+      padding-left: 0;
+    }
+    .keyword-tags-container{
+      margin-top: -6px;
+      .el-tag,
+      .add-keyword-actions,
+      .keyword-radio-group
+      {
+        margin-top: 6px;
       }
     }
+    .filter-layout{
+      position: unset!important;
+    }
+    .hasNoData_tiptext{
+      font-size: 14px;
+      font-family: Microsoft YaHei, Microsoft YaHei-Regular;
+      text-align: center;
+      color: #999999;
+      line-height: 22px;
+    }
   }
-
-  .tags-list {
-    margin-top: 12px;
-    overflow-y: auto;
-    flex: 1;
-  }
-
-
-  .add-tag-button {
-    margin-left: 16px;
-    color: #2cb7ca;
+  .over-run-tips {
+    color: #686868;
+    text-align: right;
     font-size: 14px;
     line-height: 22px;
-    white-space: nowrap;
+    padding: 0 20px 16px 0;
     cursor: pointer;
   }
-
-  .tags-footer {
-    margin-top: 20px;
-    display: flex;
+}
+.search-bidding-keywords-tip {
+  margin-top: 25px;
+  font-size: 14px;
+  margin-bottom: -10px;
+  img {
+    vertical-align: middle;
+    width: 15px;
+    margin-right: 5px;
+    position: relative;
+    top: -2px;
+  }
+}
+.tip-toggle-search-mode-container {
+  border-top: 1px solid #0000000D;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding: 16px 56px;
+  border-bottom-left-radius: 8px;
+  border-bottom-right-radius: 8px;
+  color: #5f5e64;
+  font-size: 13px;
+  line-height: 20px;
+  background: linear-gradient(90deg, #EDFDFF 0.51%, #FFFFFF 100%);
+  .tip-action {
+    display: inline-flex;
     align-items: center;
-    justify-content: center;
   }
-
-  .tags-button {
-    padding: 3px 17px;
-    color: #1d1d1d;
-    font-size: 14px;
-    line-height: 22px;
+  button {
+    margin-left: 12px;
+    background: #2abed1;
+    padding: 5px 12px;
     border-radius: 4px;
-    border: 1px solid #e0e0e0;
-    text-shadow: 0px 0px 28px 0px rgba(0, 0, 0, 0.08);
-    cursor: pointer;
-  }
-
-  .button-confirm {
-    margin-right: 16px;
-    color: #fff;
-    background: #2cb7ca;
-    border-color: #2cb7ca;
-  }
-
-  .tag-placeholder {
-    position: absolute;
-    top: 12px;
-    left: 16px;
-    color: #bbb;
-    font-size: 14px;
+    color: #f7f9fa;
   }
 }
 </style>

+ 550 - 146
apps/bigmember_pc/src/views/search/bidding/model/base.js

@@ -1,10 +1,15 @@
-import { computed, reactive, ref, toRefs, onMounted, getCurrentInstance } from 'vue'
+import { computed, reactive, ref, toRefs, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
 import { without, throttle } from 'lodash'
 import { useStore } from '@/store'
 import { useRoute, useRouter } from 'vue-router/composables'
 import { MessageBox } from 'element-ui'
-import { mixinVisited } from '@/utils/mixins/visited-setup.js'
-import  { FilterHistoryAjaxModelRestore, getParam, openLinkInWorkspace, InContainer} from '@/utils'
+import  {
+  FilterHistoryAjaxModelRestore,
+  FilterHistoryAjaxModel2ViewModel,
+  getParam,
+  openLinkInWorkspace,
+  InContainer
+} from '@/utils'
 import $bus from '@/utils/bus'
 // 筛选条件动态组件方法
 import { getCreateSearchSchema } from '@/views/search/bidding/constant/index'
@@ -29,7 +34,10 @@ import { joinBidActionsModel } from './modules/join-bid-actions'
 import { dataEmployActionsModel } from './modules/data-employ-actions'
 // 添加业务
 import { dataAddActionsModel } from './modules/data-add-actions'
-
+// 进行搜索前业务判断
+import { beforeSearchModel } from './modules/before-search'
+// 潜在客户引流--市场分析报告&&超前项目推荐数据请求
+import { recommendCardModel } from './modules/recommend-card'
 
 export default function () {
   const that = getCurrentInstance().proxy
@@ -68,6 +76,7 @@ export default function () {
 
 
   // 是否在工作台内
+  // 本地调试,可改为工作台内isInApp = ref(true),  isInWeb = ref(false) 提交记得改回!
   const isInApp = ref(InContainer.inApp)
   const isInWeb = ref(InContainer.inWeb)
 
@@ -95,16 +104,33 @@ export default function () {
     return inResourceBIIframe || inResourceBIIframe || inInjectBI
   })
 
+   // 是否是领域化页面(医械通)
+  const isBidField =  location.href.includes('/jylab/medical/index')
+
+  // 缓存存储配置
+  const storageConfig = {
+    listTab: {
+      key: 'pc_search_bidding_listTabActive',
+      _storage: localStorage
+    },
+    // 筛选条件上次搜索筛选项缓存key
+    filter: {
+      key: 'pc_search_bidding_lastFilters',
+      _storage: localStorage
+    },
+    // 页面tab(筛选项的searchGroup)
+    searchGroup: {
+      key: 'pc_search_bidding_lastSearchGroup',
+      _storage: localStorage
+    }
+  }
   // 解构基础业务
   const APIModel = useQuickSearchModel({
     type: 'search-bid'
   })
-  const {
-    pathVisiting,
-    createPathItem,
-    pathVisited
-  } = mixinVisited()
-
+  /**
+   * 列表相关model
+   */
   const {
     list,
     total,
@@ -121,16 +147,34 @@ export default function () {
     doClearAllSelection,
     doQuery: doRunQuery
   } = APIModel
-
+  /**
+   * 关键词搜索相关
+   */
   const {
     inputKeywordsState,
     searchModelOptions,
-    getFormatAPIParams: getFormatOfInputKeywords
+    getFormatAPIParams: getFormatOfInputKeywords,
+    updateInputKeywordsState,
   } = useSearchInputKeywordsModel()
+  /**
+   * 筛选v-model数据
+   */
   const {
     filterState,
+    filterBase,
     getFormatAPIParams: getFormatOfFilter,
-  } = useSearchFilterModel({ inBIPropertyIframe })
+    updateFilterBase
+  } = useSearchFilterModel({ inBIPropertyIframe, isFree, isInApp, isBidField })
+  /**
+   * 搜索前校验
+   */
+  const {
+    onTheWhiteList,
+    checkAndClearTextIncludesCommonWords
+  } = beforeSearchModel({ inputKeywordsState })
+  /**
+   * 列表头操作
+   */
   const {
     limitActions,
     headerActions,
@@ -140,9 +184,14 @@ export default function () {
     disabledHeaderActions
   } = useSearchListHeaderActionsModel()
 
-  // 页面tab切换Model
+  $bus.$on('bidding:updateInputKeywords', function (obj) {
+    updateInputKeywordsState(obj)
+  })
 
-  const SearchTabsModel = useSearchTabsModel({ showTabs2: !isInApp.value })
+  /**
+   * 页面tab切换Model
+   */
+  const SearchTabsModel = useSearchTabsModel({ showTabs2: !isInApp.value, inInjectBI })
   const { activeTab, doChangeTab } = SearchTabsModel
   // tab切换处理
   function onChangeTab (item) {
@@ -151,14 +200,17 @@ export default function () {
     })
     doChangeTab(item)
     if(isInApp.value) {
-      doQuery()
+      doQuery({}, 'tab')
     } else {
       if(item.link) {
-        location.replace(item.link)
+        window.location.replace(item.link)
       }
     }
   }
-  // 筛选条件动态配置处理
+
+  /**
+   * 筛选条件动态配置处理
+   */
   const {
     createSearchBidBaseSchema,
     createSearchBidMoreSchema
@@ -189,7 +241,9 @@ export default function () {
         vipUser: isVip.value && isInApp.value,
         oldUser: isOld.value && isInApp.value,
         showVip: isLogin.value && isInApp.value,
-        infoType: infoTypeDataType.value
+        infoType: infoTypeDataType.value,
+        isBidField: isBidField,
+        inInjectBI: inInjectBI
       }
     })
 
@@ -201,7 +255,7 @@ export default function () {
     const searchBidMoreVipSchema = ref([])
 
     SearchBidBaseSchema.value = createSearchBidBaseSchema(conf.value)
-    SearchBidMoreSchema.value =  createSearchBidMoreSchema()
+    SearchBidMoreSchema.value =  createSearchBidMoreSchema(conf.value)
 
     searchBidMoreFreeSchema.value = SearchBidMoreSchema.value?.filter(
       (s) => !s.vipMark
@@ -211,9 +265,13 @@ export default function () {
     )
 
 
-    function doUpdateData(schema) {
+    function doUpdateData(schema, type) {
       const payload = schema || conf.value
-      SearchBidBaseSchema.value = createSearchBidBaseSchema(payload)
+      if(type === 'more') {
+        SearchBidMoreSchema.value = createSearchBidMoreSchema(payload)
+      } else {
+        SearchBidBaseSchema.value = createSearchBidBaseSchema(payload)
+      }
     }
 
     return {
@@ -231,9 +289,9 @@ export default function () {
     loading,
     pageNum: 1,
     pageSize: 50,
-    total
+    total,
+    pageTotal: 0
   })
-
   // 当前展示的列表
   const activeList = computed(() => {
     return list.value.map((v) => {
@@ -242,6 +300,14 @@ export default function () {
       return v
     })
   })
+  // 表格展示的数据
+  const tableList = ref([])
+
+  // 搜索关键词限制
+  const interceptKeywords = reactive({
+    interceptOtherWords: '',
+    interceptLimit: 0
+  })
 
   // 根据权限处理列表头部的操作按钮展示
   const limitFilterHeaderActions =  computed(() => {
@@ -266,7 +332,7 @@ export default function () {
       limitActions.value['employ-bid'] = true
     }
     // 展示分发
-    if(isEntAdmin.value || isDepartmentAdmin.value) {
+    if((isEntAdmin.value || isDepartmentAdmin.value) && !inBIPropertyIframe && !inResourceBIIframe) {
       limitActions.value['distribute-bid'] = true
     }
     return headerActions.value
@@ -300,8 +366,31 @@ export default function () {
       ...styleTypes
     )
     activeHeaderActions.value.push(type)
+    // 记录列表风格切换--存到缓存
+    if(type !== 'table') {
+      const { _storage, key } = storageConfig['listTab']
+      _storage.setItem(key, type)
+    }
   }
 
+  /**
+   * 恢复以前选过的tab
+   */
+  function restoreListTabActive() {
+    const { _storage, key } = storageConfig['listTab']
+    const storage = _storage.getItem(key)
+    if (storage) {
+      if(storage === 'detailed-list' && isVip.value && isLogin.value) {
+        doChangeItemStyleType('detailed-list')
+      } else {
+        doChangeItemStyleType('refined-list')
+      }
+    } else {
+      _storage.setItem(key, 'refined-list')
+    }
+  }
+
+
   /**
    * 列表顶部按钮操作事件统一入口
    * @param item - 按钮原型
@@ -360,19 +449,19 @@ export default function () {
   // 分页事件
   function doChangePageNum(page) {
     listState.pageNum = page
-    doQuery()
+    doQuery({}, 'pageNumChange', page)
   }
 
   // 分页大小事件
   function doChangePageSize(size) {
     listState.pageSize = size
     listState.pageNum = 1
-    doQuery()
+    doQuery({}, 'pageNumChange')
   }
   // 登录
   $bus.$on('bidding:goLogin', goLogin)
   function goLogin () {
-    that.$checkLogin()
+    that.$showLoginDialog()
   }
   // 跳转详情页
   function toDetail(item) {
@@ -399,7 +488,6 @@ export default function () {
       )
     } catch (error) {}
     if(isLogin.value) {
-      checkVisited(item)
       const prefix = isInApp.value ? '/article/content/' : '/nologin/content/'
       const targetLink = `${prefix}${id}${aHref}`
 
@@ -409,7 +497,6 @@ export default function () {
         newTab: true,
       })
     } else{//没有登录跳转新的详情
-      checkVisited(item, 'nologin')
       // 一切都好渠道合作页,未登录跳转需要弹出登录,重置到已登录后的详情页
       if(cooperateCode.value) {
         window.location.href = '/notin/page'
@@ -422,51 +509,56 @@ export default function () {
   }
   // 列表tag跳转处理
   function tagToDetail (item, label) {
+    let link = ''
     if(label === 'subtype') {
-      let val = item['stypeadd'] || item['toptype'] || 'ZBXYXX_HT'
-      if(item.subtype === '中标') {
-        val = 'ZHB'
-      } else if(item.subtype === '招标') {
-        val = 'ZB'
-      }
-      window.open(`/list/stype/${val}.html`)
-      return
+      link = item['subtypeUrl']
     } else if(label === 'area'){
-      const val = item['areaadd'] || 'HN'
-      window.open(`/list/area/${val}.html`)
-      return
+      link = item['areaUrl']
     }
-  }
-  // 检测是否已经存储已读
-  function checkVisited (item, val) {
-    const id = item.id
-    let visited = false
-    if (val) {
-      visited = pathVisited(
-        createPathItem('/nologin/content/*.html', 'id=' + id)
-      )
-    } else {
-      visited = pathVisited(
-        createPathItem('/article/content/*.html', 'id=' + id)
-      )
+    if(link) {
+      window.open(link)
     }
-
-    item.visited = visited
   }
-
   // 获取 store getters
   const userType = computed(() => {
     return useStore().getters['user/userType']
   })
 
+  /**
+   * 变更搜索模式
+   * @type {boolean}
+   */
+  // P260需求精准搜索无结果时 自动切换到模糊搜索(查询) 模糊搜索也无结果时再切回精准(不查询)
+  // 条件:1.精准搜索模式 2.返回无数据 3.有主关键词或附加词
+  let autoSwitchModel = false
+  function changeSearchMode () {
+    const total = listState.total
+    const { searchMode, additionalWords,  input } = inputKeywordsState.value
+    const jzModel = searchMode === '0' && (additionalWords || input)
+    const mhModel = searchMode === '1' && (additionalWords || input)
+    if(!autoSwitchModel && jzModel && total === 0) {
+      autoSwitchModel = true
+      inputKeywordsState.value.searchMode = '1'
+      doQuery()
+    }
+    if(mhModel && autoSwitchModel) {
+      if( total > 0) {
+        that.$toast('精准搜索无结果,已为您自动切换到模糊搜索')
+        autoSwitchModel = false
+      } else {
+        // 模糊搜索无结果时 再切回精准
+        inputKeywordsState.value.searchMode = '0'
+        autoSwitchModel = false
+      }
+    }
+  }
+
   /**
    * 格式化请求参数
    * @param [params] - 可选值,部分情况会提供,默认会和该函数返回值进行合并
    */
 
   function getParams(params = {}) {
-    // 物业专版筛选条件待处理
-    // filterProperty
     // 合并所有模型的搜索筛选项
     const result = Object.assign(
       {
@@ -485,62 +577,144 @@ export default function () {
     )
     return result
   }
+
+  function beforeSearch (searchType) {
+    // 第一次搜索或者tab切换不校验白名单规则
+    if (searchType) {
+      return true
+    }
+    // 如果在反爬白名单,则空搜索刷新搜索结果(即允许空搜索)
+    // 不在,则不允许空搜索(此处空搜索指的是主搜索框是否为空)
+    if (!onTheWhiteList.value) {
+      const searchKeywords = inputKeywordsState.value.input
+      const hasOneKey = (filterState.value.buyer?.length || filterState.value.winner?.length ||  filterState.value.agency?.length) > 0
+      // 切换三种筛选类型时候判断(切换tab时不弹窗)
+      // if (!searchKeywords && !hasOneKey) {
+      //   if (from.indexOf('tab-') !== -1) {
+      //     return false
+      //   }
+      // }
+      // 关键词去两边空格后为空
+      if (!searchKeywords && inputKeywordsState.value.additionalWords.length === 0) {
+        if (!hasOneKey) {
+          that.$toast('请先输入关键词')
+          return false
+        }
+      } else {
+        // 判断关键词中是否有通用词,并清空对应通用词的项
+        const hasCommonWords = checkAndClearTextIncludesCommonWords(searchKeywords)
+        if (hasCommonWords) {
+          that.$toast('请输入项目名称等关键词')
+          return false
+        }
+      }
+    }
+    return true
+  }
   /**
    * 统一查询入口
    * - 拦截 doQuery 进行一些返回值处理
    * @param [params] - 可选值,默认会和 getParams(params) 返回值进行合并
    */
-  function doQuery(params = {}) {
-    return doRunQuery(getParams(params)).then((res) => {
+  function doQuery(params = {}, searchType, pageNum) {
+    const bSearch = beforeSearch(searchType)
+    if(!bSearch) {
+      return
+    }
+    if(!pageNum) {
+      listState.pageNum = 1
+    }
+    // 如果列表展示状态为table,将列表展现形式换成详细列表或者精简列表
+    if(activeItemStyleType.value === 'T') {
+      restoreListTabActive()
+    }
+    // 存储筛选条件
+    if(isInApp.value && isLogin.value && listState.pageNum === 1 && !inBIPropertyIframe) {
+      saveSearchGroupToLocal()
 
-      let matchKeys = []
       if(inputKeywordsState.value.input) {
-        matchKeys.push(inputKeywordsState.value.input)
-      }
-      if(inputKeywordsState.value.selectMoreKey) {
-        if(inputKeywordsState.value.additionalWords?.length) {
-          matchKeys = matchKeys.concat(inputKeywordsState.value.additionalWords)
-        }
+        saveFilterToLocal()
       }
+    }
 
-      // 用于搜索关键词高亮
-      inputKeywordsState.value.matchKeys = res.origin?.heightWords?.split(' ') || matchKeys
+    //P271需求--潜客圈进引流需求
+    if(!advancedInfo.show && !getShowChart.value && inputKeywordsState.value.input) {
+      getCustomReportData({ keywords:  inputKeywordsState.value.input })
+    }
 
-      // 列表无数据,禁用数据导出按钮
-      if(listState.total === 0) {
-        disabledHeaderActions.value = ['data-export']
-      } else {
-        disabledHeaderActions.value = []
+    // Ajax请求
+    return doRunQuery(getParams(params)).then((res) => {
+      afterQueryAjax(res)
+    })
+  }
+  // 处理查询请求后的数据以及其他业务
+  function afterQueryAjax (res) {
+    // 变更搜索模式
+    changeSearchMode()
+
+    const { origin } = res
+
+    listState.pageTotal = origin?.count || 0
+    // 限制关键词
+    interceptKeywords.interceptOtherWords = origin?.interceptOtherWords
+    interceptKeywords.interceptLimit = origin?.interceptLimit
+
+    let matchKeys = []
+    if(inputKeywordsState.value.input) {
+      matchKeys.push(inputKeywordsState.value.input)
+    }
+    if(inputKeywordsState.value.selectMoreKey) {
+      if(inputKeywordsState.value.additionalWords?.length) {
+        matchKeys = matchKeys.concat(inputKeywordsState.value.additionalWords)
       }
+    }
+    // 用于搜索关键词高亮
+    inputKeywordsState.value.matchKeys = res.origin?.keyWords?.split(' ') || matchKeys
 
-      if(isLogin.value) {
-        // 获取参标的数据
-        getJoinBidInfo(listIds.value)
-        // BI 是否批量收录,获取收录数据
-        if(inBIPropertyIframe || inResourceBIIframe) {
-          getEmployData(listIds.value)
-        }
-        // 个人报告嵌套BI页面
-        if(inInjectBI) {
-          getBidAddInfos()
-        }
+    //表格列表数据--只取第一页的前20条展示
+    if(listState.pageNum === 1) {
+
+    }
+    if(listState.pageNum === 1 && listState.total > 0) {
+      tableList.value = list.value.slice(0, 20)
+    }
 
+    // 列表无数据,禁用数据导出按钮
+    if(listState.total === 0) {
+      disabledHeaderActions.value = ['data-export']
+    } else {
+      disabledHeaderActions.value = []
+    }
+
+    if(isLogin.value) {
+      // 获取参标的数据
+      getJoinBidInfo(listIds.value)
+      // BI 是否批量收录,获取收录数据
+      if(inBIPropertyIframe || inResourceBIIframe) {
+        getEmployData(listIds.value)
+      }
+      // 个人报告嵌套BI页面
+      if(inInjectBI) {
+        getBidAddInfos()
       }
-      list.value = list.value.map(item => {
-        // 添加是否已读标志
-        const visited = pathVisited(
-          createPathItem('/article/content/*.html', `id=${item.id}`)
-        )
-        that.$set(item, 'visited', visited)
-        // 收藏字段显示
-        that.$set(item, 'collection', item.isCollected ? 1 : 0)
 
-        // 收录字段
-        that.$set(item, 'isEmploy', false)
+    }
+    list.value = list.value.map(item => {
+      // 收藏字段显示
+      that.$set(item, 'collection', item.isCollected ? 1 : 0)
 
-        return item
-      })
+      // 收录字段
+      that.$set(item, 'isEmploy', false)
 
+      return item
+    })
+
+    // 列表底部-是否展示需要切换模糊搜索的tip
+    checkToggleSearchMode({
+      pageNum: 1,
+      count: origin.total || 0,
+      blurCount: origin.bCount || 0,
+      searchMode: Number(inputKeywordsState.value.searchMode)
     })
   }
 
@@ -551,7 +725,7 @@ export default function () {
     showFilter.value = !showFilter.value
   }
 
-  // 组合好的筛选条件
+  // 组合好的组件格式的筛选条件
   function packageFilter () {
     const originParams = Object.assign(
       {
@@ -564,6 +738,14 @@ export default function () {
     return originParams
   }
 
+// P271需求,潜客圈进,展示超前项目or分析报告引流
+  const recommendCardCircleModel = recommendCardModel({ that })
+  const {
+    getShowChart,
+    advancedInfo,
+    getCustomReportData,
+  } = recommendCardCircleModel
+
   /******开通超级订阅弹窗start**********/
 
   // 开通超级订阅弹窗配置
@@ -610,9 +792,9 @@ export default function () {
     // 组件筛选条件
     const originParams = packageFilter()
     sessionStorage.setItem('search:bidding:filter', JSON.stringify(originParams))
-    window.location.replace(location.origin + '/swordfish/page_big_pc/search/bidding?goback=true')
+    // window.location.replace('/swordfish/page_big_pc/search/bidding?goback=true')
 
-    // window.location.replace(`/page_workDesktop/work-bench/page?link=${encodeURIComponent(goHref_ + 'goback=')}`)
+    window.location.replace(`/page_workDesktop/work-bench/page?link=${encodeURIComponent(goHref_ + '?goback=true')}`)
   }
   // 监听路由事件
   onMounted(() => {
@@ -625,9 +807,7 @@ export default function () {
       doChangeTab({ key})
     } else if(subtype && subtype ==='采购意向' && inBIPropertyIframe) {
       // 物业专版,采购意向回显
-      filterState.value = Object.assign(filterState.value, {
-        subtype: {采购意向: ['采购意向']}
-      })
+      filterState.value.subType = {采购意向: ['采购意向']}
     }
   })
   // 回显筛选条件
@@ -650,10 +830,6 @@ export default function () {
     checkFilterPass, // 检测是否可以保存筛选条件
   } = disposeFilterActionModel
 
-  if(isLogin.value) {
-    getFilterHistoryList()
-  }
-
   /**
    * 保存筛选条件
    */
@@ -686,7 +862,25 @@ export default function () {
           })
         })
         .catch(() => {
-          restoreFilter(item, 'saveBack')
+          // 之前是付费用户,现在变成普通用户了,需要重置一些vip选项!!!!
+          const params = Object.assign(item,{
+            bidField: isBidField ? 'medical' : '',
+            publishTime: 'thisyear',
+            selectType: ['title'],
+            subtype: item.subtype,
+            regionMap: {},
+            industry: item.industry,
+            fileExists: item.fileExists,
+            price: item.price,
+            buyerclass: {},
+            buyertel: '',
+            winnertel: '',
+            notkey: '',
+            buyer: '',
+            winner: '',
+            agency: ''
+          })
+          restoreFilter(params, 'saveBack')
           historyFilterDialogVisible.value = false
         })
     } else {
@@ -700,7 +894,9 @@ export default function () {
     } else {
       resultFilter = viewFilter
     }
+    doChangeTab({ key: resultFilter.searchGroup })
     filterState.value = {
+      bidField: isBidField ? 'medical' : '',
       publishTime: resultFilter.publishTime,
       selectType: resultFilter.selectType,
       subtype: resultFilter.subtype,
@@ -727,46 +923,16 @@ export default function () {
       additionalWords: resultFilter.additionalWords,
       selectMoreKey: resultFilter.additionalWords?.length > 0
     }
-    doChangeTab({ key: resultFilter.searchGroup })
     if(type === 'saveBack') {
       historyFilterDialogVisible.value = false
     }
-    doQuery()
+    doQuery({}, 'not-filter')
   }
 
   // 重置筛选条件
   function onResetFilter () {
-    filterState.value = {
-      // 发布时间
-      publishTime: 'thisyear',
-      // 搜索范围
-      selectType: ['content', 'title'],
-      // 信息类型
-      subtype: [],
-      // 地区
-      regionMap: {},
-      // 行业
-      industry: null,
-      // 附件
-      fileExists: '',
-      // 金额区间
-      price: null,
-      // 采购单位类型
-      buyerclass: {},
-      // 采购单位联系方式
-      buyertel: '',
-      // 中标企业联系方式
-      winnertel: '',
-      // 排除词
-      notkey: [],
-      // 采购单位
-      buyer: [],
-      // 中标企业
-      winner: [],
-      // 招标代理机构
-      agency: []
-    }
-    doQuery()
+    updateFilterBase()
+    doQuery({}, 'not-filter')
   }
  /**保存、重置、查看筛选条件end******/
 
@@ -802,11 +968,12 @@ export default function () {
     dataExport,
     setExport,
     exportDialogChange,
-    showDataExportDialog
+    showDataExportDialog,
+    toPayDataExport
   } = dataExportActionsModel ()
 
   // 数据导出操作
-  function onClickDataExport () {
+  function onClickDataExport (table) {
     if(!isLogin.value) {
       goLogin()
       return
@@ -819,7 +986,12 @@ export default function () {
       selectIds: selectIds.value,
       filter: originParams
     }
-    dataExport(params)
+    if(table) {
+      toPayDataExport(params)
+    } else {
+      dataExport(params)
+    }
+
   }
 
   /*******数据导出 end ***********/
@@ -982,6 +1154,226 @@ export default function () {
   }
   /*****BI添加操作end*********/
 
+  // 处理数据列表为空时,需要展示的提示文案包含时间
+  const timeSelectorText = computed(() => {
+    const publishTime = filterState.value.publishTime
+    const split = inBIPropertyIframe ? '-' : '_'
+    const { publishTimeText } = FilterHistoryAjaxModel2ViewModel.formatTime(publishTime, split)
+    let result = ''
+    if(publishTimeText) {
+      if(publishTimeText.includes('最')) {
+        result = publishTimeText.replace('最', '')
+      } else if(publishTimeText.includes('以后')){
+        result = publishTimeText.replace('以后', '')
+      }else if(publishTimeText.includes('以前')) {
+        result = publishTimeText.replace('以前', '')
+      } else {
+        result = publishTimeText
+      }
+    } else {
+      result = inBIPropertyIframe ? '近5年' : '近一年'
+    }
+
+    return result
+  })
+
+
+  /*********页面留资相关************/
+    // 打开留资弹窗
+  const collectElementRef = ref(null)
+  // 免费用户点免费体验留资
+  function onFreeTaste () {
+    if( collectElementRef.value) {
+      collectElementRef.value.isNeedSubmit('jylab_see500_plus', () => {
+      })
+    }
+  }
+
+  /**
+   * searchGroup筛选项缓存
+   */
+  function saveSearchGroupToLocal() {
+    const { key, _storage } = storageConfig.searchGroup
+    const params = {
+      searchGroup: activeTab.value
+    }
+    _storage.setItem(key, JSON.stringify(params))
+  }
+
+  /**
+   * 从缓存恢复searchGroup筛选项
+   */
+  function restoreSearchGroupFromLocal() {
+    const { key, _storage } = storageConfig.searchGroup
+    const params =_storage.getItem(key)
+    const jParams = JSON.parse(params)
+    if (jParams) {
+      doChangeTab({ key: jParams.searchGroup })
+      filterState.value.searchGroup = jParams.searchGroup
+    }
+  }
+
+  /**
+   *  恢复和保存筛选条件到本地业务说明:
+   *  工作台内 1、所有用户都存 2、只有付费用户回显上次筛选
+   *  工作台外:所有用户不存不会回显
+   *
+   *  补充业务:
+   *  付费用户回显筛选条件逻辑:
+   *  本地缓存有,则恢复本地缓存
+   *  本地缓存无,则恢复保存的筛选条件第一条
+   *  本地缓存无,已存筛选条件无,不处理回显
+   *
+   *  搜索范围缓存单独处理
+   *  工作台内外:本地有缓存有则回显,无则默认选中标题
+   */
+
+  /**
+   * 保存筛选条件到本地
+   * @param replace
+   */
+  function saveFilterToLocal(replace = {}) {
+    const { key, _storage } = storageConfig.filter
+    const originParams = packageFilter()
+    let params = originParams
+    if (replace && Object.keys(replace).length) {
+      params = Object.assign(params, replace)
+    }
+    _storage.setItem(key, JSON.stringify(params))
+  }
+
+  /**
+   * 从本地缓存恢复筛选条件
+   *
+   */
+  function restoreFilterFromLocal () {
+    const { key, _storage } = storageConfig.filter
+    const params =_storage.getItem(key)
+    if (params) {
+      restoreFilter(JSON.parse(params), 'localBack')
+    }
+  }
+
+  // 恢复筛选条件
+  function getLastFilter () {
+    if(isLogin.value) {
+      const { key, _storage } = storageConfig.filter
+      const params =_storage.getItem(key)
+      const jParams = params ? JSON.parse(params) : null
+      // 搜索范围--无论用户身份和工作台内外,有则恢复
+      if(jParams) {
+        const defaultSelectType = ['title']
+        const selectType = jParams.selectType
+        if(selectType.indexOf('content') > -1) {
+          defaultSelectType.push('content')
+        }
+        if(selectType.indexOf('file') > -1) {
+          defaultSelectType.push('file')
+        }
+        filterState.value.selectType = defaultSelectType
+      }
+      // 工作台内
+      if(isInApp.value) {
+        // 免费用户仅恢复tab
+        if(isFree.value) {
+          restoreSearchGroupFromLocal()
+        }
+
+        // 仅vip恢复筛选
+        if(isVip.value && jParams) {
+          restoreFilterFromLocal()
+        }
+
+        // 获取已存筛选条件的列表
+        getFilterHistoryList(null,function (arr) {
+          if(isVip.value) {
+            // 付费用户,优先恢复本地缓存,本地无缓存,则恢复已存筛选第一条数据
+            if(!jParams && Array.isArray(arr) && arr.length > 0){
+              restoreFilter(arr[0], 'saveBack')
+            }
+          }
+        })
+      }
+    }
+  }
+  /**
+   * 检查是否需要切换模糊搜索、是否展示提示
+   * 1. 精准搜索无数据 (自动切换模糊搜索)
+   * 2. 精准搜索有数据,< 50,提示手动切换搜索模式
+   */
+
+   const toggleSearchBlurData = reactive( {
+    show: false,
+    count: ''
+  })
+  function checkToggleSearchMode(params) {
+    const searchMode = params.searchMode
+    const pageNum = params.pageNum
+    const count = params.count || 0
+    const blurCount = params.blurCount || 0
+
+    if (pageNum === 1) {
+      // 重置变量
+      toggleSearchBlurData.show = false
+      toggleSearchBlurData.count = ''
+
+      if (searchMode === 0) {
+        const canShowToggleBlurModeTip = count >= 1 && count < 50 && blurCount > count
+
+        if (canShowToggleBlurModeTip) {
+          toggleSearchBlurData.show = true
+          toggleSearchBlurData.count = blurCount
+        }
+      }
+    }
+  }
+
+  // 切换到模糊搜索
+  function doToggleSearchBlurMode() {
+    inputKeywordsState.value.searchMode = '1'
+    setTimeout(function() {
+      scrollToTop(572)
+    }, 50)
+    doQuery()
+  }
+  // 滚动到某个位置
+  function scrollToTop(scrollTop) {
+    document.scrollingElement.scrollTop = scrollTop || 0
+  }
+  // 切换模糊搜索
+  const toggleBlurModeTip = computed( () => {
+    const isBlurMode = Number(inputKeywordsState.value.searchMode) === 1
+    let canShow = isBlurMode ? false : toggleSearchBlurData.show
+    if(listState.loading) {
+      canShow = false
+    }
+    const result = {
+      show: canShow,
+      count: toggleSearchBlurData.count
+    }
+    return result
+  })
+
+  // 页面初始化
+  function initPage () {
+    // 恢复以前选过的tab
+    restoreListTabActive()
+
+    // // 恢复上次筛选条件
+    onMounted(() => {
+      if(!inBIPropertyIframe) {
+        getLastFilter()
+      }
+
+      //  P271潜客圈进--客户引流请求
+      if(inputKeywordsState.value.input) {
+        getCustomReportData({ keywords: inputKeywordsState.value.input})
+      }
+    })
+
+  }
+  initPage()
+
   return {
     isLogin,
     isInApp,
@@ -990,17 +1382,22 @@ export default function () {
     inBIPropertyIframe,
     inInjectBI,
     isInBI,
+    isFree,
     isVip,
+    isBidField,
     goLogin,
     guideGoWorkSpace,
     cooperateCode,
     list,
+    tableList,
     searchModelOptions,
     searchListProps,
     SearchTabsModel,
     inputKeywordsState,
     filterState,
+    updateFilterBase,
     listState,
+    interceptKeywords,
     activeItemStyleType,
     disposeFilterSchema,
     doQuery,
@@ -1038,6 +1435,13 @@ export default function () {
     onSingleEmploy,
     onAddInfoOfBI,
     onSingleAddInfo,
-    goWorkSpace
+    goWorkSpace,
+    timeSelectorText,
+    collectElementRef,
+    onFreeTaste,
+    getLastFilter,
+    toggleBlurModeTip,
+    doToggleSearchBlurMode,
+    recommendCardCircleModel
   }
 }

+ 101 - 0
apps/bigmember_pc/src/views/search/bidding/model/modules/before-search.js

@@ -0,0 +1,101 @@
+// 获取是否允许空搜索白名单
+import { ref } from 'vue'
+import { trim } from 'lodash'
+// 获取反爬虫白名单,用于搜索前处理
+import { getInAntiSpiderWhiteList } from '@/api/modules/'
+import { showToast } from '@/components/toast'
+import $bus from '@/utils/bus'
+
+
+export function beforeSearchModel (paramsObj = { }) {
+
+  const { inputKeywordsState } = paramsObj
+  // 是否是白名单用户
+  let onTheWhiteList = ref(false)
+  // 关键词搜索的校验规则
+  const commonSearchWordsRegExp = ref([])
+
+  async function getWhiteList () {
+    const keywords = inputKeywordsState.value.input
+
+    const { error_code: code, data } = await getInAntiSpiderWhiteList()
+    if(code === 0 && data) {
+      onTheWhiteList.value = data.onTheWhiteList
+
+      if (Array.isArray(data.filterReg)) {
+        const newExp = []
+        data.filterReg.forEach(function(item) {
+          newExp.push(new RegExp(item))
+        })
+        commonSearchWordsRegExp.value = newExp
+      }
+
+
+      if (keywords && !onTheWhiteList) {
+        const timer = setTimeout(function() {
+          clearTimeout(timer)
+          const hasCommonWords = checkAndClearTextIncludesCommonWords(keywords)
+          if (hasCommonWords) {
+            showToast('请输入项目名称等关键词')
+          }
+        }, 500)
+      }
+    }
+  }
+  getWhiteList()
+
+  // 判断文字中是否有通用词(返回true表示text是通用词)
+  // (关键词不为空时,判断是否包含通用词)
+  function checkTextIncludesCommonWords (text) {
+    if (!text) {
+      return false
+    }
+    const passArr = []
+    text = trim(text)
+    if (Array.isArray(commonSearchWordsRegExp.value)) {
+      commonSearchWordsRegExp.value.forEach((reg) => {
+        const newText = text.replace(reg, '')
+        passArr.push(!!newText)
+      })
+    }
+
+    const hasNoPass = passArr.indexOf(false) !== -1
+    return hasNoPass
+  }
+   // text: 去空格后的主关键词
+  function checkAndClearTextIncludesCommonWords (text) {
+    let mainKeysHasCommonWords = false
+    let additionalWordsHasCommonWords = false
+    // 检查主关键词
+    if (checkTextIncludesCommonWords(text)) {
+      mainKeysHasCommonWords = true
+      $bus.$emit('bidding:updateInputKeywords', { input: ''})
+    }
+    // 检查附加词
+    const additionalWords = inputKeywordsState.value.additionalWords
+    const replacedAdditionalWords = []
+    if (Array.isArray(additionalWords)) {
+      for(let i = 0; i < additionalWords.length; i++) {
+        const item = additionalWords[i]
+        if (checkTextIncludesCommonWords(item)) {
+          // ...
+          additionalWordsHasCommonWords = true
+        } else {
+          replacedAdditionalWords.push(item)
+        }
+      }
+      if (additionalWordsHasCommonWords) {
+        $bus.$emit('bidding:updateInputKeywords', { additionalWords: replacedAdditionalWords})
+      }
+    }
+    return mainKeysHasCommonWords || additionalWordsHasCommonWords
+  }
+
+  return {
+    onTheWhiteList,
+    checkAndClearTextIncludesCommonWords
+  }
+
+}
+
+

+ 6 - 1
apps/bigmember_pc/src/views/search/bidding/model/modules/data-add-actions.js

@@ -27,7 +27,12 @@ export function dataAddActionsModel () {
       infoIds = [item.id]
     }
     if(ids) {
-      infoIds = difference(ids, hadAddList )
+      if (ids.length > 0) {
+        infoIds = difference(ids, hadAddList )
+      } else {
+        return showToast('尚未选择数据,请选择')
+      }
+
     }
     if(!infoIds.length) {
       return showToast('所选数据均已添加过,请重新选择')

+ 17 - 3
apps/bigmember_pc/src/views/search/bidding/model/modules/data-export-actions.js

@@ -42,9 +42,9 @@ export function dataExportActionsModel () {
       let countBool = false
       // 限制2000条提示弹窗
       if(selectCheckboxCount > 0) {
-        countBool = selectCheckboxCount >= 2000
+        countBool = selectCheckboxCount >= 20000
       } else {
-        countBool = listState.total >= 2000
+        countBool = listState.total >= 20000
       }
       if (isPrompt && countBool) {
         showDataExportDialog.value = isPrompt
@@ -84,10 +84,24 @@ export function dataExportActionsModel () {
     const { error_code: code } = await ajaxSetDontPromptAgain(params)
   }
 
+  //携带当前检索跳转数据导出支付页面
+  function toPayDataExport (config) {
+   const { listState, selectCheckboxCount, selectIds, filter } = config
+   filterFormatParams.value = FilterHistoryViewModel2AjaxModel.formatAll(filter)
+    // 未登录跳转登录
+    if (!isLogin.value) {
+      $bus.$emit('bidding:goLogin')
+      return
+    } else {
+      toDataExportEvent()
+    }
+  }
+
   return {
     dataExport,
     setExport,
     exportDialogChange,
-    showDataExportDialog
+    showDataExportDialog,
+    toPayDataExport
   }
 }

+ 7 - 1
apps/bigmember_pc/src/views/search/bidding/model/modules/filter-keywords.js

@@ -38,9 +38,15 @@ export function useSearchInputKeywordsModel() {
     return params
   }
 
+  // 动态更新筛选条件
+  function updateInputKeywordsState (keyObj = {}) {
+    inputKeywordsState.value = Object.assign(inputKeywordsState.value, keyObj)
+  }
+
   return {
     inputKeywordsState,
     searchModelOptions,
-    getFormatAPIParams
+    getFormatAPIParams,
+    updateInputKeywordsState
   }
 }

+ 124 - 26
apps/bigmember_pc/src/views/search/bidding/model/modules/filter.js

@@ -1,14 +1,19 @@
-import { ref } from 'vue'
-import  { FilterHistoryViewModel2AjaxModel } from '@/utils'
+import { ref, computed } from 'vue'
+import  {
+  FilterHistoryViewModel2AjaxModel,
+  areaObjTwoToSingle,
+  infoTypeMapFormat
+} from '@/utils'
 
 export function useSearchFilterModel(conf) {
-  const { inBIPropertyIframe } = conf
+  const { inBIPropertyIframe, isFree,isInApp, isBidField } = conf
   // 筛选组件状态
   const filterBase = ref({
+    bidField: isBidField ? 'medical' : '',
     // 发布时间
-    publishTime: 'thisyear',
+    publishTime: (isFree.value || !isInApp.value) ? 'thisyear' : 'fiveyear',
     // 搜索范围
-    selectType: ['content', 'title'],
+    selectType: ['title'],
     // 信息类型
     subtype: [],
     // 地区
@@ -18,7 +23,7 @@ export function useSearchFilterModel(conf) {
     // 附件
     fileExists: '',
     // 金额区间
-    price: null,
+    price: '',
     // 采购单位类型
     buyerclass: {},
     // 采购单位联系方式
@@ -39,9 +44,9 @@ export function useSearchFilterModel(conf) {
     // 地区
     regionMap: {},
     // 到期时间
-    expireTime: '',
+    expireTime: 'all',
     // 业务类型
-    subinformation: [],
+    subInformation: [],
     // 价格区间
     scale: [],
     // 合同周期
@@ -51,19 +56,19 @@ export function useSearchFilterModel(conf) {
     // 搜索范围
     selectType: ['title', 'content'],
     // 信息类型
-    subtype: {},
+    subType: {},
     // 发布时间
-    publishTime: 'fiveyear',
+    publishTime: '',
     // 换手率
-    changehand: '',
+    changeHand: 0,
     // 附件
     fileExists: '',
     // 采购单位联系方式
-    buyertel: '',
+    buyerTel: '',
     // 中标企业联系方式
-    winnertel: '',
+    winnerTel: '',
     // 排除词
-    notkey: []
+    notKey: []
   })
   const filterState = ref({})
   if(inBIPropertyIframe) {
@@ -71,24 +76,24 @@ export function useSearchFilterModel(conf) {
   } else {
     filterState.value = filterBase.value
   }
-
-  function getFormatAPIParams() {
-    if(!inBIPropertyIframe) {
+ // 搜索接口需要的格式化后的数据
+ function getFormatAPIParams (){
+   if(!inBIPropertyIframe) {
      return getFormatApiBaseParams()
-    } else {
-      return getFormatAPIPropertyParams()
-    }
-
-  }
+   } else {
+     return getFormatAPIPropertyParams()
+   }
+ }
   // 格式化招标采购基础筛选条件
-  function getFormatApiBaseParams () {
+  function getFormatApiBaseParams (expendObj = {}) {
     const { publishTime, regionMap, industry, notkey, buyerclass, subtype } = filterState.value
     const { area, city, district } = FilterHistoryViewModel2AjaxModel.formatAreaCity(regionMap)
-    const rPublishTime = publishTime?.indexOf('_') > -1 ? publishTime.replace(/_/g, '-') :  FilterHistoryViewModel2AjaxModel.formatTime(publishTime, true, '-')
+    const rPublishTime = publishTime?.indexOf('_') > -1 ? FilterHistoryViewModel2AjaxModel.formatExactTime(publishTime, '-') :  FilterHistoryViewModel2AjaxModel.formatTime(publishTime, true, '-')
     const rIndustry = FilterHistoryViewModel2AjaxModel.formatIndustry(industry)
     const rBuyerClass = FilterHistoryViewModel2AjaxModel.formatBuyerClass(buyerclass)
     const rSubtype = FilterHistoryViewModel2AjaxModel.formatInfoType(subtype)
     const params = {
+      bidField: filterState.value.bidField,
       publishTime: rPublishTime,
       selectType: filterState.value.selectType.join(','),
       subtype: rSubtype,
@@ -110,13 +115,106 @@ export function useSearchFilterModel(conf) {
   }
   // 格式化物业专版的筛选条件
   function getFormatAPIPropertyParams() {
-    return filterState.value
+    const { publishTime, regionMap, notKey, subType, expireTime } = filterState.value
+    const { area, city } = areaObjTwoToSingle(regionMap)
+    const rSubtype = infoTypeMapFormat(subType)
+    let rPublishTime = ''
+    if(publishTime && publishTime.indexOf('-') > -1) {
+      const arr =  publishTime.split('-')
+      rPublishTime = arr[0]/ 1000  + '-' +  arr[1] / 1000
+    } else {
+      rPublishTime = FilterHistoryViewModel2AjaxModel.formatTime(publishTime, true, '-')
+    }
+    let rExpireTime = ''
+    if(expireTime && expireTime.indexOf('_') > -1) {
+      const arr =  expireTime.split('_')
+      if(arr[0] !== '0') {
+        rExpireTime = arr[0]
+      }
+      if(arr[1] !== '0') {
+        rExpireTime += '_' + arr[1]
+      } else {
+        rExpireTime +=  '_'
+      }
+    } else {
+      rPublishTime = (expireTime === 'all' || !expireTime) ? '' : expireTime
+    }
+    const params = {
+      bidField: 'BIProperty',
+      province: area,
+      city,
+      expireTime: rExpireTime,
+      subInformation: filterState.value.subInformation?.join(),
+      scale: filterState.value.scale?.join(),
+      period:  filterState.value.period?.join(),
+      propertyForm: filterState.value.propertyForm?.join(),
+      selectType: filterState.value.selectType?.join(','),
+      subType: rSubtype,
+      publishTime: rPublishTime || 'fiveyear',
+      changeHand: filterState.value.changeHand ?  Number(filterState.value.changeHand) : 0,
+      fileExists: filterState.value.fileExists,
+      buyerTel: filterState.value.buyerTel,
+      winnerTel: filterState.value.winnerTel,
+      exclusionWords: notKey?.join(','), // 排除词
+    }
+    return params
+  }
+  // 动态更新筛选条件
+  function updateFilterBase (keyObj = {}) {
+    const {key, value } = keyObj
+
+    if(keyObj && Object.keys(keyObj).length > 0) {
+      filterBase.value = Object.assign(filterBase.value, {
+        [key]: value
+      })
+      if(!inBIPropertyIframe) {
+        filterState.value = Object.assign(filterBase.value, filterState.value)
+      }
+    } else {
+      filterBase.value = Object.assign(filterBase.value, {
+        bidField: isBidField ? 'medical' : '',
+        // 发布时间
+        publishTime: (isFree.value || !isInApp.value) ? 'thisyear' : 'fiveyear',
+        // 搜索范围
+        selectType: ['title'],
+        // 信息类型
+        subtype: [],
+        // 地区
+        regionMap: null,
+        // 行业
+        industry: null,
+        // 附件
+        fileExists: '',
+        // 金额区间
+        price: '',
+        // 采购单位类型
+        buyerclass: null,
+        // 采购单位联系方式
+        buyertel: '',
+        // 中标企业联系方式
+        winnertel: '',
+        // 排除词
+        notkey: [],
+        // 采购单位
+        buyer: [],
+        // 中标企业
+        winner: [],
+        // 招标代理机构
+        agency: []
+      })
+      if(!inBIPropertyIframe) {
+        filterState.value = filterBase.value
+      }
+    }
+
   }
 
   return {
     filterState,
     filterProperty,
+    filterBase,
     getFormatAPIParams,
-    getFormatAPIPropertyParams
+    getFormatAPIPropertyParams,
+    updateFilterBase
   }
 }

+ 1 - 1
apps/bigmember_pc/src/views/search/bidding/model/modules/join-bid-actions.js

@@ -17,7 +17,7 @@ export function joinBidActionsModel () {
   async function onJoinBid(item) {
     // 终止参标
     if (item.joinBid) {
-      showToast.$toast('如需终止参标,需要在详情页进行操作。')
+      showToast('如需终止参标,需要在详情页进行操作。')
       return
     }
     // 参标

+ 305 - 0
apps/bigmember_pc/src/views/search/bidding/model/modules/recommend-card.js

@@ -0,0 +1,305 @@
+import { reactive, ref, toRefs, computed } from 'vue'
+import { leadGetDate, ajaxSetLeadGetDateRecord } from '@/api/modules/'
+import {
+  dateFormatter,
+  openSelfLink,
+  replaceKeyword,
+  formatPrice,
+} from '@/utils/'
+import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
+import { difference } from 'lodash'
+
+
+export function recommendCardModel (modelConfig) {
+  const { that } = modelConfig
+
+  const advancedInfo = reactive( {
+    show: false,
+    showContent: false,
+    showDialog: false,
+    dialogContent: '体验超前项目推荐服务!',
+    moduleInfo: {
+      超前项目推荐: {
+        title: '超前项目推荐',
+        desc: '提前推送超前项目,优先对接项目负责人'
+      },
+      市场分析报告: {
+        title: '市场分析报告',
+        desc: '量身定制个性化报告,分析市场竞争格局,为企业找准市场机会!'
+      }
+    },
+    briefList: [],
+    projectList: []
+  })
+
+  const chartCustomData = ref({})
+
+  const chart = reactive( {
+      treeMapKey: 1,
+      treeMapData: [],
+      buyLineKey: 1,
+      buyLineData: {},
+      winnerLineKey: 1,
+      winnerLineData: {}
+  })
+
+  const nowModuleName = computed(() => {
+    if (
+      advancedInfo.briefList.length ||
+      advancedInfo.projectList.length
+    ) {
+      return '超前项目推荐'
+    } else {
+      return '市场分析报告'
+    }
+  })
+  // 获取当前模块展示文本信息
+  const getNowInfo = computed(() => {
+    return advancedInfo.moduleInfo[nowModuleName.value]
+  })
+
+  // 获取当前模块缩小后是否有数据展示
+  const getNotModuleDataStatus = computed(() => {
+    if (nowModuleName.value === '超前项目推荐') {
+      return !advancedInfo.briefList.length
+    } else {
+      return true
+    }
+  })
+
+  // 是否展示市场分析报告模块
+  const getShowChart = computed(() => {
+    return Object.keys(chartCustomData.value).length > 0
+  })
+  // 当前第一位展示内容标识
+  const showModuleChart = computed(() => {
+    const chartKeyArr = Object.keys(chartCustomData.value)
+    const fieldText = getRandomArrayElements(chartKeyArr, 2)
+    const noShowFiled = difference(chartKeyArr, fieldText)
+    return noShowFiled[0]
+  })
+  // 市场分析报告&&超前项目推荐数据请求
+  function getCustomReportData(conf) {
+    const { keywords } = conf
+    const params = {
+      dType: 0,
+      keyWords: keywords
+    }
+    leadGetDate(params).then(res => {
+      const { error_code: code = 0, data } =  res
+      if (code === 0 && data && Object.keys(data).length > 0) {
+        advancedInfo.briefList = (data?.ahead?.subTypeCount || []).map(
+          (v) => {
+            v.value = v.doc_count
+            return v
+          }
+        )
+
+        advancedInfo.projectList = data?.ahead?.projectTop2 || []
+
+        chartCustomData.value = data.custom || {}
+
+        if (
+          advancedInfo.briefList.length ||
+          advancedInfo.projectList.length ||
+          Object.keys(chartCustomData.value).length
+        ) {
+
+          advancedInfo.show = true
+          advancedInfo.showContent = false
+
+          setTimeout(() => {
+            advancedInfo.showContent = true
+            setChartData(data.custom)
+          }, 1000)
+        }
+      }
+    })
+  }
+  // 市场分析报告图表
+  function setChartData(newval) {
+    const chartKeyArr = Object.keys(newval)
+    chartKeyArr.forEach((item) => {
+      if (item === 'customer_scale') {
+        const data = newval.customer_scale.map((item) => {
+          return {
+            ...item,
+            name: item.buyclass,
+            value: item.total,
+            amount: formatPrice(item.amount / 10000)
+          }
+        })
+        chart.treeMapData = data
+        chart.treeMapKey = new Date().getTime()
+      } else if (item === 'winner_time_distribution') {
+        const data = {
+          columns: ['采购规模分布', '采购总金额占比', '采购单位总数占比'],
+          rows: []
+        }
+        let total = 0
+        const chartLIst = newval.winner_time_distribution
+        if (Array.isArray(chartLIst)) {
+          const field = {
+            [data.columns[0]]: 'key',
+            [data.columns[1]]: 'total_amount',
+            [data.columns[2]]: 'total_number'
+          }
+          chartLIst.forEach((item) => {
+            const row = {}
+            data.columns.forEach((column) => {
+              if (
+                field[column] === 'total_amount' ||
+                field[column] === 'total_number'
+              ) {
+                row[column] = (item[field[column]] * 100).toFixed(2)
+                total += item[field[column]] - 0
+              } else {
+                row[column] = item[field[column]]
+              }
+            })
+            data.rows.push(row)
+          })
+        }
+        if (total) {
+          data.rows.reverse()
+          chart.buyLineData = data
+          chart.buyLineKey = new Date().getTime()
+        }
+      } else if (item === 'buyer_time_distribution') {
+        const data = {
+          columns: ['中标规模分布', '中标总金额占比', '中标单位总数占比'],
+          rows: []
+        }
+        let total = 0
+        const buyerclassList = newval.buyer_time_distribution
+        if (Array.isArray(buyerclassList)) {
+          const field = {
+            [data.columns[0]]: 'key',
+            [data.columns[1]]: 'total_amount',
+            [data.columns[2]]: 'total_number'
+          }
+          buyerclassList.forEach((item) => {
+            const row = {}
+            data.columns.forEach((column) => {
+              if (
+                field[column] === 'total_amount' ||
+                field[column] === 'total_number'
+              ) {
+                row[column] = (item[field[column]] * 100).toFixed(2)
+                total += item[field[column]] - 0
+              } else {
+                row[column] = item[field[column]]
+              }
+            })
+            data.rows.push(row)
+          })
+        }
+        if (total) {
+          data.rows.reverse()
+          chart.winnerLineData = data
+          chart.winnerLineKey = new Date().getTime()
+        }
+      }
+    })
+  }
+
+  // 收起或打开 展示超前项目、模块
+  function toggleAdvancedContent(type) {
+    if (typeof type !== 'boolean') {
+      type = !advancedInfo.showContent
+    }
+    advancedInfo.showContent = type
+  }
+  // 展示弹窗
+  function showAdvancedDialog(title = '体验超前项目推荐服务!') {
+    advancedInfo.dialogContent = title
+    advancedInfo.showDialog = true
+  }
+
+  // 点击感兴趣
+  function onClickInterested(type) {
+    const title = type === 'A' ? '体验超前项目推荐服务!' : '为您量身定制个性化报告!'
+    showAdvancedDialog(title)
+    try {
+      ajaxSetLeadGetDateRecord({ type })
+    } catch (e) {
+      console.warn(e)
+    }
+  }
+  // 查看完整报告
+  async function goToReport() {
+    try {
+      await ajaxSetLeadGetDateRecord({ type: 'B' })
+    } catch (e) {
+      console.warn(e)
+    }
+    tryCallHooks({
+      fn: () => {
+        that.$BRACE.methods.open({
+          route: {
+            link: '/desktop/report_analysis',
+            appName: 'big',
+            appType: 'qiankun'
+          }
+        })
+      },
+      spareFn: () => {
+        window.open(
+          '/swordfish/page_big_pc/desktop/report_analysis?tab=analysis'
+        )
+      }
+    })
+  }
+  // 查看项目详情
+  function goToContent(item) {
+    try {
+      ajaxSetLeadGetDateRecord({ type: 'A' })
+    } catch (e) {
+      console.warn(e)
+    }
+    let goURL = '/article/content/' + item._id + '.html'
+    if (Array.isArray(item.keyWord)) {
+      goURL += '?kds=' + encodeURIComponent(item.keyWord.join('+'))
+    }
+    window.open(goURL)
+  }
+  // 匹配高亮文本信息
+  function getProjectTitle({ title, keyWord }) {
+    return replaceKeyword(title, keyWord, [
+      '<span class="highlight-text">',
+      '</span>'
+    ])
+  }
+  // /* 随机获取数组中的数据*/
+  function getRandomArrayElements(arr, count) {
+    const shuffled = arr.slice(0)
+    let i = arr.length
+    const min = i - count
+    let temp
+    let index
+    while (i-- > min) {
+      index = Math.floor((i + 1) * Math.random())
+      temp = shuffled[index]
+      shuffled[index] = shuffled[i]
+      shuffled[i] = temp
+    }
+    return shuffled.slice(min)
+  }
+
+  return {
+    getCustomReportData,
+    toggleAdvancedContent,
+    advancedInfo,
+    onClickInterested,
+    goToContent,
+    getProjectTitle,
+    goToReport,
+    getShowChart,
+    getNotModuleDataStatus,
+    getNowInfo,
+    nowModuleName,
+    chartCustomData,
+    showModuleChart,
+    chart
+  }
+}

+ 6 - 1
apps/bigmember_pc/src/views/search/bidding/model/modules/save-filter-actions.js

@@ -83,7 +83,7 @@ export function saveFilterActionsModel () {
     }
   }
   // 获取已存筛选条件
-  async function  getFilterHistoryList(type) {
+  async function  getFilterHistoryList(type, callback) {
     try {
         const { data, error_code: code, error_msg: msg } = await getBiddingFilterList()
       if (code === 0) {
@@ -101,6 +101,7 @@ export function saveFilterActionsModel () {
         if(type === 'delete' && arr.length === 0) {
           historyFilterDialogVisible.value = false
         }
+        callback && callback(arr)
       } else {
         if (msg) {
           showToast(msg)
@@ -115,6 +116,10 @@ export function saveFilterActionsModel () {
    * @returns {Promise<void>}
    */
   async function onHasFilter () {
+    if(!historyFilterCount.value) {
+      showToast('请先保存筛选条件')
+      return
+    }
     historyFilterDialogVisible.value = true
   }
   // 已存筛选条件弹窗,单条展开收起

+ 16 - 4
apps/bigmember_pc/src/views/search/bidding/model/modules/tabs.js

@@ -1,7 +1,7 @@
 import { computed, ref } from 'vue'
 
 export function useSearchTabsModel(conf) {
-  const { showTabs2 } = conf
+  const { showTabs2, inInjectBI } = conf
   const defaultTab = showTabs2 ? 1 : 0
   const activeTab = ref(defaultTab)
   const Tabs = [
@@ -23,7 +23,7 @@ export function useSearchTabsModel(conf) {
     {
       label: '招标采购公告',
       key: 1,
-      link: '/swordfish/page_big_pc/search/bidding?searchGroup=1'
+      link: '/jylab/supsearch/index.html?searchGroup=1'
     },
     {
       label: '企业搜索',
@@ -43,7 +43,7 @@ export function useSearchTabsModel(conf) {
     {
       label: '超前项目查询',
       key: 2,
-      link: '/swordfish/page_big_pc/search/bidding?searchGroup=2'
+      link: '/jylab/supsearch/index.html?searchGroup=2'
     },
     {
       label: '拟在建项目查询',
@@ -51,7 +51,19 @@ export function useSearchTabsModel(conf) {
       link: '/swordfish/page_web_pc/search/construction'
     },
   ]
-  const resTab = showTabs2 ? Tabs2 : Tabs
+  let resTab = []
+  if(showTabs2){
+    resTab = Tabs2
+  } else {
+    // 个人分析报告不展示超前项目
+    if(inInjectBI){
+      resTab = Tabs.filter(item => {
+        return item.label !== '超前项目'
+      })
+    } else {
+      resTab = Tabs
+    }
+  }
   const searchTabs = computed(() => {
     return resTab.map((v) => {
       v.active = activeTab.value === v.key

+ 15 - 8
apps/bigmember_pc/src/views/search/components/SearchHeader.vue

@@ -62,6 +62,11 @@ export default {
     showWxQr:{
       type: Boolean,
       default: true
+    },
+    // 是否需要刷新url的Keywords
+    needReplaceRouter: {
+      type: Boolean,
+      default: false
     }
   },
   model: {
@@ -81,12 +86,12 @@ export default {
   },
   methods: {
     getParams() {
+      let kWords = ''
       let { keywords } = this.$route.query
-      if (keywords === 'undefined') {
-        keywords = ''
-      }
-      this.searchQuery.keywords = keywords
-      this.onInput(keywords)
+      kWords = keywords || this.searchContent || ''
+
+      this.searchQuery.keywords = kWords
+      this.onInput(kWords)
       if (this.immediateSearch) {
         this.onSearch()
       }
@@ -94,9 +99,11 @@ export default {
     onInput(e) {
       this.$emit('input', e)
     },
-    onSearch() {
-      this.$emit('search')
-      // this.setUrlQuery()
+    onSearch(e) {
+      this.$emit('search', e)
+      if(this.needReplaceRouter){
+        this.setUrlQuery()
+      }
     },
     setUrlQuery: function () {
       const isSame = this.searchQuery.keywords === this.searchContent

+ 1 - 1
apps/bigmember_pc/src/views/search/components/keyword-tags.vue

@@ -11,7 +11,7 @@
         {{ tag }}
       </el-tag>
     </div>
-    <div v-if="list.length < maxListLength">
+    <div class="add-keyword-actions" v-if="list.length < maxListLength">
       <el-input
         class="add-keyword-input"
         type="text"

+ 7 - 2
apps/bigmember_pc/src/views/search/components/search-header-card.vue

@@ -24,6 +24,11 @@ const props = defineProps({
   showWorkspaceButton: {
     type: Boolean,
     default: true
+  },
+  // 是否展示tab
+  showTab: {
+    type: Boolean,
+    default: true
   }
 })
 
@@ -37,7 +42,7 @@ function goWorkSpaceCustom() {
 
 <template>
   <div class="search-header-card">
-    <div class="tab-header">
+    <div class="tab-header" v-if="showTab">
       <span
         class="tab-header-item"
         :data-badge="tab.badge"
@@ -59,7 +64,7 @@ function goWorkSpaceCustom() {
       :searchContent="value"
       :show-wx-qr="showWxQr"
       @input="$emit('input', $event)"
-      @search="$emit('search')"
+      @search="$emit('search', $event)"
     >
       <div class="tab-switch" slot="tab-switch">
         <slot name="tab-switch-content"></slot>

+ 42 - 34
apps/bigmember_pc/src/views/search/components/search-schema-filter.vue

@@ -57,43 +57,45 @@ const getPrefix = {
         <slot name="row-label-text">{{ rowLabelText }}</slot>
       </div>
     </slot>
-    <div
-      class="search-schema-filter-item flex flex-row"
-      v-for="(item, index) in schema"
-      :key="getPrefix.item + index"
-      :class="{
+    <div class="search-schema-filter-box">
+      <div
+        class="search-schema-filter-item flex flex-row"
+        v-for="(item, index) in schema"
+        :key="getPrefix.item + index"
+        :class="{
         'flex-items-start': showLabel && item.label
       }"
-    >
-      <div class="search-schema-filter-label" v-if="showLabel && item.label">
-        <slot name="item-label">
-          {{ item.label }}
-        </slot>
-      </div>
-      <div class="flex-1">
-        <!-- @component 自定义组件  -->
-        <!-- @input @change 兼容不同组件的输出  -->
-        <Component
-          v-if="item._type === 'component'"
-          :is="item.expand.component"
-          :ref="getPrefix.component + item._name"
-          :value="props.value[item.key]"
-          @input="doChangeInput(item.key, $event)"
-          @change="doChangeInput(item.key, $event)"
-          v-bind="item.expand.props"
-          v-on="{
+      >
+        <div class="search-schema-filter-label" v-if="showLabel && item.label">
+          <slot name="item-label">
+            {{ item.label }}
+          </slot>
+        </div>
+        <div class="flex-1">
+          <!-- @component 自定义组件  -->
+          <!-- @input @change 兼容不同组件的输出  -->
+          <Component
+            v-if="item._type === 'component'"
+            :is="item.expand.component"
+            :ref="getPrefix.component + item._name"
+            :value="props.value[item.key]"
+            @input="doChangeInput(item.key, $event)"
+            @change="doChangeInput(item.key, $event)"
+            v-bind="item.expand.props"
+            v-on="{
             ...item.expand.hooks
           }"
-        >
-        </Component>
-        <!-- @slot 自定义插槽  -->
-        <slot
-          v-if="item._type === 'slot'"
-          :ref="getPrefix.slot + item._name"
-          :name="item.expand.slot"
-          :item="item"
-          :index="index"
-        ></slot>
+          >
+          </Component>
+          <!-- @slot 自定义插槽  -->
+          <slot
+            v-if="item._type === 'slot'"
+            :ref="getPrefix.slot + item._name"
+            :name="item.expand.slot"
+            :item="item"
+            :index="index"
+          ></slot>
+        </div>
       </div>
     </div>
   </div>
@@ -112,7 +114,13 @@ const getPrefix = {
   &.use-style-row {
     display: flex;
     flex-direction: row;
-    align-items: center;
+    .search-schema-filter-box{
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      flex-wrap: wrap;
+    }
+
   }
 
   .search-schema-filter-label {

+ 3 - 3
apps/bigmember_pc/src/views/search/ent/model/base.js

@@ -104,12 +104,12 @@ export default function () {
     }
     // 地区数据格式处理结束
     const params = {
-      matchType: filterState.value.matchType.join(','),
+      matchType: filterState.value.matchType?.join(','),
       entArea: entArea.join(','),
       entCity: entCity.join(','),
       entCapital: filterState.value.entCapital,
-      entType: filterState.value.entType.join(','),
-      entStatus: filterState.value.entStatus.join(',')
+      entType: filterState.value.entType?.join(','),
+      entStatus: filterState.value.entStatus?.join(',')
     }
 
     return params

+ 81 - 175
apps/bigmember_pc/src/views/subscribe/SubPush.vue

@@ -62,7 +62,7 @@
             ></span>
           </div>
         </TimeSelector>
-        <!-- <AreaSelector
+        <AreaSelector
           v-if="autoVt === 'f'"
           @onChange="changeArea"
           :beforeTabClick="beforeSelected"
@@ -147,8 +147,8 @@
           >
             信息类型:<span class="el-icon-jy-vip" v-if="showVipTag"></span>
           </div>
-        </InfoTypeSelector> -->
-        <!-- <PopSelector
+        </InfoTypeSelector>
+        <PopSelector
           @onChange="changeKeys"
           ref="keySelector"
           :beforeTabClick="beforeSelected"
@@ -161,8 +161,8 @@
           >
             关键词:<span class="el-icon-jy-vip" v-if="showVipTag"></span>
           </div>
-        </PopSelector> -->
-        <!-- <selectorCard cardType="line">
+        </PopSelector>
+        <selectorCard cardType="line">
           <div
             slot="header"
             class="s-header filter-label"
@@ -178,8 +178,8 @@
             :beforeChange="beforeSelected"
             @onChange="onFileStateChange"
           />
-        </selectorCard> -->
-        <!-- <selectorCard class="more-selector" cardType="line">
+        </selectorCard>
+        <selectorCard class="more-selector" cardType="line">
           <div
             slot="header"
             class="s-header filter-label"
@@ -194,26 +194,13 @@
             :beforeChange="beforeSelected"
             @getSelectStatus="getSelectStatus"
             @addUserPerson="addUserPerson"
-            @onKeywordChange="onKeywordChange"
             :person="showPerson"
-            :key-list="keyList"
             :show-vip-tag="showVipTag"
             :vt="vt"
             :info-source="infoSource"
             :view-status-list="viewStatusList"
           ></selectGroup>
-        </selectorCard> -->
-        <!--  更多筛选  -->
-        <search-schema-filter
-          v-model="filters"
-          :schema="moduleList"
-          :show-label="false"
-          :show-row-label="true"
-          style-type="row"
-          @change="doChangeFilter"
-        >
-          <span slot="row-label-text">更多筛选:</span>
-        </search-schema-filter>
+        </selectorCard>
       </div>
       <!-- 超前项目推荐&&市场分析报告 -->
       <div id="jyChartCom">
@@ -438,6 +425,7 @@
           </button>
         </template>
       </CustomDialog>
+      <CheckUserDialog />
       <FollowOfficialAccountDialog
         :visible.sync="dialog.toFollowOfficialAccount"
       ></FollowOfficialAccountDialog>
@@ -474,8 +462,6 @@
 
 <script>
 import { Button, Dialog, Drawer } from 'element-ui'
-import SearchSchemaFilter from '@/views/search/components/search-schema-filter.vue'
-import { subscribeMoreSchemaModule } from '@/views/subscribe/constant/search-filters'
 import CustomDialog from '@/components/dialog/Dialog.vue'
 import CollapseTransition from 'element-ui/lib/transitions/collapse-transition'
 import DrawerCard from '@/components/drawer/Drawer.vue'
@@ -493,6 +479,7 @@ import RadioGroup from '@/components/selector/RadioGroup.vue'
 // import CustomReport from '@/components/custom-report/customReport.vue'
 import FollowOfficialAccountDialog from '@/components/dialog/FollowOfficialAccountDialog.vue'
 import ConfigContent from '@/components/subscribe-manager/index'
+import CheckUserDialog from '@/components/dialog/CheckUserDialog'
 import SubscribeOverview from '@/components/subscribe-overview/index'
 import {
   getFreeUserPushInfo,
@@ -505,9 +492,8 @@ import {
   dateFormatter,
   openSelfLink,
   replaceKeyword,
-  formatPrice,
+  formatPrice
 } from '@/utils/'
-import { InfoTypeTransform } from '@/utils/format/info-type-transform'
 import { mapState, mapGetters, mapActions } from 'vuex'
 import { difference } from 'lodash'
 import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
@@ -528,6 +514,7 @@ export default {
     PriceSelector,
     RadioGroup,
     DrawerCard,
+    CheckUserDialog,
     [CustomDialog.name]: CustomDialog,
     // CustomReport,
     FollowOfficialAccountDialog,
@@ -540,8 +527,7 @@ export default {
     SelectGroup,
     [CollapseTransition.name]: CollapseTransition,
     SubscribeOverview,
-    AreaCityCountryCascader,
-    SearchSchemaFilter
+    AreaCityCountryCascader
   },
   data() {
     return {
@@ -557,8 +543,7 @@ export default {
         gray: true,
         table: true,
         collect: true,
-        push: true,
-        detail: true,
+        push: true
       },
       withFileList: [
         {
@@ -612,9 +597,9 @@ export default {
         area: '',
         selectTime: '',
         city: '',
-        buyerClass: {},
-        subtype: {},
-        industry: {},
+        buyerClass: '',
+        subtype: '',
+        industry: '',
         keyWords: '',
         fileExists: '',
         price: '',
@@ -654,9 +639,7 @@ export default {
       },
       // 员工订阅总览抽屉
       showOverviewDrawer: false,
-      isOpenedViewDetail: false,
-      keyList: [],
-      moduleList: []
+      isOpenedViewDetail: false
     }
   },
   computed: {
@@ -793,7 +776,6 @@ export default {
     this.getInitInfo()
     await this.getSubScribeKeyList({ vt: this.vt })
     await this.getNewSubScribeKeyList(this.vt)
-    this.moduleList = subscribeMoreSchemaModule
   },
   mounted() {
     this.initQuery()
@@ -804,32 +786,22 @@ export default {
       this.initKeyMap()
     }
   },
+  // 此方法移除,使用router-view上的:key="route.fullPath"代替
+  // async beforeRouteUpdate (to, from, next) {
+  //   // 页面路由发生变化需要重新执行
+  //   this.getParams()
+  //   this.getInitInfo()
+  //   await this.getSubScribeKeyList({ vt: this.vt })
+  //   this.$nextTick(() => {
+  //     this.initQuery()
+  //     this.initKeyMap()
+  //     // 重置所有筛选条件
+  //     this.resetAllSelector()
+  //     next()
+  //   })
+  // },
   methods: {
     ...mapActions('user', ['getEntInfo', 'getSubScribeKeyList']),
-    // 关键词数据
-    onKeywordChange(data) {
-      this.filters.keyWords = data.two_noall.join(',')
-      const resultObject = {}
-      data.oneAndtwo.forEach((item) => {
-        const [category, subItemsStr] = item.split('_')
-        if (!resultObject[category]) {
-          resultObject[category] = []
-        }
-
-        // 对于包含多个子项的字符串(如“钢结构 彩钢板 门窗”),进一步分割并添加到数组中
-        subItemsStr.split(' ').forEach((subItem) => {
-          if (subItem !== '') { // 忽略空字符串(可能因连续空格导致)
-            resultObject[category].push(subItem.trim())
-          }
-        })
-      })
-      this.filters.item = resultObject
-      this.$refs.pushList.doQuery(this.filters)
-    },
-    doChangeFilter(data) {
-      this.onAreaCityCountry(data)
-      this.doQuery()
-    },
     // 市场分析报告图表
     setChartData(newval) {
       const chartKeyArr = Object.keys(newval)
@@ -1082,44 +1054,11 @@ export default {
       }
     },
     initKeyMap() {
-      const tempKeys = []
-      tempKeys.push({
-        label: '全部',
-        value: '全部',
-        children: [
-          {
-            label: '全部',
-            value: '全部',
-            parent: '全部',
-            selected: false,
-            indeterminate: false,
-            disabled: true
-          }
-        ],
-        selected: false,
-        indeterminate: false,
-        disabled: false
-      })
-      let tempObj = {}
+      const tempKeys = {}
       if (this.vt === 'f') {
-        tempObj = {
-          label: '未分类',
-          value: '未分类',
-          children: [],
-          selected: false,
-          indeterminate: false,
-          disabled: false
-        }
+        let tempArr = []
+        // eslint-disable-next-line no-unused-expressions
         if (this.subscribeKeyList && this.subscribeKeyList.length > 0) {
-          let keyArr = []
-          keyArr.push({
-            label: '全部',
-            value: '全部',
-            parent: '未分类',
-            selected: false,
-            indeterminate: false,
-            disabled: false
-          })
           this.subscribeKeyList.forEach((s) => {
             if (s.key && s.key.length > 1) {
               if (Array.isArray(s.key)) {
@@ -1130,77 +1069,60 @@ export default {
             if (s?.appendkey && s?.key) {
               tempList = [s?.key + ' ' + s?.appendkey.join(' ')]
             }
-            let keyObj = {
-              label: tempList.join(' '),
-              value: tempList.join(' '),
-              parent: '未分类',
-              selected: false,
-              indeterminate: false,
-              disabled: false,
-              matchway: s?.matchway
+            const newList = []
+            if (s?.matchway === 1) {
+              // 模糊  不改变传入组件数据结构,拼接字符串作为标识
+              tempList.forEach((ele) => {
+                newList.push(ele + '_matchway_1')
+              })
+            } else {
+              tempList.forEach((ele) => {
+                newList.push(ele + '_matchway_0')
+              })
             }
-            keyArr.push(keyObj)
-            tempObj.children = keyArr
-            tempKeys.push(tempObj)
+            tempArr = tempArr.concat(newList)
           })
+          tempKeys['未分类'] = tempArr
         }
       } else {
         // eslint-disable-next-line no-unused-expressions
         console.log(this.subscribeKeyList)
         if (this.subscribeKeyList && this.subscribeKeyList.length > 0) {
           this.subscribeKeyList.forEach((v) => {
-            if (v.s_item) {
-              tempObj = {
-                label: v.s_item,
-                value: v.s_item,
-                children: [],
-                selected: false,
-                indeterminate: false,
-                disabled: false
-              }
-              if (v.a_key) {
-                let keyArr = []
-                keyArr.push({
-                  label: '全部',
-                  value: '全部',
-                  parent: v.s_item,
-                  selected: false,
-                  indeterminate: false,
-                  disabled: false
-                })
-                v.a_key.forEach((s) => {
-                  // 把不同关键词设置提交的格式差异数据统一,用空格作为分隔关键词放入数组 (已知大会员,企业订阅关键词格式为['aa bb cc'])
-                  if (s.key && s.key.length > 1 && this.vt === 'v') {
-                    // 超级订阅提交回显关键词处理
-                    if (Array.isArray(s.key)) {
-                      s.key = [s.key.join(' ')]
-                    }
-                  }
-                  let tempList = s?.key || []
-                  if (s?.appendkey && s?.key) {
-                    tempList = [s?.key + ' ' + s?.appendkey.join(' ')] // 有时会存在appendkey数组 同样处理为空格分隔的字符串放入数组
-                  }
-                  let keyObj = {
-                    label: tempList.join(' '),
-                    value: tempList.join(' '),
-                    parent: v.s_item,
-                    selected: false,
-                    indeterminate: false,
-                    disabled: false,
-                    notkey: s.notkey?.join(' '),
-                    matchway: s?.matchway
+            let tempArr = []
+            if (v.a_key) {
+              v.a_key.forEach((s) => {
+                // 把不同关键词设置提交的格式差异数据统一,用空格作为分隔关键词放入数组 (已知大会员,企业订阅关键词格式为['aa bb cc'])
+                if (s.key && s.key.length > 1 && this.vt === 'v') {
+                  // 超级订阅提交回显关键词处理
+                  if (Array.isArray(s.key)) {
+                    s.key = [s.key.join(' ')]
                   }
-                  keyArr.push(keyObj)
-                })
-                tempObj.children = keyArr
-              }
+                }
+                let tempList = s?.key || []
+                if (s?.appendkey && s?.key) {
+                  tempList = [s?.key + ' ' + s?.appendkey.join(' ')] // 有时会存在appendkey数组 同样处理为空格分隔的字符串放入数组
+                }
+                const newList = []
+                if (s.matchway === 1) {
+                  // 模糊  不改变传入组件数据结构,拼接字符串作为标识
+                  tempList.forEach((ele) => {
+                    newList.push(ele + '_matchway_1')
+                  })
+                } else {
+                  // 精准
+                  tempList.forEach((ele) => {
+                    newList.push(ele + '_matchway_0')
+                  })
+                }
+                tempArr = tempArr.concat(newList)
+              })
             }
-            tempKeys.push(tempObj)
+            tempKeys[v.s_item] = tempArr
           })
         }
       }
-      this.keyList = tempKeys
-      // this.$refs.keySelector.initDataMap(tempKeys)
+      this.$refs.keySelector.initDataMap(tempKeys)
     },
     initQuery() {
       this.$refs.timeSelector.setState({
@@ -1228,14 +1150,17 @@ export default {
     },
     changeIndustry(item) {
       this.filters.industry = 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.filters.subtype = item.join(',')
+      this.doQuery()
     },
     onFileStateChange() {
       this.doQuery()
@@ -1250,14 +1175,8 @@ export default {
       this.doQuery()
     },
     doQuery() {
-      const filterBuyerClass = this.formatIndustryMap(this.filters.buyerClass).map((v) => v.split('_')[1]).join(',')
-      console.log(this.filters, 'filters')
-      const filterSubtype = InfoTypeTransform.mapToList(this.filters.subtype)
       const payload = {
         ...this.filters,
-        industry: this.formatIndustryMap(this.filters.industry).join(','),
-        buyerClass: filterBuyerClass,
-        subtype: filterSubtype.length > 0 ? filterSubtype.join(',') : '',
         vt: this.vt === 'f' ? '' : this.vt
       }
       this.$refs.pushList.doQuery(payload)
@@ -1623,19 +1542,6 @@ export default {
   background-color: #fff;
 
   ::v-deep {
-    .date-time-container{
-      background-color: transparent;
-    }
-    .selector-card:not(:last-of-type){
-      border-bottom: none;
-    }
-    .search-schema-filter-container {
-      padding: 0 32px;
-    }
-    .selector-card.s-line .selector-card-header{
-      margin-right: 12px;
-      min-width: 70px;
-    }
     .drawer-class {
       background: #f7f9fc;
     }
@@ -2067,7 +1973,7 @@ export default {
     justify-content: flex-end;
     font-size: 14px;
     white-space: nowrap;
-    width: 70px;
+    min-width: 98px;
     color: #686868;
   }
 

+ 2 - 2
apps/bigmember_pc/src/views/subscribe/constant/search-filters.js

@@ -1,6 +1,6 @@
 import BuyerTypeSelector from '@/components/filter-items/BuyerTypeSelector.vue'
 import IndustrySelector from '@/components/filter-items/IndustrySelector.vue'
-import KeyWordSelector from '@/components/filter-items/KeyWordSelector.vue'
+import KeyWordSelector from '@/components/filter-items/KeywordSelector.vue'
 import RegionSelector from '@/components/filter-items/RegionSelector'
 import AmountRangeSelector from '@/components/filter-items/AmountRangeSelector.vue'
 import AttachmentSelector from '@/components/filter-items/AttachmentSelector.vue'
@@ -189,4 +189,4 @@ function createSubscribeMoreSchema() {
 }
 
 const subscribeMoreSchemaModule = createSubscribeMoreSchema.apply(this)
-export { subscribeMoreSchemaModule }
+export { subscribeMoreSchemaModule }

+ 20 - 2
apps/bigmember_pc/src/views/workspace/dashboard.vue

@@ -28,8 +28,12 @@
       <MessageTips class="aside-module"></MessageTips>
       <AsideOthers class="aside-module"></AsideOthers>
     </el-aside>
-    <ActivityDialog ad="jy-pc-index-tap" />
+    <ActivityDialog
+      @initNext="initNextDialog('checkUserShow')"
+      ad="jy-pc-index-tap"
+    />
     <GuideIntroDialog />
+    <CheckUserDialog v-if="dialog.checkUserShow" />
   </el-container>
 </template>
 
@@ -39,6 +43,7 @@ import { chunk } from 'lodash'
 import { Container, Aside, Main } from 'element-ui'
 import MessageTips from './components/MessageTips.vue'
 import CommonUse from './components/CommonUse.vue'
+import CheckUserDialog from '@/components/dialog/CheckUserDialog'
 import SubscribeList from './components/SubscribeList.vue'
 // import MyCollections from './components/MyCollections.vue'
 // import ProjectFollow from './components/ProjectFollow.vue'
@@ -82,8 +87,16 @@ export default {
     BusinessToDo,
     NewsList,
     AnalysisReport,
+    CheckUserDialog,
     MyEquityList
   },
+  data() {
+    return {
+      dialog: {
+        checkUserShow: false
+      }
+    }
+  },
   computed: {
     componentsPowerMap() {
       const { myCustomerShow, customerWatcherShow, hasMemberNJPower } = this
@@ -115,7 +128,12 @@ export default {
       'customerWatcherShow',
       'hasMemberNJPower',
       'dataReportShow'
-    ])
+    ]),
+  },
+  methods: {
+    initNextDialog(key) {
+      this.dialog[key] = true
+    }
   }
 }
 </script>

+ 2 - 2
apps/bigmember_pc/vite.config.js

@@ -21,7 +21,7 @@ const baseCDN = {
   js: [
     'https://cdn-common.jianyu360.com/cdn/lib/echarts/4.8.0/echarts.min.js',
     'https://cdn-common.jianyu360.com/cdn/lib/v-charts/1.19.0/index.min.js',
-    'https://cdn-common.jianyu360.com/cdn/lib/jquery/3.5.1/jquery.min.js',
+    // 'https://cdn-common.jianyu360.com/cdn/lib/jquery/3.5.1/jquery.min.js', // 标签上需要添加ignore
     'https://res.wx.qq.com/open/js/jweixin-1.6.0.js'
   ]
 }
@@ -137,6 +137,6 @@ export default defineConfig({
   },
   server: {
     port: 8081,
-    proxy: useServerProxy('web2')
+    proxy: useServerProxy('web')
   }
 })

+ 11 - 1
apps/mobile/src/api/modules/ent.js

@@ -1,5 +1,5 @@
 import request from '@/api'
-// import qs from 'qs'
+import qs from 'qs'
 // 获取企业列表
 export function getEntList(data) {
   // data = qs.stringify(data)
@@ -28,3 +28,13 @@ export function ajaxGetUserInfo(data) {
     data
   })
 }
+
+// 获取用户最高权限身份
+export function getUserHighestPowerIdentity(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/publicapply/identity/newProduct',
+    method: 'post',
+    data
+  })
+}

+ 8 - 0
apps/mobile/src/api/modules/pay.js

@@ -290,3 +290,11 @@ export function sendProjectPdfFile(data) {
     data: qs.stringify(data)
   })
 }
+
+export function vipRenewReminder(data) {
+  return request({
+    url: '/subscribepay/vipsubscribe/vipRenewReminder',
+    method: 'post',
+    data: qs.stringify(data)
+  })
+}

BIN
apps/mobile/src/assets/image/tip/vip-expire-dialog-bg.png


+ 55 - 32
apps/mobile/src/components/ad/pop-screen/index.vue

@@ -1,10 +1,15 @@
 <template>
-  <van-overlay
+  <van-dialog
     v-if="!destroyed"
-    class-name="pop-screen"
-    :show="show"
-    @click="close"
+    get-container="body"
+    close-on-click-overlay
+    overlay-class="tabbar-home-pop-screen"
+    :show-confirm-button="false"
+    class="tabbar-home-dialog"
+    v-model="show"
+    @close="close"
   >
+    <AppIcon name="close" @click.stop="close" />
     <div
       class="container"
       :style="{
@@ -13,7 +18,6 @@
       }"
       @click.stop="openAD"
     >
-      <AppIcon name="close" @click.stop="close" />
       <AdSvga
         v-if="isSvgaType"
         :style="{
@@ -21,7 +25,6 @@
           height: getStyle.height
         }"
         :svga-link="config.pic"
-        @load="loadSuccess"
       />
       <van-image
         v-else
@@ -29,14 +32,13 @@
         :src="config.pic"
         :width="getStyle.width"
         :height="getStyle.height"
-        @load="loadSuccess"
       />
     </div>
-  </van-overlay>
+  </van-dialog>
 </template>
 
 <script>
-import { Icon, Image, Overlay } from 'vant'
+import { Icon, Image, Dialog } from 'vant'
 import { AppIcon } from '@/ui'
 import AdSvga from '@/components/ad/svga-support'
 
@@ -47,7 +49,7 @@ export default {
   components: {
     [AppIcon.name]: AppIcon,
     [AdSvga.name]: AdSvga,
-    [Overlay.name]: Overlay,
+    [Dialog.Component.name]: Dialog.Component,
     [Image.name]: Image,
     [Icon.name]: Icon
   },
@@ -94,7 +96,6 @@ export default {
   /**
    * 初始化数据
    * @typedef {object} FullScreen 初始化数据
-   * @property {boolean} loaded 广告图片是否加载完成
    * @property {boolean} destroyed 是否需要初始化组件
    * @property {boolean} show 是否显示遮罩
    */
@@ -103,7 +104,6 @@ export default {
    * @returns FullScreen
    */
   data: () => ({
-    loaded: false,
     destroyed: true,
     show: false
   }),
@@ -112,12 +112,20 @@ export default {
     if (!toDayShow) {
       this.destroyed = false
     } else {
+      this.initNextDialog()
       this.$emit('destroyed')
     }
   },
+  mounted() {
+    this.loadSuccess()
+  },
   methods: {
+    initNextDialog() {
+      this.$nextTick(() => {
+        this.$emit('initNext')
+      })
+    },
     loadSuccess() {
-      this.loaded = true
       this.show = true
       this.$emit('load')
     },
@@ -133,6 +141,7 @@ export default {
           expires: this.expires
         })
       }
+      this.initNextDialog()
       this.$emit('close', type)
     },
     async openAD() {
@@ -154,26 +163,40 @@ export default {
 }
 </script>
 
-<style scoped lang="scss">
-.pop-screen {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
+<style lang="scss">
+.tabbar-home-pop-screen {
   background: rgba(0, 0, 0, 0.5);
-  .container {
-    position: relative;
-  }
-  .van-image {
-    max-width: 100%;
-  }
-  .iconfont {
-    pointer-events: none;
-    position: absolute;
-    top: -28px - 16px;
-    right: 0;
-    font-size: 28px;
-    color: #fff;
+  z-index: 3001 !important;
+}
+</style>
+<style scoped lang="scss">
+::v-deep {
+  .van-dialog__content {
+    display: flex;
+    align-items: center;
+    justify-content: center;
   }
 }
+
+.tabbar-home-dialog {
+  z-index: 3002 !important;
+  overflow: unset;
+  background: transparent;
+}
+.container {
+  position: relative;
+  border-radius: 8px;
+  overflow: hidden;
+}
+.van-image {
+  max-width: 100%;
+}
+.icon-close {
+  pointer-events: none;
+  position: absolute;
+  top: -28px - 16px;
+  right: 0;
+  font-size: 28px;
+  color: #fff;
+}
 </style>

+ 13 - 21
apps/mobile/src/components/search/bidding/filters.vue

@@ -253,6 +253,7 @@
               <KeywordsInputGroup
                 :readonly="noLoginOrFree"
                 v-model="cacheMoreFilters.buyerList"
+                :maxTagCount="15"
                 inputMaxlength="30"
                 placeholder="输入采购单位名称,找其招标项目"
                 class="buyer-filter"
@@ -275,6 +276,7 @@
               <KeywordsInputGroup
                 :readonly="noLoginOrFree"
                 v-model="cacheMoreFilters.winnerList"
+                :maxTagCount="15"
                 inputMaxlength="30"
                 placeholder="输入中标企业名称,找其中标项目"
                 class="winner-filter"
@@ -297,6 +299,7 @@
               <KeywordsInputGroup
                 :readonly="noLoginOrFree"
                 v-model="cacheMoreFilters.agencyList"
+                :maxTagCount="15"
                 inputMaxlength="30"
                 placeholder="输入代理机构名称,找其代理项目"
                 class="winner-filter"
@@ -762,28 +765,16 @@ export default {
           sameList.push(deepCompare(this.filters.winnerConcat, winnerConcat))
           sameList.push(deepCompare(this.filters.buyerConcat, buyerConcat))
           sameList.push(deepCompare(this.filters.notKey, notKey))
-          sameList.push(
-            deepCompare(this.filters.winnerConcat, winnerConcat)
-          )
-          sameList.push(
-            deepCompare(this.filters.buyerConcat, buyerConcat)
-          )
-          sameList.push(
-            deepCompare(this.filters.notKey, notKey)
-          )
+          sameList.push(deepCompare(this.filters.winnerConcat, winnerConcat))
+          sameList.push(deepCompare(this.filters.buyerConcat, buyerConcat))
+          sameList.push(deepCompare(this.filters.notKey, notKey))
 
           // 采购单位
-          sameList.push(
-            deepCompare(this.filters.buyerList, buyerList)
-          )
+          sameList.push(deepCompare(this.filters.buyerList, buyerList))
           // 中标单位
-          sameList.push(
-            deepCompare(this.filters.winnerList, winnerList)
-          )
+          sameList.push(deepCompare(this.filters.winnerList, winnerList))
           // 代理机构
-          sameList.push(
-            deepCompare(this.filters.agencyList, agencyList)
-          )
+          sameList.push(deepCompare(this.filters.agencyList, agencyList))
 
           // 附件
           sameList.push(this.filters.fileExists === fileExists)
@@ -949,6 +940,7 @@ export default {
             this.cacheMoreFilters.priceCheckbox = priceCheckbox
             filters.priceCheckbox = priceCheckbox
             priceSelector?.setState(price)
+            filters.price = JSON.parse(JSON.stringify(price))
           }
 
           // 时间
@@ -1066,7 +1058,7 @@ export default {
             const priceState = priceSelector?.getState()
             filters.price = priceState
           } else {
-            filters.price = defaultPrice
+            filters.price = JSON.parse(JSON.stringify(defaultPrice))
           }
           filters.priceCheckbox = priceCheckbox
 
@@ -1100,9 +1092,9 @@ export default {
       this.$emit('confirm', { type })
       this.$refs[`${type}Dropdown`]?.toggle(false)
     },
-    onChange(value) {
+    onChange(value = {}) {
       const filters = {}
-      Object.assign(filters, this.filters, value)
+      Object.assign(filters, JSON.parse(JSON.stringify(this.filters)), value)
       this.$emit('change', filters)
     },
     popupState(type, state = false) {

+ 7 - 32
apps/mobile/src/components/treasure-box/CommonUse.vue

@@ -82,35 +82,13 @@ export default {
     getCommonUseList() {
       const _this = this
       this.loading = true
-      workspaceCommonUse('list', { platform: this.$env.platform?.toUpperCase() }).then(res => {
-        const { data = [], error_code: code } = res
-        if (code === 0 && data) {
-          const maxNum = data.num || 8
-          _this.commonList = data.list || []
-          _this.commonList.forEach(temp => {
-            // 计算背景色
-            calcImgThemeColor(temp.icon).then(({ color }) => {
-              // rgba转hex
-              const hex = rgb2Hex(color)
-              const { rgba } = hexToRgba(hex, 0.08)
-              this.$set(temp, 'themeColor', rgba)
-            })
-            if (_this.isSettings) {
-              temp.canAdd = false
-            }
-          })
-          // 初始化常用组件
-          _this.$store.dispatch('treasureBox/setCommonFunction', _this.commonList)
-          _this.$store.dispatch('treasureBox/setCommonMaxNum', maxNum)
-        }
-      }).finally(() => {
-        this.loading = false
-      })
+      workspaceCommonUse('list', { platform: this.$env.platform?.toUpperCase() })
         .then((res) => {
           const { data = [], error_code: code } = res
           if (code === 0 && data) {
-            _this.commonList = data
-            _this.commonList.forEach((temp) => {
+            const maxNum = data.num || 8
+            _this.commonList = data.list || []
+            _this.commonList.forEach(temp => {
               // 计算背景色
               calcImgThemeColor(temp.icon).then(({ color }) => {
                 // rgba转hex
@@ -123,13 +101,10 @@ export default {
               }
             })
             // 初始化常用组件
-            _this.$store.dispatch(
-              'treasureBox/setCommonFunction',
-              _this.commonList
-            )
+            _this.$store.dispatch('treasureBox/setCommonFunction', _this.commonList)
+            _this.$store.dispatch('treasureBox/setCommonMaxNum', maxNum)
           }
-        })
-        .finally(() => {
+        }).finally(() => {
           this.loading = false
         })
     },

+ 3 - 0
apps/mobile/src/views/article/content.vue

@@ -122,6 +122,7 @@
       v-model="popup.thirdPartyVerify"
       :beforeLeavePage="beforeLeavePage"
     />
+    <CheckUserDialog />
     <appShareSheet
       v-model="shareShow"
       @share="calcAppShareInfo"
@@ -164,6 +165,7 @@ import AdSingle from '@/components/ad/Ad.vue'
 import FreeUserBiddingMask from '@/views/article/components/FreeUserBiddingMask.vue'
 import FreeUserAdvancedMask from '@/views/article/components/FreeUserAdvancedMask.vue'
 import ThirdPartyVerifyPopup from '@/views/article/components/ThirdPartyVerifyPopup.vue'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import { throttle } from 'lodash'
 import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
 import { getArticleShareInfo, getContentShareEncrypt } from '@/api/modules/article'
@@ -192,6 +194,7 @@ export default {
     FreeUserBiddingMask,
     FreeUserAdvancedMask,
     ThirdPartyVerifyPopup,
+    CheckUserDialog,
     NpsCard,
     TabActions,
     AdSingle

+ 106 - 0
apps/mobile/src/views/identity/components/CheckPowerAndSwitch.vue

@@ -0,0 +1,106 @@
+<template>
+  <div class="check-power-and-switch"></div>
+</template>
+<script>
+import { getUserHighestPowerIdentity } from '@/api/modules/ent'
+import { changeUserIdentity } from '@/api/modules/mine'
+
+export default {
+  name: 'CheckPowerAndSwitch',
+  data() {
+    return {
+      targetIdentity: {
+        currentIdentity: '',
+        entId: '',
+        entName: '',
+        productType: '',
+        token: ''
+      }
+    }
+  },
+  computed: {
+    messageText() {
+      const { entName, productType, currentIdentity } = this.targetIdentity
+      const arr = [
+        '当前访问身份为',
+        `<span class="highlight-text">“${currentIdentity || ''}”</span>`,
+        ',系统识别您在',
+        `<span class="highlight-text">“${entName || ''}”</span>身份下有`,
+        `<span class="highlight-text">“${productType || ''}”</span>权限`,
+        ',建议您切换到该身份下使用剑鱼标讯。'
+      ]
+      return arr.join('')
+    }
+  },
+  created() {
+    this.getUserHighestIdentity()
+  },
+  methods: {
+    showDialog(conf = {}) {
+      const defaultConf = {
+        title: '',
+        message: '',
+        className: 'j-confirm-dialog',
+        messageAlign: 'left',
+        showCancelButton: false,
+        confirmButtonText: '我知道了'
+      }
+      Object.assign(defaultConf, conf)
+      return this.$dialog.confirm(defaultConf)
+    },
+    async showSwitchPowerDialog() {
+      try {
+        await this.showDialog({
+          title: '身份切换提醒',
+          message: this.messageText,
+          messageAlign: 'left',
+          showCancelButton: true,
+          confirmButtonText: '立即切换',
+          cancelButtonText: '暂不切换',
+          beforeClose: this.beforeClose
+        })
+      } catch (error) {
+        this.getUserHighestIdentity(true)
+      }
+    },
+    async beforeClose(action, done) {
+      if (action === 'confirm') {
+        await this.switchNow()
+        done()
+      } else {
+        done()
+      }
+    },
+    async getUserHighestIdentity(clear) {
+      let payload = {
+        isClear: clear ? true : undefined
+      }
+      const { error_code: code, data } = await getUserHighestPowerIdentity(payload)
+      if (code === 0 && data) {
+        this.targetIdentity = data
+        if (data.token) {
+          this.showSwitchPowerDialog(this.targetIdentity)
+        }
+      }
+    },
+    async switchNow() {
+      const p = this.targetIdentity
+      if (!p.token) return
+      try {
+        this.loading = true
+        const { error_code: code, data } = await changeUserIdentity({
+          token: p.token
+        })
+        if (code === 0 && data === 1) {
+          location.reload()
+        } else {
+          this.$toast('身份切换失败')
+        }
+        this.loading = false
+      } catch (error) {
+        this.loading = false
+      }
+    }
+  }
+}
+</script>

+ 43 - 0
apps/mobile/src/views/identity/components/CheckUserDialog.vue

@@ -0,0 +1,43 @@
+<template>
+  <div class="check-user-dialog" v-if="isLogin">
+    <CheckVipExpire @loaded="onVipCheckLoaded" @change="onVipCheckChange" />
+    <CheckPowerAndSwitch v-if="checkPowerShow" />
+  </div>
+</template>
+<script>
+import CheckPowerAndSwitch from '@/views/identity/components/CheckPowerAndSwitch'
+import CheckVipExpire from '@/views/identity/components/CheckVipExpire'
+import { mapGetters } from 'vuex'
+
+export default {
+  name: 'CheckUserDialog',
+  components: {
+    CheckPowerAndSwitch,
+    CheckVipExpire
+  },
+  data() {
+    return {
+      vipCheck: {
+        loaded: false,
+        show: false
+      }
+    }
+  },
+  computed: {
+    ...mapGetters('user', ['isLogin']),
+    checkPowerShow() {
+      const { loaded, show } = this.vipCheck
+      return loaded && !show
+    }
+  },
+  methods: {
+    onVipCheckLoaded({ show }) {
+      this.vipCheck.loaded = true
+      this.vipCheck.show = show
+    },
+    onVipCheckChange({ show }) {
+      this.vipCheck.show = show
+    }
+  }
+}
+</script>

+ 275 - 0
apps/mobile/src/views/identity/components/CheckVipExpire.vue

@@ -0,0 +1,275 @@
+<template>
+  <van-dialog
+    class="check-vip-expire"
+    v-model="show"
+    closeOnClickOverlay
+    get-container="body"
+    @close="closeDialog"
+    :show-confirm-button="false"
+  >
+    <AppIcon name="close" />
+    <div class="check-vip-expire-container">
+      <div class="check-vip-expire-header" v-html="titleText"></div>
+      <div class="check-vip-expire-footer">
+        <div
+          class="text-container"
+          :class="{ 'expire-text-center': !activityText }"
+        >
+          <p>为避免遗漏重大项目,请及时续费</p>
+          <p class="activity-tip" v-if="activityText">{{ activityText }}</p>
+        </div>
+        <button class="confirm-button clickable" @click="rechargeNow">
+          立即续费
+        </button>
+      </div>
+    </div>
+  </van-dialog>
+</template>
+
+<script>
+import { AppIcon } from '@/ui'
+import { Dialog } from 'vant'
+import { vipRenewReminder } from '@/api/modules/'
+
+export default {
+  name: 'CheckVipExpire',
+  components: {
+    [AppIcon.name]: AppIcon,
+    [Dialog.Component.name]: Dialog.Component
+  },
+  data() {
+    return {
+      show: false,
+      pInfo: {
+        activityInfo: {},
+        endDays: 0
+      }
+    }
+  },
+  computed: {
+    titleText() {
+      const { endDays } = this.pInfo
+      const t = endDays
+      let arr = []
+      if (t <= 1) {
+        arr = [
+          '您的超级订阅将于',
+          '<strong class="time"> 今天 </strong>',
+          '到期'
+        ]
+      } else {
+        arr = [
+          '您的超级订阅还有',
+          `<strong class="time"> ${t}天 </strong>`,
+          '到期'
+        ]
+      }
+      return arr.join('')
+    },
+    activityText() {
+      const { activityInfo } = this.pInfo
+      if (!activityInfo) return ''
+      const { activity } = activityInfo
+      if (!activity) return ''
+      let arr = []
+      if (Array.isArray(activity) && activity.length >= 1) {
+        const activity1 = activity[0]
+        const discount = activity1.discount
+        if (Array.isArray(discount) && discount.length >= 1) {
+          arr.push('限时活动:')
+          const discount1 = discount[0]
+          // type 0满减、1折扣券、2满赠、3促销、4限时折扣、5限时减免
+          // type  035满减  14满折  2满赠
+          const lotteryType = discount1.type
+          if (lotteryType === 0 || lotteryType === 3 || lotteryType === 5) {
+            // 满减:续费最高立减1999元(变量,小数不展示,不四舍五入)
+            const reduce = discount1.reduce
+            const text = `续费最高立减${reduce}元`
+            arr.push(text)
+          } else if (lotteryType === 1 || lotteryType === 4) {
+            // 满折:续费1年(变量)立享6折(变量)优惠
+            const zhe = Math.round(discount1.discount * 10 * 10) / 10
+            const text = `续费${activityInfo.info}立享${zhe}折优惠`
+            arr.push(text)
+          } else if (lotteryType === 2) {
+            // 满赠:现在续费1年(变量)再送1年(注“12个月”按照1年展示)
+            // timeType 时间类型:1/天、2/月,3/年
+            const zengTime = discount1.time
+            const zengTimeType = discount1.timeType
+            const timeTypeMap = {
+              1: '天',
+              2: '个月',
+              3: '年'
+            }
+            const suffixText = timeTypeMap[zengTimeType]
+            let zeng = `${zengTime}${suffixText}`
+            if (zeng === '12个月') {
+              zeng = '1年'
+            }
+            const text = `现在续费${activityInfo.info}再送${zeng}`
+            arr.push(text)
+          }
+        }
+      }
+      return arr.join('')
+    }
+  },
+  created() {
+    this.getVipRenewReminder()
+  },
+  methods: {
+    initNextDialog() {
+      this.$emit('initNext')
+    },
+    showDialog() {
+      this.show = true
+      this.onChange()
+    },
+    async closeDialog(type = false) {
+      this.show = false
+      this.$nextTick(() => {
+        this.onChange()
+      })
+      if (!type) {
+        // 弹窗关闭,可以初始化下个弹窗
+        this.initNextDialog()
+      }
+      await this.getVipRenewReminder(true)
+    },
+    onChange() {
+      this.$emit('change', { show: this.show })
+    },
+    loaded() {
+      this.$emit('loaded', { show: this.show })
+    },
+    async getVipRenewReminder(clear) {
+      let payload = {
+        clearRedis: clear ? true : undefined
+      }
+      try {
+        const { data } = await vipRenewReminder(payload)
+        if (data && data.endDays !== undefined) {
+          this.pInfo = data
+          if (data.endDays >= 0) {
+            this.showDialog()
+          } else {
+            // 未满足展示条件,可以初始化下个弹窗
+            if (!clear) {
+              this.initNextDialog()
+            }
+          }
+        } else {
+          // 未满足展示条件,可以初始化下个弹窗
+          if (!clear) {
+            this.initNextDialog()
+          }
+        }
+      } catch (error) {
+        console.log(error)
+        // 未满足展示条件,可以初始化下个弹窗
+        if (!clear) {
+          this.initNextDialog()
+        }
+      } finally {
+        if (!clear) {
+          this.loaded()
+        }
+      }
+    },
+    async rechargeNow() {
+      this.closeDialog(true)
+      this.$router.push({
+        path: '/common/order/create/svip',
+        query: {
+          type: 'renew'
+        }
+      })
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+::v-deep {
+  .van-dialog__content {
+    height: 100%;
+  }
+}
+
+.check-vip-expire {
+  width: 326px;
+  height: 364px;
+  background: transparent url(@/assets/image/tip/vip-expire-dialog-bg.png)
+    no-repeat center;
+  background-size: contain;
+  overflow: unset;
+
+  .icon-close {
+    pointer-events: none;
+    position: absolute;
+    top: -28px - 16px;
+    right: 0;
+    font-size: 28px;
+    color: #fff;
+  }
+}
+.check-vip-expire-container {
+  position: relative;
+  height: 100%;
+}
+
+.check-vip-expire-header,
+.check-vip-expire-footer {
+  position: absolute;
+  left: 0;
+  right: 0;
+  font-size: 16px;
+  line-height: 24px;
+  color: #5f391a;
+  text-align: center;
+  padding: 0 20px;
+}
+
+.check-vip-expire-header {
+  top: 80px;
+  ::v-deep {
+    .time {
+      font-size: 18px;
+    }
+  }
+}
+.check-vip-expire-footer {
+  bottom: 18px;
+}
+.text-container {
+  margin-bottom: 8px;
+  &.expire-text-center {
+    margin-bottom: 20px;
+  }
+}
+
+.activity-tip {
+  margin-top: 4px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 2px 0;
+  font-size: 14px;
+  line-height: 20px;
+  color: #fff;
+  border-radius: 4px;
+  background: linear-gradient(90deg, #ff9347 0%, #ff4236 100%);
+}
+
+.confirm-button {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  padding: 8px 0;
+  font-size: 16px;
+  line-height: 24px;
+  color: #fae7ca;
+  border-radius: 8px;
+  background: linear-gradient(117.49deg, #3e3835 8.4%, #242120 100%);
+}
+</style>

+ 3 - 0
apps/mobile/src/views/search/middle/bidding/index.vue

@@ -45,12 +45,14 @@
         </div>
       </div>
     </div>
+    <CheckUserDialog />
   </div>
 </template>
 
 <script>
 import { HistoryList, AppIcon } from '@/ui'
 import HotKeyCard from '@/components/search/middle/HotKeyCard.vue'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import {
   getBiddingSearchHistory,
   getBiddingFilterList,
@@ -73,6 +75,7 @@ export default {
   components: {
     AppIcon,
     HotKeyCard,
+    CheckUserDialog,
     HistoryList
   },
   inject: {

+ 148 - 47
apps/mobile/src/views/search/result/bidding/index.vue

@@ -2,6 +2,7 @@
   <div class="j-container search-result-bidding top-radius">
     <div
       class="bidding-advanced-switch"
+      v-if="conf.searchGroupList.length"
       :class="{ 'loading-mask': listState.loading }"
     >
       <van-tabs
@@ -30,7 +31,7 @@
       <!-- 筛选器 -->
       <BiddingSearchFilters
         :bidColPower="pageState.bidColPower"
-        class="top-radius "
+        class="top-radius"
         :class="{ 'loading-mask': listState.loading }"
         ref="searchFilters"
         @noPower="onNoPower"
@@ -120,6 +121,25 @@
             </div>
           </div>
         </div>
+        <div
+          class="tip-toggle-search-mode-container"
+          v-show="toggleBlurModeTip.show"
+        >
+          <div>
+            如需查看更多相关信息,建议您将搜索模式切换为
+            <div class="tip-action highlight-text">
+              “模糊搜索”
+              <AppIcon
+                class="search-mode-label-icon"
+                name="help1"
+                @click="showSearchModeHelp"
+              >
+              </AppIcon>
+            </div>
+            按照当前条件共匹配到{{ toggleBlurModeTip.count }}条公告。
+          </div>
+          <button @click="doToggleSearchBlurMode">立即切换查看</button>
+        </div>
         <div class="list-wrapper" ref="listWrapper">
           <van-popup
             :style="popupHeight"
@@ -514,7 +534,7 @@ export default {
             id: '5',
             title: '招标结果',
             name: 'zbjg'
-          },
+          }
         ],
         tabListFree: [
           {
@@ -656,7 +676,11 @@ export default {
       },
       showBidStatus: false,
       popupHeight: 'height: 440px',
-      projectCellInfo: {}
+      projectCellInfo: {},
+      toggleSearchBlurData: {
+        show: false,
+        count: ''
+      }
     }
   },
   computed: {
@@ -835,6 +859,19 @@ export default {
      */
     inInjectBI() {
       return this.$route.name === 'search-bi-bidding'
+    },
+    // 切换模糊搜索
+    toggleBlurModeTip() {
+      const isBlurMode = this.filters.searchMode.indexOf('1') !== -1
+      let canShow = isBlurMode ? false : this.toggleSearchBlurData.show
+      if (this.listState.loading) {
+        canShow = false
+      }
+      const result = {
+        show: canShow,
+        count: this.toggleSearchBlurData.count
+      }
+      return result
     }
   },
   provide() {
@@ -925,20 +962,35 @@ export default {
     formatMoney,
     dateFormatter,
     replaceKeyword,
+    // 切换到模糊搜索
+    doToggleSearchBlurMode() {
+      this.switchSearchMode(1)
+      this.doSearch()
+    },
+    async showSearchModeHelp() {
+      return await this.$dialog.alert({
+        title: '搜索模式',
+        messageAlign: 'left',
+        className: 'j-confirm-dialog',
+        confirmButtonText: '我知道了',
+        message:
+          '<p>精准搜索: 搜索结果必须完全包含完整的关键词。如搜索"医疗设备" ,搜索结果一定完整包含“医疗设备”才能被搜索到,而“医疗的设备”或“设备医疗”的项目不会被搜索到。</p><br /><p>模糊搜索: 系统会先自动智能分词然后再进行搜索。如搜索"医疗设备" ,系统会自动分成“医疗”“设备”然后进行搜索,只要项目中出现“医疗”和“设备”都会被搜索到,前提是两个词必须一同出现在一则公告内,不分先后顺序。</p>'
+      })
+    },
     initBiHeaderTab() {
       // 山川应用下隐藏非招标结果的信息类型
       // 仅保留['全部', '招标结果', '招标信用信息']
-      this.searchGroupList = [
+      this.conf.searchGroupList = [
         {
           id: '0',
           title: '全部',
           name: ''
-        },
-        {
-          id: '5',
-          title: '招标结果',
-          name: 'zbjg'
-        },
+        }
+        // {
+        //   id: '5',
+        //   title: '招标结果',
+        //   name: 'zbjg'
+        // }
       ]
     },
     /**
@@ -1003,7 +1055,7 @@ export default {
       }
     },
     initDefaultFilterState() {
-      Object.assign(this.filters, this.defaultFilterState)
+      Object.assign(this.filters, JSON.parse(JSON.stringify(this.defaultFilterState)))
       this.$nextTick(() => {
         this.filters.keywords = this.topSearch.input
       })
@@ -1015,11 +1067,13 @@ export default {
     },
     getQueryString() {
       const { searchGroup } = this.$route.query
-      const searchGroupMap = this.conf.searchGroupList.map((s) => s.id)
-      if (searchGroup && searchGroupMap.includes(searchGroup)) {
-        this.pageState.searchGroup = searchGroup
-      } else {
-        this.pageState.searchGroup = this.conf.searchGroupList[0].id
+      if (this.conf.searchGroupList.length) {
+        const searchGroupMap = this.conf.searchGroupList.map((s) => s.id)
+        if (searchGroup && searchGroupMap.includes(searchGroup)) {
+          this.pageState.searchGroup = searchGroup
+        } else {
+          this.pageState.searchGroup = this.conf.searchGroupList[0].id
+        }
       }
     },
     calcInitFilters() {
@@ -1499,25 +1553,15 @@ export default {
             this.$toast('精准搜索无结果,已为您自动切换到模糊搜索')
           }
         }
-      } else {
-        // 搜索结果无数据,自动切换搜索模式到模糊搜索
-        /* 切换搜索模式逻辑 start */
-        if (params.pageNum === 1) {
-          if (params.searchMode === 0) {
-            this.switchSearchMode(1)
-            this.pageState.searchModeAutoChangedSearch = true
-            this.doSearch({ from: 'searchModeAutoChangedSearch' })
-          } else if (params.searchMode === 1) {
-            if (this.pageState.searchModeAutoChangedSearch) {
-              // 自动切换搜索模式搜索后还没查到数据,就改回精准搜索
-              this.switchSearchMode(0)
-              this.pageState.searchModeAutoChangedSearch = false
-            }
-          }
-        }
-        /* 切换搜索模式逻辑 end */
       }
 
+      this.checkToggleSearchMode({
+        pageNum: params.pageNum,
+        count: data.total || 0,
+        blurCount: data?.bCount || 0,
+        searchMode: params.searchMode
+      })
+
       if (params.pageNum === 1) {
         // 保存更多关键词到历史记录中
         this.saveAdditionalWordsToHistory()
@@ -1527,6 +1571,40 @@ export default {
         }
       }
     },
+    /**
+     * 检查是否需要切换模糊搜索、是否展示提示
+     * 1. 精准搜索无数据 (自动切换模糊搜索)
+     * 2. 精准搜索有数据,< 50,提示手动切换搜索模式
+     */
+    checkToggleSearchMode({
+      pageNum = 1,
+      searchMode = 0,
+      count = 0,
+      blurCount = 0
+    }) {
+      if (pageNum === 1) {
+        // 重置变量
+        this.toggleSearchBlurData.show = false
+        this.toggleSearchBlurData.count = ''
+
+        if (searchMode === 0) {
+          const canAutoToggleBlurMode = count < 1 && blurCount > count
+          const canShowToggleBlurModeTip =
+            count >= 1 && count < 50 && blurCount > count
+
+          if (canShowToggleBlurModeTip) {
+            this.toggleSearchBlurData.show = true
+            this.toggleSearchBlurData.count = blurCount
+          }
+
+          if (canAutoToggleBlurMode) {
+            this.switchSearchMode(1)
+            this.pageState.searchModeAutoChangedSearch = true
+            this.doSearch({ from: 'searchModeAutoChangedSearch' })
+          }
+        }
+      }
+    },
     // 切换搜索模式:精准搜索0/模糊搜索1
     switchSearchMode(m) {
       this.filters.searchMode = [m + '']
@@ -1610,7 +1688,8 @@ export default {
       }
       // 是否有附件
       item.isFile = item?.fileExists || false
-      item.leftTopBadgeText = item.site === '剑鱼信息发布平台' ? '业主委托项目' : ''
+      item.leftTopBadgeText =
+        item.site === '剑鱼信息发布平台' ? '业主委托项目' : ''
       // 拟建项目独有参数
       if (projectInfo) {
         Object.assign(item, projectInfo)
@@ -2176,7 +2255,7 @@ export default {
         done()
       }
     },
-    formatFilterItems (item) {
+    formatFilterItems(item) {
       item.scope = item.selectType
       item.infotype = item.subtype
       const formatted = FilterHistoryAjaxModel2ViewModel.formatAll(item)
@@ -2274,6 +2353,7 @@ export default {
           listState: this.listState,
           pageState: this.pageState,
           BIInfo: this.BIInfo,
+          toggleSearchBlurData: this.toggleSearchBlurData,
           recommendInfo: this.recommendInfo
         },
         { storage: sessionStorage }
@@ -2374,16 +2454,15 @@ export default {
       if (replace && Object.keys(replace).length) {
         Object.assign(params, replace)
       }
-      this.$storage.set(
-        BIDDING_SEARCH_LAST_FILTERS_CACHE_KEY,
-        params,
-        {
-          login: true
-        }
-      )
+      this.$storage.set(BIDDING_SEARCH_LAST_FILTERS_CACHE_KEY, params, {
+        login: true
+      })
     },
     restoreSearchGroupFromLocal() {
-      const params = this.$storage.get(BIDDING_SEARCH_GROUP_LAST_CACHE_KEY, false)
+      const params = this.$storage.get(
+        BIDDING_SEARCH_GROUP_LAST_CACHE_KEY,
+        false
+      )
       if (params) {
         this.pageState.searchGroup = params.searchGroup
       }
@@ -2392,10 +2471,7 @@ export default {
       const params = {
         searchGroup: this.pageState.searchGroup
       }
-      this.$storage.set(
-        BIDDING_SEARCH_GROUP_LAST_CACHE_KEY,
-        params
-      )
+      this.$storage.set(BIDDING_SEARCH_GROUP_LAST_CACHE_KEY, params)
     },
     beforeTabActiveChange(name) {
       if (name === 'detailedList') {
@@ -2528,6 +2604,31 @@ export default {
 }
 </style>
 <style lang="scss" scoped>
+.tip-toggle-search-mode-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  margin: 8px 16px;
+  margin-top: 0;
+  padding: 12px 16px;
+  background: linear-gradient(180deg, #e8ffff 0%, #ffffff 100%);
+  border: 0.5px solid #2abed1;
+  border-radius: 8px;
+  color: #5f5e64;
+  font-size: 13px;
+  line-height: 20px;
+  .tip-action {
+    display: inline-flex;
+    align-items: center;
+  }
+  button {
+    margin-top: 12px;
+    background: #2abed1;
+    padding: 5px 12px;
+    border-radius: 4px;
+    color: #f7f9fa;
+  }
+}
 .search-result-bidding {
   background: #f5f5f5;
   ::v-deep {

+ 3 - 0
apps/mobile/src/views/tabbar/Box.vue

@@ -39,6 +39,7 @@
         <all-use :all-need-badge="false" @openLink="openLink" />
       </div>
     </div>
+    <CheckUserDialog />
   </div>
 </template>
 <script>
@@ -48,6 +49,7 @@ import Swiper from '@/components/treasure-box/Swiper'
 import RecommendList from '@/components/treasure-box/Recommend'
 import CommonUse from '@/components/treasure-box/CommonUse'
 import AllUse from '@/components/treasure-box/AllUse'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import { openLinkOfOther, openAppOrWxPage } from '@/utils'
 import { LINKS, AdCode } from '@/data'
 import { mapGetters } from 'vuex'
@@ -65,6 +67,7 @@ export default {
     CommonUse,
     AllUse,
     RecommendList,
+    CheckUserDialog,
     [AppIcon.name]: AppIcon
   },
   data() {

+ 15 - 2
apps/mobile/src/views/tabbar/Home.vue

@@ -52,12 +52,14 @@
     <ad-side :config="AD.side" :scroll-status="sideStatus"></ad-side>
     <!-- 弹窗广告位 -->
     <AdPopScreen
-      v-if="newUserPopConfig.showNextFullPop && AD.full.pic"
+      v-if="AD.full.pic"
+      v-show="newUserPopConfig.showNextFullPop"
       cache-key="pop-screen"
       :config="AD.full"
       @close="checkShowMessagePop(true)"
       @load="toggleMessagePopShow(false)"
       @destroyed="checkShowMessagePop(true)"
+      @initNext="onInitNextDialog"
     />
     <!-- 新注册用户活动广告位 -->
     <AdPopScreen
@@ -67,6 +69,7 @@
       @close="closeNewUserPop"
       @load="toggleMessagePopShow(false)"
     />
+    <CheckUserDialog v-if="dialog.checkUserShow" />
     <!-- 新注册用户底部悬浮广告位 -->
     <!-- <div class="new-user--pop-bottom" :class="{ 'has-wx': $envs.inWX }" v-if="isLogin && newUserPopConfig.showBottomPop && AD.newUserPopOfBottom.pic">
       <ad-single class="subscribe-top-box" :config="AD.newUserPopOfBottom"  :before-open="beforeOpenNewUserAD"  :show-tag="false" @close="closeNewUserPopOfBottom" @load="toggleMessagePopShow(false)"></ad-single>
@@ -90,6 +93,7 @@ import {
 } from '@/api/modules'
 import MessageCard from '@/components/message/message-card'
 import { adConfigFormatter } from '@/utils/format/modules/ad-formatter'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import {
   transferMethodsOfRefs,
   openLinkOfOther,
@@ -120,6 +124,7 @@ export default {
     MessageCard,
     Swipe,
     SwipeFloor,
+    CheckUserDialog,
     [Side.name]: Side,
     [Button.name]: Button,
     [Cell.name]: Cell,
@@ -174,6 +179,9 @@ export default {
     cacheState: {
       toSearch: false
     },
+    dialog: {
+      checkUserShow: false
+    },
     sideStatus: true,
     newUserPopConfig: {
       showNextFullPop: false,
@@ -254,6 +262,10 @@ export default {
     })
   },
   methods: {
+    onInitNextDialog() {
+      console.log('==========')
+      this.dialog.checkUserShow = true
+    },
     showNoLoginHeader() {
       if (!this.isLogin && this.$envs.inAppOrH5) {
         this.$route.meta.header = true
@@ -457,9 +469,10 @@ export default {
             } else {
               this.AD.header = {}
             }
-            if (full.length) {
+            if (full?.length) {
               this.AD.full = full.map(adConfigFormatter)[0]
             } else {
+              this.onInitNextDialog()
               this.AD.full = {}
             }
             if (side.length) {

+ 3 - 0
apps/mobile/src/views/tabbar/Layout.vue

@@ -19,6 +19,7 @@
         </div>
       </van-overlay>
       <router-view></router-view>
+      <!-- <CheckUserDialog /> -->
     </div>
     <!-- 此处必须用v-if,因为footerTabbar的mounted中清除了app的历史记录 -->
     <div class="j-footer" v-show="getFooterShow">
@@ -30,6 +31,7 @@
 <script>
 import { Overlay } from 'vant'
 import FooterTabbar from '@/components/footer-tabbar'
+// import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import { callHideTab } from '@/utils'
 import { mapActions, mapGetters } from 'vuex'
 import { ajaxGetTipInfo } from '@/api/modules'
@@ -47,6 +49,7 @@ const beforeunload = function () {
 export default {
   name: 'TabbarLayout',
   components: {
+    // CheckUserDialog,
     [FooterTabbar.name]: FooterTabbar,
     [Overlay.name]: Overlay
   },

+ 3 - 0
apps/mobile/src/views/tabbar/Message.vue

@@ -79,6 +79,7 @@
         <p>暂无消息</p>
       </AppEmpty>
     </div>
+    <CheckUserDialog />
   </div>
 </template>
 
@@ -96,6 +97,7 @@ import { callChangeTab, checkNowInAppTabbarPage } from '@/utils/callFn/'
 import { appCallReloadTab } from '@/utils/callFn/appFn'
 import { MESSAGE } from '@/data'
 import { AppEmpty, AppIcon } from '@/ui'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import { openLinkOfOther, iosBackRefresh } from '@/utils'
 import { mapState, mapActions } from 'vuex'
 
@@ -104,6 +106,7 @@ export default {
   components: {
     [Badge.name]: Badge,
     [Icon.name]: Icon,
+    CheckUserDialog,
     AppEmpty,
     AppIcon
   },

+ 3 - 0
apps/mobile/src/views/tabbar/Mine.vue

@@ -8,6 +8,7 @@
       <!--菜单列表-->
       <mine-list />
     </div>
+    <CheckUserDialog />
   </div>
 </template>
 
@@ -18,6 +19,7 @@ import MineList from '@/components/mine/MineList'
 import store from '@/store'
 import { appCallBackTab } from '@/utils/callFn/appFn'
 import { envs } from '@/utils/prototype/modules/platform'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import { callHideRedSpotOnMenu } from '@/utils'
 
 export default {
@@ -25,6 +27,7 @@ export default {
   components: {
     MineHeader,
     SignIn,
+    CheckUserDialog,
     MineList
   },
   async beforeRouteEnter(to, from, next) {

+ 3 - 0
apps/mobile/src/views/tabbar/Subscribe.vue

@@ -474,6 +474,7 @@
       ref="popup_dataExport"
       @next="next_export"
     ></popupDataexport>
+    <CheckUserDialog />
   </div>
 </template>
 
@@ -500,6 +501,7 @@ import DataReportTip from '@/components/subscribe/DataReportTip'
 import RecommendCard from '@/components/recommend/'
 import Ad from '@/components/ad/Ad'
 import popupDataexport from '@/components/dataExport/popupDataexport.vue'
+import CheckUserDialog from '@/views/identity/components/CheckUserDialog'
 import { LINKS, vtMap, AdCode } from '@/data'
 import { wxShareMixin } from '@/utils/mixins/modules/wx-share'
 import { iosBackRefresh } from '@/utils/utils'
@@ -567,6 +569,7 @@ export default {
     [DropFilter.name]: DropFilter,
     [DatetimePicker.name]: DatetimePicker,
     [IndustrySidebar.name]: IndustrySidebar,
+    CheckUserDialog,
     RecommendCard,
     bidStatusNode,
     popupDataexport,

+ 15 - 9
data/data-models/modules/article/model/content.js

@@ -2,7 +2,7 @@ import BaseModel from '../../../core/base'
 import useSummaryModel from '../transform/summary2'
 import useCommonTitleModel from '../transform/content'
 import useThirdPartyVerifyModel from '../transform/third-party-verify'
-import { replaceKeyword } from '@jy/util'
+import { replaceKeywordWithRichText } from '@jy/util'
 const thirdPartyVerify = useThirdPartyVerifyModel()
 
 class ContentModel extends BaseModel {
@@ -42,7 +42,7 @@ class ContentModel extends BaseModel {
       try {
         result.content.contentHighlighted = this.highlightContentHTML(result.content.content, data, result)
       } catch (error) {
-        console.error(error)        
+        console.error(error)
       }
     }
 
@@ -61,10 +61,10 @@ class ContentModel extends BaseModel {
     const projectCode = baseInfo?.projectCode
     // 下划线高亮项目名称编号
     if (projectName && title.toLowerCase().indexOf(projectName.toLowerCase()) > -1) {
-      title = replaceKeyword(title, projectName, '<span class="keyword keyword-underline project project-name hide-underline">$1</span>')
+      title = replaceKeywordWithRichText(title, projectName, '<span class="keyword keyword-underline project project-name hide-underline">$1</span>')
     }
     if (projectCode && title.toLowerCase().indexOf(projectCode.toLowerCase()) > -1) {
-      title = replaceKeyword(title, projectCode, '<span class="keyword keyword-underline project project-code">$1</span>')
+      title = replaceKeywordWithRichText(title, projectCode, '<span class="keyword keyword-underline project project-code">$1</span>')
     }
 
     // ------------------
@@ -74,7 +74,7 @@ class ContentModel extends BaseModel {
       highlightKeys = formatted.content.highlightKeys
     }
     highlightKeys.forEach((key) => {
-      title = replaceKeyword(title, key, '<span class="keyword highlight-text">$1</span>')
+      title = replaceKeywordWithRichText(title, key, '<span class="keyword highlight-text">$1</span>')
     })
     return title
   }
@@ -89,12 +89,18 @@ class ContentModel extends BaseModel {
 
     content = content.replace(/[^\{\u4e00-\u9fa5]{1,90}{[^\}\u4e00-\u9fa5]+?}/g, '').trim()
 
+    let tempNode = document.createElement('div')
+    tempNode.innerHTML = content
+
+
+
+
     // 下划线高亮项目名称编号
     if(projectName && content.toLowerCase().indexOf(projectName.toLowerCase()) > -1){
-      content = replaceKeyword(content, projectName, '<span class="keyword keyword-underline my-follow project project-name hide-underline">$1</span>')
+      content = replaceKeywordWithRichText(content, projectName, '<span class="keyword keyword-underline my-follow project project-name hide-underline">$1</span>')
     }
     if(projectCode && content.toLowerCase().indexOf(projectCode.toLowerCase()) > -1){
-      content = replaceKeyword(content, projectCode, '<span class="keyword keyword-underline my-follow project project-code">$1</span>')
+      content = replaceKeywordWithRichText(content, projectCode, '<span class="keyword keyword-underline my-follow project project-code">$1</span>')
     }
     // 下划线高亮中标企业
     const winners = summary.winners
@@ -103,7 +109,7 @@ class ContentModel extends BaseModel {
         const winnerName = winners[i].name
         const winnerId = winners[i].id
         if (winnerName && content.toLowerCase().indexOf(winnerName.toLowerCase()) > -1) {
-          content = replaceKeyword(content, winnerName, `<span data-eid='${winnerId}' class='keyword keyword-underline winner-name my-follow-ent'>$1</span>`)
+          content = replaceKeywordWithRichText(content, winnerName, `<span data-eid='${winnerId}' class='keyword keyword-underline winner-name my-follow-ent'>$1</span>`)
         }
       }
     }
@@ -125,7 +131,7 @@ class ContentModel extends BaseModel {
       highlightKeys = formatted.content.highlightKeys
     }
     highlightKeys.forEach((key) => {
-      content = replaceKeyword(content, key, '<span class="keyword highlight-text">$1</span>')
+      content = replaceKeywordWithRichText(content, key, '<span class="keyword highlight-text">$1</span>')
     })
 
     // 将多个连续的br替换成一个

+ 0 - 1
data/data-models/modules/quick-search/plugins/search-bid.js

@@ -13,7 +13,6 @@ export default class SearchBidListApi extends SearchListApiBase {
     const type = params._expand.type
     delete params._expand
     return ajaxGetSearchBidList(type, params).then((res) => {
-      console.log(res, 'res')
       let success = res?.error_code === 0
 
       return {

+ 1 - 1
data/data-models/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@jy/data-models",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "description": "聚合API接口,对外提供业务数据模型",
   "main": "index.js",
   "files": [

+ 61 - 3
packages/util/modules/format/str.js

@@ -77,14 +77,14 @@ export function replaceKeyword(
   }
 
 
-  
+
   // 数组去空
   let lastArr = oldCharArr
     .filter((item) => !!item)
     .sort((a, b) => b.length - a.length)
   // 数组去重
   lastArr = Array.from(new Set(lastArr))
-  
+
   if (lastArr.length === 0 && notStrReplacer.length === 0) {
     return value
   }
@@ -105,6 +105,64 @@ export function replaceKeyword(
   }
 }
 
+/**
+ * 富文本高亮专用替换,用于指定关键词包裹高亮,使用dom解析避免HTML标签被影响
+ * @param htmlString - String
+ * @param keyword - String
+ * @param richChar - String $1
+ */
+export function replaceKeywordWithRichText (
+  htmlString,
+  keyword,
+  richChar = '<span class="highlight-text">$1</span>'
+) {
+  if (!keyword || !richChar || keyword === '略') return htmlString
+
+  // 创建一个临时的DOM元素来解析HTML字符串
+  const tempDiv = document.createElement('div')
+  tempDiv.innerHTML = htmlString
+
+  // 格式化需要替换的富文本
+  const richString = richChar.replace(/\$1/g, keyword)
+  const richTextNode = document.createElement('div')
+  richTextNode.innerHTML = richString;
+
+  function getRichNode () {
+    return richTextNode.firstChild.cloneNode(true)
+  }
+
+  // 递归函数来遍历DOM节点并替换文本
+  function replaceText(node) {
+    if (node.nodeType === Node.TEXT_NODE && node.textContent.includes(keyword)) {
+      // 创建一个文档片段来存放替换后的富文本和剩余文本
+      const frag = document.createDocumentFragment()
+      // 将文本分割为关键词前后的文本,以及替换的富文本
+      const parts = node.textContent.split(keyword)
+
+      for (let i = 0; i < parts.length; i++) {
+        // 关键词替换为富文本
+        if (parts.length > 1 && i !== 0) {
+          frag.appendChild(getRichNode());
+        }
+        // 其他文本正常添加
+        frag.appendChild(document.createTextNode(parts[i]))
+      }
+      // 替换原始文本节点
+      node.parentNode.replaceChild(frag, node)
+    } else if (node.nodeType === Node.ELEMENT_NODE) {
+      // 如果是元素节点,递归其子节点
+      Array.from(node.childNodes).forEach(replaceText)
+    }
+  }
+
+  // 从临时div开始递归替换文本
+  replaceText(tempDiv)
+
+  // 返回修改后的HTML字符串
+  return tempDiv.innerHTML
+
+}
+
 /**
  * 从key=value&key1=value1&key2=value2...中获取key值
  * @param {String} formString 目标字符串,必须为key=value&key1=value1的格式
@@ -125,7 +183,7 @@ export function getFormValue(formString, targetKey) {
 
 /**
  * 从url中获取对应name的参数
- * @param {String} name 
+ * @param {String} name
  * @returns {String}
  */
 export function getQueryParam(name) {

+ 1 - 1
packages/util/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@jy/util",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "description": "剑鱼项目通用工具集",
   "main": "index.js",
   "scripts": {

+ 8 - 0
plugins/login-auth/example/index.html

@@ -80,6 +80,14 @@
             toggleLoginDom(false)
           }
         })
+        window.jyCoreSDK.$on('user-set-pass-success', (data) => {
+          alert('设置密码成功')
+          location.reload()
+        })
+        window.jyCoreSDK.$on('user-not-set-pass', (data) => {
+          alert('暂不设置密码')
+          location.reload()
+        })
       })
     </script>
   </body>

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