Переглянути джерело

Merge branch 'main' into feature/v1.0.78

yuelujie 8 місяців тому
батько
коміт
dfe20b5bc1
100 змінених файлів з 15377 додано та 189 видалено
  1. 10 0
      apps/bigmember_pc/src/api/modules/public.js
  2. BIN
      apps/bigmember_pc/src/assets/images/article-mask/content-mask-card@2x.png
  3. BIN
      apps/bigmember_pc/src/assets/images/article-mask/content-mask-text@2x.png
  4. 1 1
      apps/bigmember_pc/src/assets/js/china_area.js
  5. 189 0
      apps/bigmember_pc/src/assets/js/selector/source.js
  6. 28 0
      apps/bigmember_pc/src/assets/style/page/article.scss
  7. 50 10
      apps/bigmember_pc/src/components/article-item/ArticleItem.vue
  8. 10 2
      apps/bigmember_pc/src/components/collect-info/CollectInfo.vue
  9. 12 3
      apps/bigmember_pc/src/components/common/ContentLayout.vue
  10. 1157 0
      apps/bigmember_pc/src/components/filter-items/CommonThreeSelector.vue
  11. 25 2
      apps/bigmember_pc/src/components/filter-items/ContactSelector.vue
  12. 47 2
      apps/bigmember_pc/src/components/selector/TimeSelectorContent.vue
  13. 11 2
      apps/bigmember_pc/src/composables/attachment-download/component/AttachmentDownload.vue
  14. 12 6
      apps/bigmember_pc/src/router/modules/search.js
  15. 1 0
      apps/bigmember_pc/src/router/router-interceptors.js
  16. 6 2
      apps/bigmember_pc/src/utils/common.js
  17. 15 0
      apps/bigmember_pc/src/utils/format/date.js
  18. 68 0
      apps/bigmember_pc/src/utils/format/search-bid-filter.js
  19. 5 3
      apps/bigmember_pc/src/utils/whichContainer.js
  20. 68 43
      apps/bigmember_pc/src/views/article-content/components/ContentHeader.vue
  21. 95 1
      apps/bigmember_pc/src/views/article-content/components/ContentSummary.vue
  22. 61 0
      apps/bigmember_pc/src/views/article-content/components/ContentTextMask.vue
  23. 7 2
      apps/bigmember_pc/src/views/article-content/components/RecommendEnt.vue
  24. 26 8
      apps/bigmember_pc/src/views/article-content/composables/useContentStore.js
  25. 125 44
      apps/bigmember_pc/src/views/article-content/pages/Article.vue
  26. 21 6
      apps/bigmember_pc/src/views/order/components/big-member/info-data.js
  27. 4 2
      apps/bigmember_pc/src/views/search/bidding/components/search-filter-header.vue
  28. 3 0
      apps/bigmember_pc/src/views/search/bidding/model/base.js
  29. 4 4
      apps/bigmember_pc/src/views/search/bidding/model/modules/filter.js
  30. 5 0
      apps/bigmember_pc/src/views/search/bidding/model/modules/tabs.js
  31. 4 2
      apps/bigmember_pc/src/views/search/components/SearchHeader.vue
  32. 11 3
      apps/bigmember_pc/src/views/search/components/search-schema-filter.vue
  33. 5 0
      apps/bigmember_pc/src/views/search/ent/model/modules/tabs.js
  34. 3 1
      apps/bigmember_pc/src/views/search/layout/search-list.vue
  35. 128 0
      apps/bigmember_pc/src/views/search/sun/components/current-filter-text.vue
  36. 258 0
      apps/bigmember_pc/src/views/search/sun/components/history-filter-dialog.vue
  37. 610 0
      apps/bigmember_pc/src/views/search/sun/components/recommend-card.vue
  38. 223 0
      apps/bigmember_pc/src/views/search/sun/components/save-filter-dialog.vue
  39. 193 0
      apps/bigmember_pc/src/views/search/sun/components/search-bid-filter.vue
  40. 209 0
      apps/bigmember_pc/src/views/search/sun/components/search-bid-header.vue
  41. 183 0
      apps/bigmember_pc/src/views/search/sun/components/search-filter-header.vue
  42. 260 0
      apps/bigmember_pc/src/views/search/sun/components/search-list-table.vue
  43. 39 0
      apps/bigmember_pc/src/views/search/sun/components/tooltip-help-class.vue
  44. 127 0
      apps/bigmember_pc/src/views/search/sun/composables/list-header-actions.js
  45. 17 0
      apps/bigmember_pc/src/views/search/sun/constant/index.js
  46. 264 0
      apps/bigmember_pc/src/views/search/sun/constant/search-filters-bi.js
  47. 360 0
      apps/bigmember_pc/src/views/search/sun/constant/search-filters.js
  48. 748 0
      apps/bigmember_pc/src/views/search/sun/index.vue
  49. 1708 0
      apps/bigmember_pc/src/views/search/sun/model/base.js
  50. 9 0
      apps/bigmember_pc/src/views/search/sun/model/index.js
  51. 99 0
      apps/bigmember_pc/src/views/search/sun/model/modules/before-search.js
  52. 57 0
      apps/bigmember_pc/src/views/search/sun/model/modules/data-add-actions.js
  53. 414 0
      apps/bigmember_pc/src/views/search/sun/model/modules/data-collect-actions.js
  54. 46 0
      apps/bigmember_pc/src/views/search/sun/model/modules/data-distribute-actions.js
  55. 155 0
      apps/bigmember_pc/src/views/search/sun/model/modules/data-employ-actions.js
  56. 121 0
      apps/bigmember_pc/src/views/search/sun/model/modules/data-export-actions.js
  57. 52 0
      apps/bigmember_pc/src/views/search/sun/model/modules/filter-keywords.js
  58. 215 0
      apps/bigmember_pc/src/views/search/sun/model/modules/filter.js
  59. 56 0
      apps/bigmember_pc/src/views/search/sun/model/modules/join-bid-actions.js
  60. 101 0
      apps/bigmember_pc/src/views/search/sun/model/modules/list-header-actions.js
  61. 306 0
      apps/bigmember_pc/src/views/search/sun/model/modules/recommend-card.js
  62. 178 0
      apps/bigmember_pc/src/views/search/sun/model/modules/save-filter-actions.js
  63. 80 0
      apps/bigmember_pc/src/views/search/sun/model/modules/tabs.js
  64. 45 2
      apps/bigmember_pc/src/views/workspace/dashboard.vue
  65. 1 1
      apps/jy-pc/index.html
  66. BIN
      apps/jy-pc/src/assets/image/purchase/vip.png
  67. 24 24
      apps/jy-pc/src/assets/js/china_area.js
  68. 37 0
      apps/jy-pc/src/router/modules/purchase.js
  69. 64 1
      apps/jy-pc/src/views/issued/components/IssuedForm.vue
  70. 487 0
      apps/jy-pc/src/views/purchase/Detail.vue
  71. 396 0
      apps/jy-pc/src/views/purchase/Index.vue
  72. 1165 0
      apps/jy-pc/src/views/purchase/components/PurchaseForm.vue
  73. 229 0
      apps/jy-pc/src/views/purchase/components/PurchaseTable.vue
  74. 261 0
      apps/jy-pc/src/views/purchase/info/InfoPurchase.vue
  75. 285 0
      apps/jy-pc/src/views/purchase/my/MyPurchase.vue
  76. 11 0
      apps/mobile/src/api/modules/search.js
  77. BIN
      apps/mobile/src/assets/image/icon/sun-1.png
  78. BIN
      apps/mobile/src/assets/image/icon/sun-2.png
  79. BIN
      apps/mobile/src/assets/image/icon/sun-3.png
  80. BIN
      apps/mobile/src/assets/image/mask/bg/article-content-mask-card@2x.png
  81. BIN
      apps/mobile/src/assets/image/treasure-box/sun-guide.png
  82. 1 1
      apps/mobile/src/assets/js/china_area.js
  83. 6 0
      apps/mobile/src/assets/style/modules/tag.scss
  84. 15 0
      apps/mobile/src/assets/style/pic-icon.scss
  85. 1 0
      apps/mobile/src/components/common/ScrollNav.vue
  86. 86 0
      apps/mobile/src/components/search/middle/HotKeyCardSun.vue
  87. 1385 0
      apps/mobile/src/components/search/sun/filters.vue
  88. 73 0
      apps/mobile/src/components/search/sun/lingyu/index.vue
  89. 1007 0
      apps/mobile/src/components/selector/common-three-sidebar/index.vue
  90. 31 10
      apps/mobile/src/components/selector/date-time-group/index.vue
  91. 15 0
      apps/mobile/src/components/selector/date-time-list/index.vue
  92. 160 0
      apps/mobile/src/components/treasure-box/SunshineGuide.vue
  93. 1 1
      apps/mobile/src/composables/attachment-download/component/AttachmentDownload.vue
  94. 5 0
      apps/mobile/src/data/links.js
  95. 191 0
      apps/mobile/src/data/selector.js
  96. 16 0
      apps/mobile/src/data/service-list.js
  97. 16 0
      apps/mobile/src/router/modules/search.js
  98. 11 0
      apps/mobile/src/ui/project-cell/index.vue
  99. 1 0
      apps/mobile/src/utils/comput/index.js
  100. 46 0
      apps/mobile/src/utils/comput/text.js

+ 10 - 0
apps/bigmember_pc/src/api/modules/public.js

@@ -164,3 +164,13 @@ export function getZhiMaFilterData() {
     method: 'get'
   })
 }
+
+// 功能引导(阳光直采)
+export function showFunctionGuide(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/publicapply/userbase/tipInfo',
+    method: 'POST',
+    data
+  })
+}

BIN
apps/bigmember_pc/src/assets/images/article-mask/content-mask-card@2x.png


BIN
apps/bigmember_pc/src/assets/images/article-mask/content-mask-text@2x.png


+ 1 - 1
apps/bigmember_pc/src/assets/js/china_area.js

@@ -4977,7 +4977,7 @@ const chinaMapJSON = [
           '木里藏族自治县',
           '盐源县',
           '德昌县',
-          '会理',
+          '会理',
           '会东县',
           '宁南县',
           '普格县',

+ 189 - 0
apps/bigmember_pc/src/assets/js/selector/source.js

@@ -0,0 +1,189 @@
+// 领域数据源
+export const lingyuDataSource = [
+  {
+    label: '货物',
+    value: '',
+    children: [  
+      {  
+        value: 'A01',  
+        label: '房屋和构筑物',  
+        children: [  
+          { value: 'A01010000', label: '房屋' },  
+          { value: 'A01020000', label: '构筑物' },  
+          { value: 'A01030000', label: '土地' }  
+        ]
+      },  
+      {  
+        value: 'A02',  
+        label: '设备',  
+        children: [  
+          { value: 'A02010000', label: '信息化设备' },  
+          { value: 'A02020000', label: '办公设备' },  
+          { value: 'A02030000', label: '车辆' },  
+          { value: 'A02040000', label: '图书档案设备' },  
+          { value: 'A02050000', label: '机械设备' },  
+          { value: 'A02060000', label: '电气设备' },  
+          { value: 'A02070000', label: '雷达、无线电和卫星导航设备' },  
+          { value: 'A02080000', label: '通信设备' },  
+          { value: 'A02090000', label: '广播、电视、电影设备' },  
+          { value: 'A02100000', label: '仪器仪表' },  
+          { value: 'A02110000', label: '电子和通信测量仪器' },  
+          { value: 'A02120000', label: '计量标准器具及量具、衡器' },  
+          { value: 'A02130000', label: '探矿、采矿、选矿和造块设备' },  
+          { value: 'A02140000', label: '石油天然气开采设备' },  
+          { value: 'A02150000', label: '石油和化学工业设备' },  
+          { value: 'A02160000', label: '炼焦和金属冶炼轧制设备' },  
+          { value: 'A02170000', label: '电力工业设备' },  
+          { value: 'A02180000', label: '非金属矿物制品工业设备' },  
+          { value: 'A02190000', label: '核工业设备' },  
+          { value: 'A02200000', label: '航空航天工业设备' },  
+          { value: 'A02210000', label: '工程机械' },  
+          { value: 'A02220000', label: '农业和林业机械' },  
+          { value: 'A02230000', label: '木材采集和加工设备' },  
+          { value: 'A02240000', label: '食品加工设备' },  
+          { value: 'A02250000', label: '饮料加工设备' },  
+          { value: 'A02260000', label: '烟草加工设备' },  
+          { value: 'A02270000', label: '粮油作物和饲料加工设备' },  
+          { value: 'A02280000', label: '纺织设备' },  
+          { value: 'A02290000', label: '缝纫、服饰、制革和毛皮加工设备' },  
+          { value: 'A02300000', label: '造纸和印刷机械' },  
+          { value: 'A02310000', label: '化学药品和中药设备' },  
+          { value: 'A02320000', label: '医疗设备' },  
+          { value: 'A02330000', label: '电工、电子生产设备' },  
+          { value: 'A02340000', label: '安全生产设备' },  
+          { value: 'A02350000', label: '邮政设备' },  
+          { value: 'A02360000', label: '环境污染防治设备' },  
+          { value: 'A02370000', label: '政法、消防、检测设备' },  
+          { value: 'A02380000', label: '水工机械' },  
+          { value: 'A02390000', label: '货币处理设备' },  
+          { value: 'A02400000', label: '殡葬设备及用品' },  
+          { value: 'A02410000', label: '铁路运输设备' },  
+          { value: 'A02420000', label: '水上交通运输设备' },  
+          { value: 'A02430000', label: '航空器及其配套设备' },  
+          { value: 'A02440000', label: '海洋仪器设备' },  
+          { value: 'A02450000', label: '文艺设备' },  
+          { value: 'A02460000', label: '体育设备设施' },  
+          { value: 'A02470000', label: '娱乐设备' }  
+        ]  
+      },  
+      {  
+        value: 'A03',  
+        label: '文物和陈列品',  
+        children: [  
+          { value: 'A03020000', label: '可移动文物' },  
+          { value: 'A03030000', label: '文创衍生品' },  
+          { value: 'A03040000', label: '标本' },  
+          { value: 'A03050000', label: '模型' }  
+        ]  
+      },  
+      {  
+        value: 'A04',  
+        label: '图书和档案',  
+        children: [  
+          { value: 'A04010000', label: '图书' },  
+          { value: 'A04020000', label: '期刊' },  
+          { value: 'A04030000', label: '资料' },  
+          { value: 'A04040000', label: '档案' }  
+        ]  
+      },  
+      {  
+        value: 'A05',  
+        label: '家具和用具',  
+        children: [  
+          { value: 'A05010000', label: '家具' },  
+          { value: 'A05020000', label: '用具' },  
+          { value: 'A05030000', label: '装具' },  
+          { value: 'A05040000', label: '办公用品' }  
+        ]  
+      },  
+      {  
+        value: 'A06',  
+        label: '特种动植物',  
+        children: [  
+          { value: 'A06010000', label: '特种用途动物' },  
+          { value: 'A06020000', label: '特种用途植物' }  
+        ]  
+      },
+      {  
+        value: 'A07',  
+        label: '物资',  
+        children: [  
+          {  
+            value: 'A07010000',  
+            label: '建筑建材'  
+          },  
+          {  
+            value: 'A07020000',  
+            label: '医药品'  
+          },  
+          {  
+            value: 'A07030000',  
+            label: '农林牧渔业产品'  
+          },  
+          {  
+            value: 'A07040000',  
+            label: '矿与矿物'  
+          },  
+          {  
+            value: 'A07050000',  
+            label: '电力、城市燃气、蒸汽和热水、水'  
+          },  
+          {  
+            value: 'A07060000',  
+            label: '食品、饮料和烟草原料'  
+          },  
+          {  
+            value: 'A07070000',  
+            label: '炼焦产品、炼油产品'  
+          },  
+          {  
+            value: 'A07080000',  
+            label: '基础化学品及相关产品'  
+          },  
+          {  
+            value: 'A07090000',  
+            label: '橡胶、塑料、玻璃和陶瓷制品'  
+          },  
+          {  
+            value: 'A07100000',  
+            label: '纸及纸质品'  
+          }  
+        ]  
+      },  
+      {  
+        value: 'A08',  
+        label: '无形资产',  
+        children: [  
+          {  
+            value: 'A08010000',  
+            label: '专利类无形资产'  
+          },  
+          {  
+            value: 'A08020000',  
+            label: '非专利技术类无形资产'  
+          },  
+          {  
+            value: 'A08030000',  
+            label: '著作权类无形资产'  
+          },  
+          {  
+            value: 'A08040000',  
+            label: '资源资质类无形资产'  
+          },  
+          {  
+            value: 'A08050000',  
+            label: '商标权类无形资产'  
+          },  
+          {  
+            value: 'A08060000',  
+            label: '信息数据类无形资产'  
+          },  
+          {  
+            value: 'A08070000',  
+            label: '经营类无形资产'  
+          }  
+        ]  
+      }  
+    ]
+  }
+]

+ 28 - 0
apps/bigmember_pc/src/assets/style/page/article.scss

@@ -116,6 +116,30 @@
     }
   }
 
+  .origin-detail-action-container {
+    display: flex;
+    align-items: center;
+    .origin-link-suffix {
+      display: flex;
+      align-items: center;
+      margin-left: 12px;
+      font-size: 14px;
+      line-height: 18px;
+      color: #888;
+    }
+  }
+  .origin-link-button {
+    display: flex;
+    align-items: center;
+    padding: 4px 14px;
+    background: linear-gradient(to right, #2abed1 0%, #1b8ffaf5 96%);
+    font-size: 16px;
+    line-height: 24px;
+    color: #fff;
+    border-radius: 4px;
+    margin-left: 12px;
+    cursor: pointer;
+  }
   .origin-detail-action {
     width: 132px;
     height: 34px;
@@ -133,6 +157,10 @@
     }
   }
 
+  .content-mask-container {
+    margin-top: 10px;
+  }
+
   .watch-tab-header {
     margin-top: 16px;
   }

+ 50 - 10
apps/bigmember_pc/src/components/article-item/ArticleItem.vue

@@ -52,6 +52,12 @@
         <div class="tags">
           <span
             class="tag tag-user"
+            v-if="config.tagUserPublishState && article.publicType"
+            >{{article.publicType}}</span
+          >
+          <span
+            class="tag tag-user"
+            v-show="!config.publicType"
             v-if="
               article.site === '剑鱼信息发布平台' ||
               article.spidercode === 'a_jyxxfbpt_gg' ||
@@ -100,6 +106,9 @@
           <span class="tag dpink" v-if="calcBudget && calcBudget !== '0元'">
             {{ calcBudget }}
           </span>
+          <span class="tag border" :class="{'tag-user': !article.signupEnd }" v-if="config.tagSignupEnd && article.signupEnd !== undefined && article.signEndTimeText">
+            {{ article.signupEnd ? '报名已截止' : '报名未截止' }}
+          </span>
         </div>
 
         <div style="display: flex; align-items: center">
@@ -209,10 +218,15 @@
             <i class="l-d-item-label">采购单位联系方式:</i>
             {{ article.buyerPerson }}
             <em v-if="article.buyerPerson">,</em>
-            {{ article.buyerTel }}
+            <template v-if="article.buyerTel === maskText">
+              <i :class="{'highlight-text pointer': article.buyerTel === maskText}" @click.stop="leaveInfo">{{ article.buyerTel }}</i>
+            </template>
+            <template v-else>
+              {{ article.buyerTel }}
+            </template>
             <em
               class="more-tel"
-              v-if="article.buyerTel"
+              v-if="article.buyerTel && article.buyerTel !== maskText"
               @click.stop="goPortrayal('buyerDesc', article.buyer, 'contact')"
               >获取更多</em
             >
@@ -299,6 +313,25 @@
             {{ dateFromNow(article.bidOpenTime * 1000) }}
           </span>
         </p>
+        <div
+          class="l-d-item"
+          v-if="config.tagSignupEnd && article.signEndTimeText"
+        >
+          <p v-if="article.signEndTimeText">
+            <i class="l-d-item-label">报名截止日期:</i>
+            {{ article.signEndTimeText }}
+          </p>
+          
+        </div>
+        <div
+          class="l-d-item"
+          v-if="article.deliveryLoc"
+        >
+          <p>
+            <i class="l-d-item-label">交付地点:</i>
+            {{ article.deliveryLoc }}
+          </p>
+        </div>
       </div>
     </div>
     <div class="list-item-right">
@@ -534,6 +567,7 @@ export default {
   },
   data() {
     return {
+      maskText: '点击查看',
       sourceMap: {
         1: '个人订阅',
         2: '企业自动分发',
@@ -582,11 +616,17 @@ export default {
       this.$emit('setShow')
     },
     goPortrayal(type, item, position) {
+      if (item === this.maskText) {
+        return this.leaveInfo()
+      }
       // position: 锚点定位参数
       const { url } = getPowerUrl(type, { id: item })
       const params = position ? '?position=' + position : ''
       window.open(url + params)
     },
+    leaveInfo() {
+      this.$emit('leaveInfo')
+    },
     formatBuyer(data) {
       if (!data) return []
       const arr = data.split('、')
@@ -889,14 +929,14 @@ $border-color: #ececec;
     }
     span {
       margin-right: 32px;
-      .l-d-item-label {
-        color: #686868;
-      }
-      a {
-        &:hover,
-        &:focus {
-          color: #2cb7ca;
-        }
+    }
+    .l-d-item-label {
+      color: #686868;
+    }
+    a {
+      &:hover,
+      &:focus {
+        color: #2cb7ca;
       }
     }
   }

+ 10 - 2
apps/bigmember_pc/src/components/collect-info/CollectInfo.vue

@@ -569,7 +569,9 @@ export default {
         member_freeuse: '请留下联系方式,我们会尽快联系您体验大会员全部功能!',
         jylab_see500_plus: '请留下联系方式,我们会尽快联系您!开通大会员可查看更多招标项目,实时监控更多潜在商机!',
         pc_dzbg_fullreport: '请留下联系方式,我们会安排客户经理与您对接下载查看!',
-        pc_dzbgxzb_customizedquantity: '请留下联系方式,我们会安排客户经理与您对接,下载更多市场分析定制报告!'
+        pc_dzbgxzb_customizedquantity: '请留下联系方式,我们会安排客户经理与您对接,下载更多市场分析定制报告!',
+        pc_sunlightlist_viewdetails: '每天前10名开通权限的用户可免费开通',
+        pc_sunlight_viewdetails: '每天前10名开通权限的用户可免费开通',
       },
       sourceDescMap: {
         pc_buyer_monitor_more: '采购单位画像页-超级订阅用户申请监控更多业主',
@@ -592,6 +594,8 @@ export default {
         pc_article_original_one: '标讯详情页-免费用户获取1次查看原文链接机会',
         pc_article_original_more: '标讯详情页-获取更多查看原文链接机会',
         pc_dzbg_fullreport: '申请免费体验-完整查看市场分析定制报告',
+        pc_sunlightlist_viewdetails: '阳光直采采购信息列表-查看采购信息详情',
+        pc_sunlight_viewdetails: '阳光直采-查看采购信息详情',
         pc_dzbgxzb_customizedquantity: '市场分析定制报告下载包-定制报告份数'
       },
       sourceTitleTopMap: {
@@ -617,7 +621,9 @@ export default {
         pc_article_ent_limit: '申请监控更多企业',
         'certificateServices-pc-biddingDetailPage-content': '请留下您的信息,我们会尽快和您联系',
         pc_dzbg_fullreport: '体验市场分析定制报告',
-        pc_dzbgxzb_customizedquantity: '市场分析定制报告下载'
+        pc_dzbgxzb_customizedquantity: '市场分析定制报告下载',
+        pc_sunlightlist_viewdetails: '采购人联系方式等关键信息无权限查看',
+        pc_sunlight_viewdetails: '采购人联系方式等关键信息无权限查看',
       },
       isRefresh: false
     }
@@ -704,6 +710,8 @@ export default {
         return '已收到您提交的市场分析定制报告体验申请,我们会尽快联系您。'
       } else if (this.source === 'pc_dzbgxzb_customizedquantity') {
         return '已收到您提交的市场分析定制报告下载申请,我们会尽快联系您。'
+      } else if (this.source === 'pc_sunlightlist_viewdetails' || this.source === 'pc_sunlight_viewdetails') {
+        return '后续会有客服与您致电,并告知您获取结果!'
       } else {
         return '我们会尽快联系您并预约演示时间,请耐心等待~您将获得免费体验大会员全部功能!'
       }

+ 12 - 3
apps/bigmember_pc/src/components/common/ContentLayout.vue

@@ -89,11 +89,20 @@ export default {
       ]
     }
   },
+  watch: {
+    needAd: {
+      immediate: true,
+      handler(n) {
+        if (n) {
+          this.$nextTick(() => {
+            this.getAdvertisementList()
+          })
+        }
+      }
+    }
+  },
   created() {
     this.routerName = this.$route.name
-    if (this.needAd) {
-      this.getAdvertisementList()
-    }
   },
   methods: {
     getRandomString,

+ 1157 - 0
apps/bigmember_pc/src/components/filter-items/CommonThreeSelector.vue

@@ -0,0 +1,1157 @@
+<template>
+  <Layout
+    ref="layoutRef"
+    :type="type"
+    :placeholder="placeholder"
+    :trigger="trigger"
+    :value="computedVal"
+    @visible="onVisibleChange"
+  >
+    <div slot="empty" class="zhima-bid--wrap dropdown-content--wrap">
+      <div class="dropdown-content--hd" v-if="showSearch">
+        <div class="dropdown-content--hd-item dropdown-content--hd-left">
+          <el-input
+            size="small"
+            clearable
+            placeholder="请输入标签名称搜索"
+            suffix-icon="el-icon-search"
+            v-model="searchContent"
+            @keyup.native="onKeyup"
+            @focus="onFocus"
+            @blur="onBlur"
+            @keydown.native="onKeydown"
+            :class="{ 'input-focus': inputFocus }"
+          >
+          </el-input>
+        </div>
+        <div class="dropdown-content--hd-item dropdown-content--hd-right">
+          <slot name="header-right"></slot>
+        </div>
+      </div>
+      <div class="dropdown-content--bd">
+        <div class="module-container level-1-container">
+          <header class="module-header"></header>
+          <div class="list-empty-container" v-if="searchEmpty">
+            暂无相关选项
+          </div>
+          <div class="module-main">
+            <ul>
+              <li
+                class="module-item"
+                @mouseover="onLevel1MouseOver(level1, pIndex)"
+                @mouseout="onLevel1MouseOut($event)"
+                :class="{ active: pActive === pIndex }"
+                v-for="(level1, pIndex) in level1List"
+                v-show="!level1.searchHide"
+                :key="level1.name"
+              >
+                <el-checkbox
+                  v-model="level1.checked"
+                  :indeterminate="level1.indeterminate"
+                  @change="onProvinceChange($event, level1, pIndex)"
+                ></el-checkbox>
+                <span
+                  class="item-name"
+                  @click.self="onOpenLevel2(level1, pIndex)"
+                  v-html="level1.label"
+                ></span>
+                <i class="el-icon-arrow-right"></i>
+              </li>
+            </ul>
+          </div>
+        </div>
+        <div class="module-container level-2-container">
+          <header class="module-header"></header>
+          <div class="module-main">
+            <ul>
+              <li
+                class="module-item"
+                @mouseover="onLevel2MouseOver(level2, cIndex)"
+                @mouseout="onLevel2MouseOut($event)"
+                :class="{ active: cActive === cIndex }"
+                v-for="(level2, cIndex) in level2List"
+                v-show="!level2.searchHide"
+                :key="level2.name"
+              >
+                <el-checkbox
+                  v-model="level2.checked"
+                  :disabled="level2.disabled"
+                  :indeterminate="level2.indeterminate"
+                  @change="onCitiesChange($event, level2, cIndex)"
+                ></el-checkbox>
+                <span
+                  class="item-name"
+                  @click.self="onOpenLevel3(level2, cIndex)"
+                  v-html="level2.label"
+                ></span>
+                <i class="el-icon-arrow-right"></i>
+              </li>
+            </ul>
+          </div>
+        </div>
+        <div class="module-container level-3-container">
+          <header class="module-header"></header>
+          <div class="module-main">
+            <ul>
+              <li
+                class="module-item"
+                v-for="(level3, index) in level3List"
+                :key="level3 + index"
+                v-show="!level3.searchHide"
+              >
+                <el-checkbox
+                  v-model="level3.checked"
+                  :disabled="level3.disabled"
+                  :indeterminate="level3.indeterminate"
+                  @change="onLevel3Change($event, level3)"
+                ></el-checkbox>
+                <span class="item-name" v-html="level3.label"></span>
+              </li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </div>
+    <template #suffix-icon>
+      <slot name="suffix-icon">
+        <el-tooltip effect="dark" placement="bottom" v-if="showHelpIcon">
+          <div class="iconfont icon-help pointer zhima-bid--icon"></div>
+          <div slot="content" class="zhima-bid-help-tip" v-html="helpIconText"></div>
+        </el-tooltip>
+      </slot>
+    </template>
+  </Layout>
+</template>
+
+<script>
+import { Tag, Checkbox, Input, Tooltip } from 'element-ui'
+import Layout from '@/components/filter-items/Layout.vue'
+
+export default {
+  name: 'ZhiMaBidSelector',
+  components: {
+    [Tag.name]: Tag,
+    [Input.name]: Input,
+    [Checkbox.name]: Checkbox,
+    [Tooltip.name]: Tooltip,
+    Layout
+  },
+  props: {
+    showSearch: {
+      type: Boolean,
+      default: false
+    },
+    sourceList: {
+      type: Array,
+      default: () => [
+        // {
+        //   label: 'xxx',
+        //   value: 'xxx',
+        //   children: [
+        //     {
+        //       label: 'xx',
+        //       value: 'xx'
+        //     }
+        //   ]
+        // }
+      ]
+    },
+    showHelpIcon: {
+      type: Boolean,
+      default: false
+    },
+    helpIconText: {
+      type: String,
+      default: ''
+    },
+    type: {
+      type: String,
+      default: 'dropdown'
+    },
+    trigger: {
+      type: String,
+      default: 'hover'
+    },
+    placeholder: {
+      type: String,
+      default: ''
+    },
+    // 是否展示level1的全部
+    isHaveAll: {
+      type: Boolean,
+      default: true
+    },
+    value: {
+      type: Object,
+      default: () => {}
+    },
+    formatSourceData: Function,
+  },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
+  data() {
+    return {
+      searchContent: '',
+      inputFocus: false,
+      tags: [],
+      pActive: 0,
+      cActive: 0,
+      level1List: [],
+      level2List: [],
+      level3List: [],
+      pTimer: null,
+      cTimer: null
+    }
+  },
+  computed: {
+    computedVal() {
+      let count = this.tags.length
+      const value = this.value
+      if (!value) return ''
+      const emptyObj = Object.keys(value).length === 0
+      if (count <= 0 || emptyObj) {
+        return ''
+      } else {
+        return `${this.placeholder}${count}个`
+      }
+    },
+    showedLevel1List() {
+      return this.level1List.filter((v) => !v.searchHide)
+    },
+    // 是否没有搜索到数据
+    searchEmpty() {
+      if (this.searchContent && this.showedLevel1List.length === 0) {
+        return true
+      } else {
+        return false
+      }
+    }
+  },
+  watch: {
+    value(val) {
+      this.setState(val)
+    },
+    searchContent(n) {
+      // 思路:
+      // 1. 对其显示隐藏做标记,level1、level2和level3则用computed过滤
+      //
+      this.calcMatch(n)
+      this.$nextTick(() => {
+        this.openLevel1First()
+      })
+    }
+  },
+  mounted() {
+    this.refreshSourceList(this.sourceList)
+    this.calcMatch()
+  },
+  methods: {
+    calcMatch(searchValue = '') {
+      this.level1List.forEach((level1) => {
+        // 一级匹配
+        const { searchShow: level1ItemShow } = this.doMatch(searchValue, level1)
+        this.$set(level1, 'searchHide', !level1ItemShow)
+        // 二级匹配
+        level1.children.forEach((level2) => {
+          // 如果一级匹配到了,其子集都要显示
+          const { searchShow: level2ItemShow } = this.doMatch(
+            searchValue,
+            level2
+          )
+          let needHide = !level2ItemShow
+          if (level1ItemShow) {
+            needHide = false
+          }
+          this.$set(level2, 'searchHide', needHide)
+          if (level2ItemShow) {
+            this.$set(level1, 'searchHide', false)
+          }
+
+          level2.children.forEach((level3) => {
+            const { searchShow: level3ItemShow } = this.doMatch(
+              searchValue,
+              level3
+            )
+            let needHide = !level3ItemShow
+            if (level1ItemShow || level2ItemShow) {
+              needHide = false
+            }
+            this.$set(level3, 'searchHide', needHide)
+            if (level3ItemShow) {
+              this.$set(level1, 'searchHide', false)
+              this.$set(level2, 'searchHide', false)
+            }
+          })
+        })
+      })
+    },
+    doMatch(searchValue, item) {
+      let notAll = true
+      if (searchValue) {
+        notAll = item.name !== '全部'
+      }
+
+      const matched = item.name?.includes(searchValue)
+      if (matched && searchValue) {
+        item.label = item.name.replace(
+          searchValue,
+          `<span class="highlight-text">${searchValue}</span>`
+        )
+      } else {
+        item.label = item.name
+      }
+
+      let searchShow = true
+      if (searchValue) {
+        searchShow = notAll && matched
+      }
+
+      return {
+        notAll,
+        matched,
+        searchShow
+      }
+    },
+    onFocus() {
+      this.onKeydown()
+      this.inputFocus = true
+    },
+    onBlur() {
+      this.inputFocus = false
+    },
+    refreshSourceList(list) {
+      if (Array.isArray(list)) {
+        this.initAreaMap(list)
+
+        this.$nextTick(() => {
+          this.calcMatch()
+        })
+      }
+    },
+    // 获取选中的数据列表tag
+    getSelectedTags() {
+      const tagsArr = []
+      this.level1List.forEach((level1) => {
+        if (Array.isArray(level1.children)) {
+          level1.children.forEach((level2) => {
+            if (Array.isArray(level2.children)) {
+              level2.children.forEach((level3) => {
+                if (level3.name !== '全部') {
+                  if (level3.checked) {
+                    tagsArr.push(level3.name)
+                  }
+                }
+              })
+            }
+          })
+        }
+      })
+      this.tags = tagsArr
+      return tagsArr
+    },
+    // 整理城市数据列表
+    initAreaMap(list) {
+      const level1List = this.getStandardData(list)
+      // 如果需要包含全国选项
+      if (this.isHaveAll) {
+        level1List.unshift({
+          name: '全部',
+          label: '全部',
+          checked: false,
+          disabled: false,
+          indeterminate: false,
+          children: []
+        })
+      }
+      level1List.forEach((level1) => {
+        level1.children.unshift({
+          name: '全部',
+          label: '全部',
+          checked: false,
+          disabled: false,
+          indeterminate: false,
+          children: []
+        })
+        level1.children.forEach((level2) => {
+          level2.children.unshift({
+            name: '全部',
+            label: '全部',
+            checked: false,
+            disabled: level2.children.length === 0,
+            indeterminate: false
+          })
+        })
+      })
+      this.level1List = level1List
+
+      // 默认打开第一个城市、区县
+      this.openLevel1First()
+    },
+    openLevel1First(level1List = this.level1List) {
+      // 默认打开第一个城市、区县
+      if (this.searchContent) {
+        // 找到子集第一个hide为false的子集展示
+        const r2 = this.findFirstShowChildren(level1List)
+        this.level2List = r2.list
+        this.pActive = r2.index
+        const r3 = this.findFirstShowChildren(this.level2List)
+        this.level3List = r3.list
+        this.cActive = r3.index
+      } else {
+        this.level2List = level1List[0].children
+        this.level3List = level1List[0].children[0].children
+      }
+    },
+    findFirstShowChildren(levelList) {
+      const findIndex = levelList.findIndex((t) => !t.searchHide)
+      if (findIndex > 0) {
+        return {
+          list: levelList[findIndex]?.children || [],
+          index: findIndex
+        }
+      } else {
+        return {
+          list: [],
+          index: 0
+        }
+      }
+    },
+    /**
+     *  返回需要的数组格式
+     */
+    getStandardData(initData = []) {
+      // 处理成标准字段
+      if (this.formatSourceData) {
+        return this.formatSourceData(initData)
+      }
+      const standardData = initData.map((p) => {
+        return {
+          name: p.label,
+          label: p.label,
+          checked: false,
+          disabled: false,
+          indeterminate: false,
+          children: p.children.map((c) => {
+            return {
+              name: c.label,
+              label: c.label,
+              checked: false,
+              disabled: false,
+              indeterminate: false,
+              children: c.children.map((a) => {
+                return {
+                  name: a.label,
+                  label: a.label,
+                  checked: false,
+                  disabled: false
+                }
+              })
+            }
+          })
+        }
+      })
+      return standardData
+    },
+    /**
+     * 将过滤好的地区数据转换成map格式(根据场景需要转换)
+     * 例:{北京:{北京市:[]}, 河南:{ 郑州市:[新郑市, 登封市、金水区]}}
+     */
+    getCitiesToMap(filterData = this.getStandardData()) {
+      const obj = {}
+      if (filterData.length > 0) {
+        filterData.forEach((province) => {
+          const cityMap = {}
+          province.children.forEach((city) => {
+            cityMap[city.name] = city.children.map((v) => v.name)
+          })
+          obj[province.name] = cityMap
+        })
+      }
+      return obj
+    },
+    // 判断全国、全部全选半选状态
+    getAllCheckBoxSelectedStatus() {
+      if (!this.isHaveAll) return
+      const level1List = this.level1List
+      const allProvinceCount = level1List.filter(
+        (v) => v.name !== '全部'
+      ).length
+      const allSelectedProvinceCount = level1List.filter(
+        (v) => v.name !== '全部' && v.checked
+      ).length
+      const allHalfSelectedProvinceCount = level1List.filter(
+        (v) => v.name !== '全部' && v.indeterminate
+      ).length
+      level1List.forEach((province) => {
+        if (allProvinceCount === allSelectedProvinceCount) {
+          level1List[0].checked = true
+          level1List[0].indeterminate = false
+        } else {
+          level1List[0].indeterminate =
+            allSelectedProvinceCount > 0 || allHalfSelectedProvinceCount > 0
+          const allCityCount = province.children.filter(
+            (v) => v.name !== '全部'
+          ).length
+          const allSelectedCityCount = province.children.filter(
+            (v) => v.name !== '全部' && v.checked
+          ).length
+          const allHalfSelectedCityCount = province.children.filter(
+            (v) => v.name !== '全部' && v.indeterminate
+          ).length
+          province.children.forEach((city) => {
+            if (
+              allCityCount === allSelectedCityCount &&
+              allSelectedCityCount > 0
+            ) {
+              province.children[0].checked = true
+              province.children[0].indeterminate = false
+            } else {
+              province.children[0].indeterminate =
+                allSelectedCityCount > 0 || allHalfSelectedCityCount > 0
+              const allDistrictCount = city.children.filter(
+                (v) => v.name !== '全部'
+              ).length
+              const allSelectedDistrictCount = city.children.filter(
+                (v) => v.name !== '全部' && v.checked
+              ).length
+              const allHalfSelectedDistrictCount = city.children.filter(
+                (v) => v.name !== '全部' && v.indeterminate
+              ).length
+              city.children.forEach((district) => {
+                if (
+                  allDistrictCount === allSelectedDistrictCount &&
+                  allSelectedDistrictCount > 0
+                ) {
+                  city.children[0].checked = true
+                  city.children[0].indeterminate = false
+                } else {
+                  city.children[0].indeterminate =
+                    allSelectedDistrictCount > 0 ||
+                    allHalfSelectedDistrictCount > 0
+                }
+              })
+            }
+          })
+        }
+      })
+    },
+    // 点击省展开城市
+    onOpenLevel2(province, pIndex) {
+      this.cActive = 0
+      this.pActive = pIndex
+      this.level3List = []
+      this.level2List = province.children
+      this.level3List = province.children[0].children
+    },
+    // 点击城市展开区县
+    onOpenLevel3(city, cIndex) {
+      this.cActive = cIndex
+      this.level3List = city.children
+    },
+    onLevel1MouseOut(e) {
+      clearTimeout(this.pTimer)
+      this.pTimer = null
+    },
+    onLevel1MouseOver(level1, pIndex) {
+      if (!this.pTimer) {
+        this.pTimer = setTimeout(() => {
+          this.onOpenLevel2(level1, pIndex)
+        }, 150)
+      }
+    },
+    onLevel2MouseOut(e) {
+      clearTimeout(this.cTimer)
+      this.cTimer = null
+    },
+    onLevel2MouseOver(city, cIndex) {
+      if (!this.cTimer) {
+        this.cTimer = setTimeout(() => {
+          this.onOpenLevel3(city, cIndex)
+        }, 150)
+      }
+    },
+    // 设置全国全选、半选状态
+    setWholeCountryCheckedStatus() {
+      const selectedProvince = this.level1List.filter((province) => {
+        return province.checked && province.name !== '全部'
+      })
+      const allProvince = this.level1List.filter((province) => {
+        return province.name !== '全部'
+      })
+      const selectedIndeterminate = this.level1List.filter((province) => {
+        return province.name !== '全部' && province.indeterminate
+      })
+      if (this.isHaveAll) {
+        // 如果有全国选项 则选中的省份数量(排除全国)等于全部省份数量 则绑定全国选中
+        if (selectedProvince.length === allProvince.length) {
+          this.level1List[0].indeterminate = false
+          this.level1List[0].checked = true
+        } else {
+          // 如果选中全国数量不等于全部省份数量 则全国半选  选中数等于0除外
+          this.level1List[0].checked = false
+          this.level1List[0].indeterminate =
+            selectedIndeterminate.length > 0 || selectedProvince.length > 0
+        }
+      }
+      this.getSelectedTags()
+    },
+    // 设置省全选、半选状态
+    setProvinceCheckedStatus() {
+      // 设置当前省份下城市的全选状态(全选、半选)
+      const currentProvince = this.level1List[this.pActive]
+      const selectedCities = currentProvince.children.filter(
+        (v) => v.checked && v.name !== '全部'
+      )
+      const allCities = currentProvince.children.filter(
+        (v) => v.name !== '全部'
+      )
+      const cityIndeterminate = currentProvince.children.filter(
+        (v) => v.name !== '全部' && v.indeterminate
+      )
+      if (selectedCities.length === allCities.length) {
+        this.level1List[this.pActive].checked = true
+        this.level1List[this.pActive].indeterminate = false
+      } else {
+        this.level1List[this.pActive].checked = false
+        this.level1List[this.pActive].indeterminate =
+          selectedCities.length !== 0 || cityIndeterminate.length > 0
+      }
+      currentProvince.children[0].indeterminate =
+        (selectedCities.length !== allCities.length &&
+          selectedCities.length !== 0) ||
+        cityIndeterminate.length > 0
+      currentProvince.children[0].checked =
+        selectedCities.length === allCities.length
+      this.setWholeCountryCheckedStatus()
+    },
+    setCitiesCheckedStatus() {
+      // 设置当前省份下城市的全选状态(全选、半选)
+      const currentCity = this.level2List[this.cActive]
+      const selectedCountry = currentCity.children.filter(
+        (v) => v.checked && v.name !== '全部'
+      )
+      const allCountry = currentCity.children.filter((v) => v.name !== '全部')
+      if (selectedCountry.length === allCountry.length) {
+        currentCity.checked = true
+        currentCity.indeterminate = false
+      } else {
+        currentCity.checked = false
+        currentCity.indeterminate = selectedCountry.length !== 0
+      }
+      // if (this.isHaveAll) {
+      currentCity.children[0].indeterminate =
+        selectedCountry.length !== allCountry.length &&
+        selectedCountry.length !== 0
+      currentCity.children[0].checked =
+        selectedCountry.length === allCountry.length
+      // }
+      this.setProvinceCheckedStatus()
+    },
+    /**
+     * 提取省份checkbox选择状态方法
+     * checked: checkbox点击状态
+     * province: 当前点击的城市数据
+     */
+    setProvinceChangeCommon(checked, province) {
+      // 全国选中/取消选中
+      if (province.name === '全部' || province.name === '全部') {
+        this.level1List.forEach((first) => {
+          first.checked = checked
+          first.indeterminate = false
+          first.children.forEach((second) => {
+            second.checked = checked
+            second.indeterminate = false
+            second.children.forEach((third) => {
+              third.checked = checked
+              third.indeterminate = false
+            })
+          })
+        })
+      } else {
+        // 省份选中/取消选中
+        province.indeterminate = false
+        province.children.forEach((second) => {
+          second.checked = checked
+          second.indeterminate = false
+          second.children.forEach((third) => {
+            third.checked = checked
+            third.indeterminate = false
+          })
+        })
+      }
+      this.setWholeCountryCheckedStatus()
+    },
+    // 省份选中事件
+    onProvinceChange(checked, province, index) {
+      this.onOpenLevel2(province, index)
+      province.checked = checked
+      this.setProvinceChangeCommon(checked, province)
+      this.onSelectChange()
+    },
+    /**
+     * 提取城市checkbox选择状态方法
+     * checked: checkbox点击状态
+     * city: 当前点击的城市数据
+     */
+    setCitiesChangeCommon(checked, city) {
+      if (city.name === '全部') {
+        // 全部城市选中/取消选中
+        this.level1List.forEach((first, index) => {
+          if (this.pActive === index) {
+            first.checked = checked
+            first.indeterminate = false
+            first.children.forEach((second) => {
+              second.checked = checked
+              second.indeterminate = false
+              second.children.forEach((third) => {
+                third.checked = checked
+                third.indeterminate = false
+              })
+            })
+          }
+        })
+      } else {
+        // 当前城市选中/取消选中
+        city.indeterminate = false
+        city.children.forEach((third) => {
+          third.checked = checked
+          third.indeterminate = false
+        })
+      }
+      this.setProvinceCheckedStatus()
+    },
+    // 城市选中事件
+    onCitiesChange(checked, city, cIndex) {
+      this.onOpenLevel3(city, cIndex)
+      // 选择超出可选区域冒泡事件
+      if (checked) {
+        city.checked = true
+        this.setCitiesChangeCommon(checked, city)
+      } else {
+        // 取消选中
+        city.checked = false
+        this.setCitiesChangeCommon(checked, city)
+      }
+      this.onSelectChange()
+    },
+    setCountryChangeCommon(checked, country) {
+      if (country.name === '全部') {
+        this.level3List.forEach((third) => {
+          third.checked = checked
+          third.indeterminate = false
+        })
+        this.setCitiesCheckedStatus()
+      } else {
+        this.setCitiesCheckedStatus()
+      }
+    },
+    // 区县选中事件
+    onLevel3Change(checked, country) {
+      // 选择超出可选区域冒泡事件
+      if (checked) {
+        country.checked = true
+        this.setCountryChangeCommon(checked, country)
+      } else {
+        country.checked = false
+        this.setCountryChangeCommon(checked, country)
+      }
+      this.onSelectChange()
+    },
+    // 下拉框出现/隐藏时触发
+    onVisibleChange(flag) {
+      if (!flag) {
+        this.$emit('hideSelect', this.getState())
+      } else {
+        this.$emit('showSelect', this.getState())
+      }
+    },
+    getDropdownRef() {
+      const layoutRef = this.$refs.layoutRef
+      if (layoutRef) {
+        const dropdownRef = layoutRef.$refs?.dropdownRef
+        return dropdownRef
+      }
+    },
+    onKeyup() {
+      const dropdownRef = this.getDropdownRef()
+      if (dropdownRef) {
+        dropdownRef.visible = true
+      }
+    },
+    // dropdown的坑点,微软输入法,input输入的时候会失去焦,dropdown会收起,暂且如此处理
+    onKeydown() {
+      const dropdownRef = this.getDropdownRef()
+      if (dropdownRef && dropdownRef.show) {
+        dropdownRef.show()
+      }
+    },
+    onNoPowerLimit(payload) {
+      this.$emit('limit', payload)
+    },
+    onSelectChange() {
+      const state = this.getState()
+      this.$emit('change', state)
+    },
+    // 省市县区三级结构拆分省市(省:[市])和区县(地市:[区县])
+    formatProvinceAndCities(regionMap) {
+      let area = {}
+      const district = {}
+      if (Object.keys(regionMap).length === 0) {
+        area = {}
+      } else {
+        for (const key in regionMap) {
+          if (Object.keys(regionMap[key]).length === 0) {
+            area[key] = []
+          } else {
+            const cities = regionMap[key]
+            const cityArr = []
+            for (const city in cities) {
+              cityArr.push(city)
+              area[key] = cityArr
+              if (cities[city].length > 0) {
+                district[city] = cities[city]
+              }
+            }
+          }
+        }
+      }
+      return { area, district }
+    },
+    // 省市县区map转字符串 应用场景:平铺选择结果需要
+    formatRegionToString(regionMap, mark = '、') {
+      if (!regionMap || !Object.keys(regionMap).length) return '全部'
+      const tagsArr = []
+      for (const province in regionMap) {
+        if (Object.keys(regionMap[province]).length === 0) {
+          tagsArr.push(province)
+        } else {
+          const cityObj = regionMap[province]
+          for (const cKey in cityObj) {
+            if (cityObj[cKey].length === 0) {
+              tagsArr.push(cKey)
+            } else {
+              tagsArr.push(...cityObj[cKey])
+            }
+          }
+        }
+      }
+      return tagsArr.join(mark)
+    },
+    // 获取数据,并整理成前端标准格式
+    getState() {
+      let level1List = JSON.parse(JSON.stringify(this.level1List))
+      const allProvinceCount = level1List.filter(
+        (v) => v.name !== '全部'
+      ).length
+      const allSelectedProvinceCount = level1List.filter(
+        (v) => v.name !== '全部' && v.checked
+      ).length
+      const allCountryChecked = level1List.some(
+        (v) => v.name === '全部' && v.checked
+      )
+      const noSelected = level1List.every((v) => !v.checked && !v.indeterminate)
+      let regionMap = null
+      if (noSelected) {
+        // console.log('no region selected')
+      } else {
+        if (
+          allProvinceCount === allSelectedProvinceCount &&
+          allCountryChecked
+        ) {
+          level1List = []
+        } else {
+          level1List.forEach((province) => {
+            const allCityCount = province.children.filter(
+              (v) => v.name !== '全部'
+            ).length
+            const allSelectedCityCount = province.children.filter(
+              (v) => v.name !== '全部' && v.checked
+            ).length
+            if (allCityCount === allSelectedCityCount) {
+              province.children = []
+            } else {
+              province.children.forEach((city) => {
+                const allDistrictCount = city.children.filter(
+                  (v) => v.name !== '全部'
+                ).length
+                const allSelectedDistrictCount = city.children.filter(
+                  (v) => v.name !== '全部' && v.checked
+                ).length
+                if (allDistrictCount === allSelectedDistrictCount) {
+                  city.children = []
+                }
+              })
+            }
+          })
+        }
+        const formatData = level1List.filter((first) => {
+          return (first.checked || first.indeterminate) && first.name !== '全部'
+        })
+        formatData.forEach((second) => {
+          const secondList = second.children.filter((v) => {
+            return (v.checked || v.indeterminate) && v.name !== '全部'
+          })
+          secondList.forEach((item) => {
+            item.children = item.children.filter((v) => {
+              return v.checked && v.name !== '全部'
+            })
+          })
+          second.children = secondList
+        })
+        regionMap = this.getCitiesToMap(formatData)
+      }
+      return regionMap
+    },
+    setState(data = {}) {
+      this.resetState()
+      if (!data) return
+      if (Object.keys(data).length === 0) {
+        // 选择的全国
+        this.level1List.forEach((province) => {
+          province.checked = true
+          province.indeterminate = false
+          province.children.forEach((city) => {
+            city.checked = true
+            city.indeterminate = false
+            city.children.forEach((country) => {
+              country.checked = true
+              country.indeterminate = false
+            })
+          })
+        })
+      } else {
+        // 选择的省
+        const level1List = this.level1List
+        level1List[0].indeterminate = true
+        level1List.forEach((province) => {
+          for (const key in data) {
+            if (province.name === key) {
+              if (Object.keys(data[key]).length === 0) {
+                // 选择的全省(省下的全部地市)
+                province.checked = true
+                province.children.forEach((city) => {
+                  city.checked = true
+                  city.children.forEach((district) => {
+                    district.checked = true
+                  })
+                })
+              } else {
+                // 选择省下的部分地市
+                province.indeterminate = true
+                province.children.forEach((city) => {
+                  for (const cKey in data[key]) {
+                    if (city.name === cKey) {
+                      if (Object.keys(data[key][cKey]).length === 0) {
+                        // 选择的市下的全部区县
+                        city.checked = true
+                        city.children.forEach((district) => {
+                          district.checked = true
+                        })
+                      } else {
+                        // 选择的市下的部分区县
+                        city.indeterminate = true
+                        city.children.forEach((district) => {
+                          if (data[key][cKey].length === 0) {
+                            district.checked = true
+                          } else {
+                            data[key][cKey].forEach((item) => {
+                              if (item === district.name) {
+                                district.checked = true
+                              }
+                            })
+                          }
+                        })
+                      }
+                    }
+                  }
+                })
+                this.getAllCheckBoxSelectedStatus()
+              }
+            }
+          }
+        })
+      }
+      this.getSelectedTags()
+    },
+    resetState() {
+      this.level1List.forEach((province) => {
+        province.checked = false
+        province.indeterminate = false
+        province.children.forEach((city) => {
+          city.checked = false
+          city.indeterminate = false
+          city.children.forEach((country) => {
+            country.checked = false
+            country.indeterminate = false
+          })
+        })
+      })
+      this.tags = []
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.vip-module {
+  .zhima-bid--icon {
+    color: rgba(201, 143, 55, 1) !important;
+  }
+}
+</style>
+<style lang="scss" scoped>
+::v-deep {
+  .el-dropdown-menu.fixed-dropdown {
+    transform: translate(-20px, 0);
+  }
+  .suffix-icon-container {
+    position: relative;
+    z-index: 99;
+  }
+
+  .input-focus {
+    .el-input__suffix-inner {
+      color: $color_main;
+    }
+  }
+}
+.zhima-bid-help-tip {
+  padding: 2px;
+  line-height: 18px;
+}
+
+.zhima-bid--icon {
+  color: $color_main;
+}
+
+.list-empty-container {
+  padding: 8px;
+}
+
+.dropdown-content--wrap {
+  max-width: 580px;
+  background: #fff;
+  border-radius: 5px;
+  border: 1px solid $color_main;
+  overflow: hidden;
+  .dropdown-content--hd {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 8px;
+    font-size: 14px;
+    line-height: 22px;
+    .dropdown-content--hd-left {
+      margin-right: 12px;
+      flex: 1;
+    }
+    ::v-deep {
+      .el-input__icon {
+        font-size: 16px;
+      }
+    }
+  }
+  .select--result {
+    display: flex;
+    flex-wrap: wrap;
+    padding: 8px 0 0px;
+    max-height: 100px;
+    overflow-y: auto;
+    &::-webkit-scrollbar {
+      width: 4px;
+    }
+  }
+  .dropdown-content--bd {
+    display: flex;
+    align-items: center;
+    border-top: 1px solid #ececec;
+    height: 360px;
+    overflow: hidden;
+  }
+  .level-1-container {
+    min-width: 140px;
+    border-right: 1px solid #ececec;
+  }
+  .level-2-container {
+    min-width: 180px;
+    white-space: nowrap;
+  }
+  .level-3-container {
+    min-width: 220px;
+    white-space: nowrap;
+  }
+  .level-2-container {
+    border-right: 1px solid #ececec;
+  }
+  .module-container {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+  }
+  .module-header {
+    // padding: 12px 11px;
+    // color:#999999;
+    // font-size: 14px;
+    // line-height: 22px;
+  }
+  .module-main {
+    flex: 1;
+    overflow-y: scroll;
+  }
+  .module-item {
+    position: relative;
+    display: flex;
+    align-items: center;
+    padding: 4px 8px;
+    min-height: 30px;
+    font-size: 14px;
+    line-height: 22px;
+    color: #1d1d1d;
+    &.active,
+    &:hover {
+      background: #ececec;
+      cursor: pointer;
+    }
+    .item-name {
+      flex: 1;
+      margin-left: 4px;
+      white-space: pre-wrap;
+    }
+  }
+  ::v-deep {
+    .el-tag {
+      width: auto;
+      max-width: unset !important;
+      margin: 0 0 8px 8px;
+      &.el-tag--info {
+        height: 24px;
+        line-height: 22px;
+        background: #f5f6f7;
+        color: #1d1d1d;
+        border: 1px solid#ececec;
+        font-size: 14px;
+      }
+      .el-tag__close.el-icon-close {
+        background: transparent;
+        color: #aaa;
+        font-size: 15px;
+        right: -4px;
+      }
+      &:hover {
+        color: $color-text--highlight;
+        border-color: $color-text--highlight;
+        background: #fff;
+        cursor: pointer;
+        .el-tag__close {
+          color: $color-text--highlight;
+        }
+      }
+    }
+  }
+  .module-main::-webkit-scrollbar {
+    width: 4px;
+  }
+}
+</style>

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

@@ -43,9 +43,18 @@ export default {
       type: String,
       default: 'winner'
     },
+    allValue: {
+      type: String,
+      default: ''
+    },
     options: {
       type: Array,
-      default: () => []
+      default: () => [
+        // {
+        //   label: 'xx',
+        //   value: 'xx'
+        // }
+      ]
     },
     value: {
       type: [String, Object],
@@ -72,12 +81,23 @@ export default {
     computedVal () {
       if (this.value) {
         const item = this.calcOptions.find(v => v.value === this.value)
-        return item.label
+        if (this.allValue) {
+          if (item.value === this.allValue) {
+            return ''
+          } else {
+            return item.label
+          }
+        } else {
+          return item.label
+        }
       } else {
         return ''
       }
     },
     calcOptions () {
+      if (Array.isArray(this.options) && this.options.length > 0) {
+        return this.options
+      }
       if (this.source === 'winner') {
         return winnerContactData
       } else {
@@ -85,6 +105,9 @@ export default {
       }
     },
     calcPlaceholder () {
+      if (this.placeholder) {
+        return this.placeholder
+      }
       if (this.source === 'winner') {
         return '中标企业联系方式'
       } else {

+ 47 - 2
apps/bigmember_pc/src/components/selector/TimeSelectorContent.vue

@@ -92,7 +92,7 @@ import { DatePicker } from 'element-ui'
 import { dateFormatter } from '@/utils/'
 import moment from 'moment'
 import 'moment/locale/zh-cn'
-import { uniqWith } from 'lodash'
+import { uniqWith, cloneDeep } from 'lodash'
 
 moment.locale('zh-cn')
 const timeSelectMap = {
@@ -276,6 +276,33 @@ const timeSelectMap = {
       selected: false
     }
   ],
+  bidDeadline: [
+    {
+      name: '全部',
+      value: 'all',
+      selected: true
+    },
+    {
+      name: '今天',
+      value: 'today',
+      selected: false
+    },
+    {
+      name: '明天',
+      value: 'tomorrow',
+      selected: false
+    },
+    {
+      name: '未来7天',
+      value: 'next7days',
+      selected: false
+    },
+    {
+      name: '未来30天',
+      value: 'next30days',
+      selected: false
+    }
+  ],
   bidPushTime: [
     {
       name: '全部',
@@ -504,7 +531,7 @@ export default {
       } else {
         timeSelectList = propTimeSelectList
       }
-      return timeSelectList
+      return cloneDeep(timeSelectList)
     },
     calcLastTime() {
       if (this.selectorTime === 'more') {
@@ -539,6 +566,9 @@ export default {
         switch (data.exact) {
           case 'all':
           case 'today':
+          case 'tomorrow':
+          case 'next7days':
+          case 'next30days':
           case 'yesterday':
           case 'lately3':
           case 'lately7':
@@ -653,6 +683,21 @@ export default {
           t.end = moment().endOf('day').format('x')
           break
         }
+        case 'tomorrow': {
+          t.start = moment().add(1, 'days').startOf('day').format('x')
+          t.end = moment().add(1, 'days').endOf('day').format('x')
+          break
+        }
+        case 'next7days': {
+          t.start = moment().format('x')
+          t.end = moment().add(7, 'days').endOf('day').format('x')
+          break
+        }
+        case 'next30days': {
+          t.start = moment().format('x')
+          t.end = moment().add(30, 'days').endOf('day').format('x')
+          break
+        }
         case 'yesterday': {
           t.start = moment().startOf('day').format('x') - durations.day1
           t.end = moment().endOf('day').format('x') - durations.day1

+ 11 - 2
apps/bigmember_pc/src/composables/attachment-download/component/AttachmentDownload.vue

@@ -32,7 +32,7 @@
           </template>
         </div>
       </div>
-      <div class="content-file-attachment-actions">
+      <div class="content-file-attachment-actions" v-if="canShowTip">
         <span
           class="action-button"
           @click="chargeFilePack"
@@ -58,7 +58,7 @@
 <script>
 import { mapState, mapGetters } from 'vuex'
 import { useGetContentAttachment } from '@/composables/attachment-download/'
-import { IsCustomTopNet } from '@/views/article-content/composables/useContentStore'
+import { IsCustomTopNet, IsSunPublishContent } from '@/views/article-content/composables/useContentStore'
 
 export default {
   name: 'AttachmentDownload',
@@ -162,6 +162,9 @@ export default {
       if (IsCustomTopNet.value) {
         return false
       }
+      if (IsSunPublishContent.value) {
+        return false
+      }
       return true
     }
   },
@@ -218,6 +221,9 @@ export default {
       if (IsCustomTopNet.value) {
         return this.downloadFile(file)
       }
+      if (IsSunPublishContent.value) {
+        return this.downloadFile(file)
+      }
       // 大客户直接下载
       if (location.pathname.indexOf('entservice') !== -1) {
         return this.downloadFile(file)
@@ -326,6 +332,9 @@ export default {
         if (IsCustomTopNet.value) {
           skipRefresh = true
         }
+        if (IsSunPublishContent.value) {
+          skipRefresh = true
+        }
 
         if (!skipRefresh) {
           this.refreshResourcePackCount()

+ 12 - 6
apps/bigmember_pc/src/router/modules/search.js

@@ -12,12 +12,12 @@ export default [
     name: 'bidding-search',
     component: () => import('@/views/search/bidding/index.vue')
   },
-  {
-    path: '/search/bidding-test',
-    alias: ['/jylab/supsearch/index.html'],
-    name: 'bidding-search-test',
-    component: () => import('@/views/search/bidding/test.vue')
-  },
+  // {
+  //   path: '/search/bidding-test',
+  //   alias: ['/jylab/supsearch/index.html'],
+  //   name: 'bidding-search-test',
+  //   component: () => import('@/views/search/bidding/test.vue')
+  // },
   //  企业搜索
   {
     path: '/search/ent',
@@ -25,6 +25,12 @@ export default [
     name: 'ent-search',
     component: () => import('@/views/search/ent/index.vue')
   },
+  // 阳光采购
+  {
+    path: '/search/sun',
+    name: 'sun-search',
+    component: () => import('@/views/search/sun/index.vue')
+  },
   // 采购单位搜索
   {
     path: '/search/purchase',

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

@@ -49,6 +49,7 @@ const powerCheckWhiteList = [
   'recommen-list',
   'business_detail',
   'bidding-search',
+  'sun-search',
   'ent-search',
   'purchase-search',
   'supply-search',

+ 6 - 2
apps/bigmember_pc/src/utils/common.js

@@ -14,9 +14,13 @@ Vue.prototype.$checkLogin = function () {
   }
 }
 
-Vue.prototype.$showLoginDialog = function () {
+// openLoginDig(type, redirectUrl, loginCallback)
+// 1. type为true,登录后直接跳转工作桌面
+// 2. redirectUrl为'reload',则登录后刷新当前页面。如果redirectUrl!=='reload',则想该url做location.href跳转
+// 3. loginCallback登录成功的回调
+Vue.prototype.$showLoginDialog = function (type, redirectUrl, loginCallback) {
   try {
-    openLoginDig && openLoginDig(...arguments)
+    openLoginDig && openLoginDig(type, redirectUrl, loginCallback)
   } catch (error) {
     console.log(error)
     location.href = '/notin/page?close_goBack=1'

+ 15 - 0
apps/bigmember_pc/src/utils/format/date.js

@@ -37,6 +37,21 @@ export function calcNotExactTime(exact = 'lately7', unit = 'ms') {
       t.end = dayjs().endOf('day').valueOf() // 当天23:59:59时间戳
       break
     }
+    case 'tomorrow': {
+      t.start = dayjs().add(1, 'days').startOf('day').valueOf()
+      t.end = dayjs().add(1, 'days').endOf('day').valueOf()
+      break
+    }
+    case 'next7days': {
+      t.start = dayjs().valueOf()
+      t.end = dayjs().add(7, 'days').endOf('day').valueOf()
+      break
+    }
+    case 'next30days': {
+      t.start = dayjs().valueOf()
+      t.end = dayjs().add(30, 'days').endOf('day').valueOf()
+      break
+    }
     case 'yesterday': {
       t.start = dayjs().startOf('day').valueOf() - durations.day1
       t.end = dayjs().endOf('day').valueOf() - durations.day1

+ 68 - 0
apps/bigmember_pc/src/utils/format/search-bid-filter.js

@@ -806,6 +806,30 @@ export class FilterHistoryViewModel2AjaxModel {
     }
     return result
   }
+   
+  /**
+   * 三级领域格式化
+   *  String area
+   *  String city
+   * returns Object
+   *
+   * {
+   *    aaaa: {
+   *      aabb: [],
+   *      aacc: []
+   *    },
+   *    bbb: {
+   *      ff: [],
+   *      ff: ['hhh'],
+   *      gg: ['kk']
+   *    },
+   *    eee: {}
+   * }
+   * 
+   */
+  static formatLingyu(p = {}, split = ',') {
+    return threeObjToSingle(p, split)
+  }
 }
 
 /**
@@ -1504,6 +1528,50 @@ export class FilterModel2ViewModel {
   }
 }
 
+function threeObjToSingle(obj, split = ',') {
+  const map = {
+    area: '',
+    city: '',
+    district: ''
+  }
+  if (!obj)
+    return map
+  const area = []
+  const city = []
+  let district = []
+  for (const key in obj) {
+    if (typeof obj[key] === 'object') {
+      if (Object.keys(obj[key]).length === 0) {
+        area.push(key)
+      }
+      else {
+        // 城市项
+        const cityItem = obj[key]
+        for (const cKey in cityItem) {
+          // 区县项
+          const districtItem = cityItem[cKey]
+          if (Array.isArray(districtItem)) {
+            if (districtItem.length === 0) {
+              city.push(cKey)
+            }
+            else {
+              const resetArr = districtItem.map((temp) => {
+                return temp
+              })
+              district = district.concat(resetArr)
+            }
+          }
+        }
+      }
+    }
+  }
+  return {
+    first: area.join(split),
+    second: city.join(split),
+    third: district.join(split)
+  }
+}
+
 /**
  * 三级地区对象转换成,单个area、city、district
  */

+ 5 - 3
apps/bigmember_pc/src/utils/whichContainer.js

@@ -13,10 +13,12 @@ export function GetInWhichContainer() {
 const InWhichContainer = GetInWhichContainer()
 
 // in-iframe 当前页面在 iframe 中打开
-const isInIframe = window.frames.length > 0
+const isInIframe = window.top !== window.self
+
+const inApp = InWhichContainer === 'in-app' || window.self.location.pathname.includes('page_workDesktop')
 
 export const InContainer = {
-  inApp: InWhichContainer === 'in-app',
-  inWeb: InWhichContainer === 'in-web',
+  inApp,
+  inWeb: !inApp && !isInIframe,
   inIframe: isInIframe
 }

+ 68 - 43
apps/bigmember_pc/src/views/article-content/components/ContentHeader.vue

@@ -6,6 +6,7 @@ import ArticleStar from '@/components/push-list/ArticleStar.vue'
 import {
   ContentModel,
   ContentId,
+  IsSunPublishContent,
   useRenMaiModel
 } from '@/views/article-content/composables/useContentStore'
 import { computed, getCurrentInstance, onMounted, ref } from 'vue'
@@ -25,7 +26,17 @@ const vt = computed(() => {
   return useStore().getters['user/vt']
 })
 
-const headerType = '业主委托项目'
+const headerType = computed(() => {
+  if (IsSunPublishContent.value) {
+    return ContentModel.value.publicType
+  } else {
+    return '业主委托项目'
+  }
+})
+
+const sunPublishContent = computed(() => IsSunPublishContent.value)
+
+const headerTypeShow = computed(() => ContentModel.value.isSelfSite || sunPublishContent.value)
 
 const keepLabel = [
   {
@@ -105,7 +116,7 @@ const {
       v-if="InWhichContainer === 'in-web'"
       class="right-work-actions"
     ></WorkspaceButtonGroup>
-    <div class="before-type" v-if="ContentModel.isSelfSite">
+    <div class="before-type" v-if="headerTypeShow && headerType">
       {{ headerType }}
     </div>
     <h1 class="title">
@@ -116,6 +127,7 @@ const {
         <a
           v-if="item.hasLink"
           class="pointer"
+          :class="[item.className]"
           data-link
           :key="index"
           v-drag
@@ -123,7 +135,7 @@ const {
         >
           {{ item.label }}
         </a>
-        <a v-else :key="index + 'no-link'">
+        <a v-else :key="index + 'no-link'" :class="[item.className]">
           {{ item.label }}
         </a>
       </template>
@@ -131,14 +143,17 @@ const {
     <div class="actions-info">
       <div class="time-label">{{ ContentModel.time }}</div>
       <div class="actions" v-if="!isUseBIActions">
-        <div class="action-item" @click="openDistribute" v-if="vt === 'q'">
-          <span class="iconfont icon-shoudongfenfa"></span>
-          <span class="text">分发</span>
-        </div>
-        <div class="action-item" @click="openShare">
-          <span class="iconfont icon-fenxiang"></span>
-          <span class="text">转给同事</span>
-        </div>
+        <template v-if="!sunPublishContent">
+          <div class="action-item" @click="openDistribute" v-if="vt === 'q'">
+            <span class="iconfont icon-shoudongfenfa"></span>
+            <span class="text">分发</span>
+          </div>
+          <div class="action-item" @click="openShare">
+            <span class="iconfont icon-fenxiang"></span>
+            <span class="text">转给同事</span>
+          </div>
+        </template>
+        
 
         <div class="action-item">
           <el-popover
@@ -157,38 +172,41 @@ const {
           </el-popover>
         </div>
 
-        <quick-monitor
-          class="action-item"
-          :cache="true"
-          type="project"
-          :params="ContentId"
-        ></quick-monitor>
+        <template v-if="!sunPublishContent">
+          <quick-monitor
+            class="action-item"
+            :cache="true"
+            type="project"
+            :params="ContentId"
+          ></quick-monitor>
 
-        <div
-          class="action-item"
-          v-if="JoinBidInfo.showParticipate"
-          @click="doChangeJoinBid('in')"
-        >
-          <span class="iconfont icon-canbiao"></span>
-          <span class="text">参标</span>
-        </div>
-        <div
-          class="action-item"
-          v-if="JoinBidInfo.showStopParticipate"
-          @click="doChangeJoinBid('out')"
-        >
-          <span class="iconfont icon-canbiao icon-stop-canbiao"></span>
-          <span class="text">终止参标</span>
-        </div>
+          <div
+            class="action-item"
+            v-if="JoinBidInfo.showParticipate"
+            @click="doChangeJoinBid('in')"
+          >
+            <span class="iconfont icon-canbiao"></span>
+            <span class="text">参标</span>
+          </div>
+          <div
+            class="action-item"
+            v-if="JoinBidInfo.showStopParticipate"
+            @click="doChangeJoinBid('out')"
+          >
+            <span class="iconfont icon-canbiao icon-stop-canbiao"></span>
+            <span class="text">终止参标</span>
+          </div>
 
-        <div class="action-item">
-          <article-star
-            :id="ContentId"
-            :star="starModel.star"
-            @change="doFetchStarState"
-            @change-labels="doFetchStarState"
-          ></article-star>
-        </div>
+          <div class="action-item">
+            <article-star
+              :id="ContentId"
+              :star="starModel.star"
+              @change="doFetchStarState"
+              @change-labels="doFetchStarState"
+            ></article-star>
+          </div>
+        </template>
+        
       </div>
       <div class="actions" v-if="isUseBIActions">
         <content-b-i-actions
@@ -197,7 +215,7 @@ const {
         ></content-b-i-actions>
       </div>
     </div>
-    <div class="expands-info">
+    <div class="expands-info" v-if="!sunPublishContent">
       <div class="join-bid-actions flex flex-(row items-center)">
         <el-tooltip
           class="item"
@@ -240,7 +258,7 @@ const {
         </div>
       </div>
     </div>
-    <div class="expands-actions">
+    <div class="expands-actions" v-if="!sunPublishContent">
       <div class="expands-actions-l"></div>
       <div class="expands-actions-r">
         <el-button
@@ -329,6 +347,13 @@ const {
   }
   .tags {
     margin-top: 12px;
+    .tag-orange {
+      color: #FA6F33;
+      background-color: #FFF1EB;
+      &.border {
+        border: 1px solid #F56500;
+      }
+    }
   }
   .tags a {
     border-radius: 4px;

+ 95 - 1
apps/bigmember_pc/src/views/article-content/components/ContentSummary.vue

@@ -3,6 +3,7 @@ import { computed, getCurrentInstance, ref } from 'vue'
 import CollectInfo from '@/components/collect-info/CollectInfo.vue'
 import {
   IsCustomTopNet,
+  IsSunPublishContent,
   SummaryModel
 } from '@/views/article-content/composables/useContentStore'
 import { chunk, fill, padEnd } from 'lodash'
@@ -24,6 +25,9 @@ const tableConfig = ref({
   ]
 })
 
+const sunPublishContent = computed(() => IsSunPublishContent.value)
+const showHeaderTip = computed(() => !sunPublishContent.value)
+
 const contentSummaryTable = computed(() => {
   const list = SummaryModel.value.list.filter((v) => v.value) || []
   let result = []
@@ -71,6 +75,8 @@ const contentSummaryTable = computed(() => {
   return result
 })
 
+const purchasingList = computed(() => SummaryModel.value.purchasingList)
+
 // 单元格样式
 function getCellClassName({ row, column, rowIndex, columnIndex }) {
   if (columnIndex === 0 || columnIndex === 2) {
@@ -109,7 +115,11 @@ function doOpenCustomer() {
 
 function doOpenItem(item, type = '') {
   if (item.type === 'free-view') {
-    return doOpenCollectDialog('peugeot_view_infor')
+    if (sunPublishContent.value) {
+      return doOpenCollectDialog('pc_sunlight_viewdetails')
+    } else {
+      return doOpenCollectDialog('peugeot_view_infor')
+    }
   }
   const key = item.data.key
   if (key.indexOf('winner') !== -1) {
@@ -143,6 +153,7 @@ function doOpenItem(item, type = '') {
       <div
         class="summary-header-tip color-highlight flex flex-(row items-center)"
         @click="doOpenCollectDialog('pc_article_customization')"
+        v-if="showHeaderTip"
       >
         最近5年招标采购数据均可导出下载,如需更多年份和行业字段您可申请数据定制
         <i class="iconfont icon-more"></i>
@@ -208,6 +219,66 @@ function doOpenItem(item, type = '') {
       </el-table-column>
     </el-table>
 
+    <div class="summary-caigou-list" v-if="purchasingList && purchasingList.length > 0">
+      <div class="summary-caigou-hd">
+        <div class="summary-caigou">采购清单</div>
+      </div>
+      <el-table
+        class="summary-caigou-table"
+        :data="purchasingList"
+        border
+        style="width: 100%"
+      >
+        <el-table-column
+          prop="name"
+          label="序号"
+          width="50"
+          align="center"
+          :resizable="false"
+        >
+          <template slot-scope="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          label="标的名称"
+          prop="itemName"
+        >
+          <template slot-scope="scope">
+            {{ scope.row.itemName || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          label="品牌"
+          prop="brandName"
+        >
+          <template slot-scope="scope">
+            {{ scope.row.brandName || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          label="规格型号"
+          prop="model"
+        >
+          <template slot-scope="scope">
+            {{ scope.row.model || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          header-align="center"
+          label="数量"
+          prop="number"
+        >
+          <template slot-scope="scope">
+            {{ scope.row.numberUnit || '-' }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
     <div class="table-footer-tip">
       *以上摘要信息由剑鱼标讯智能提取,仅供参考。如有误差,请
       <span class="highlight-label" @click="doOpenCustomer">联系客服</span>
@@ -238,6 +309,29 @@ function doOpenItem(item, type = '') {
     }
   }
 
+  .summary-caigou-list {
+    margin-bottom: 16px;
+    .summary-caigou-hd {
+      font-size: 16px;
+      line-height: 24px;
+      margin-bottom: 16px;
+    }
+  }
+
+  .summary-caigou-table {
+    color: #1d1d1d;
+    ::v-deep {
+      .el-table__header {
+        thead {
+          color: #686868;
+        }
+        tr,th {
+          background-color: #F9FAFB;
+        }
+      }
+    }
+  }
+
   ::v-deep {
     .el-table.summary-table {
       margin: 16px 0;

+ 61 - 0
apps/bigmember_pc/src/views/article-content/components/ContentTextMask.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="content-text-mask-container">
+    <div class="content-text-mask">
+      <div class="content-text-mask-bg-container">
+        <img class="mask-img mask-bg" src="@/assets/images/article-mask/content-mask-text@2x.png" />
+      </div>
+      <div class="content-text-card">
+        <img src="@/assets/images/article-mask/content-mask-card@2x.png" alt="">
+        <div class="click-mask pointer" @click="toSee"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ContentTextMask',
+  props: {},
+  data() {
+    return {}
+  },
+  methods: {
+    toSee() {
+      this.$emit('see')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.content-text-mask {
+  position: relative;
+  .content-text-mask-bg-container {
+    position: relative;
+    width: 100%;
+    height: 100%;
+    z-index: 1;
+  }
+  .mask-img {
+    display: block;
+    width: 100%;
+  }
+  .content-text-card {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%,-50%);
+    width: 480px;
+    height: 295px;
+    z-index: 2;
+  }
+  .click-mask {
+    position: absolute;
+    width: 132px;
+    height: 36px;
+    left: 50%;
+    bottom: 20%;
+    transform: translateX(-50%);
+  }
+}
+</style>

+ 7 - 2
apps/bigmember_pc/src/views/article-content/components/RecommendEnt.vue

@@ -1,5 +1,5 @@
 <script setup>
-import { SummaryModel } from '@/views/article-content/composables/useContentStore'
+import { SummaryModel, IsSunPublishContent } from '@/views/article-content/composables/useContentStore'
 import QuickMonitor from '@/composables/quick-monitor/component/QuickMonitor.vue'
 import { computed, ref, watch } from 'vue'
 import { ajaxGetMiniEntInfo } from '@/api/modules/detail'
@@ -82,7 +82,12 @@ const list = computed(() => {
   return result
 })
 
-getRecommendEntInfo(list.value)
+// 阳光直采,不调用企业画像相关接口
+if (IsSunPublishContent.value) {
+  // do something...
+} else {
+  getRecommendEntInfo(list.value)
+}
 
 const recommendEntList = computed(() => {
   return recommendList.value.filter((v) => {

+ 26 - 8
apps/bigmember_pc/src/views/article-content/composables/useContentStore.js

@@ -8,16 +8,16 @@ import {
 } from '@/api/modules/detail'
 import { getUserResource } from '@/api/modules/bi'
 
-function setPageTDK({ title, description, keywords }) {
+function setPageTDK({ title, description, keywords }, doc = document) {
   const descriptionDOM
-    = document.querySelector('meta[name=description]')
-    || document.querySelector('meta[name=Description]')
+    = doc.querySelector('meta[name=description]')
+    || doc.querySelector('meta[name=Description]')
   const keywordsDOM
-    = document.querySelector('meta[name=keywords]')
-    || document.querySelector('meta[name=Keywords]')
+    = doc.querySelector('meta[name=keywords]')
+    || doc.querySelector('meta[name=Keywords]')
 
   if (title) {
-    document.title = title
+    doc.title = title
   }
   try {
     if (description) {
@@ -36,7 +36,9 @@ const AgentInfo = ref({
   token: '',
   baseToken: ''
 })
-const Content = reactive(useContentModel())
+
+const contentModelData = useContentModel()
+const Content = reactive(contentModelData)
 const ContentExpands = reactive(useContentExpandModel())
 
 const ContentPageLoading = ref(true)
@@ -52,6 +54,14 @@ const ContentId = computed(() => {
 const IsCustomTopNet = computed(() => {
   return ContentModel.value.IsCustomTopNet
 })
+const hasPowerToReadSunPublishContent = computed(() => {
+  return ContentModel.value.hasPowerToReadSunPublishContent
+})
+
+// 是否阳光采购发布标讯
+const IsSunPublishContent = computed(() => {
+  return ContentModel.value.IsSunPublishContent
+})
 
 const SummaryModel = computed(() => {
   return Content.model.summary
@@ -91,7 +101,11 @@ async function useContentStore() {
     .then(Content.transformModel)
     .then((model) => {
       ContentPageLoading.value = false
-      setPageTDK(model.content.tdk)
+      if (IsSunPublishContent.value && model.content.changedTitle) {
+        setPageTDK(model.content.tdk, window.top.document)
+      } else {
+        setPageTDK(model.content.tdk)
+      }
     })
 
   if (AgentInfo.value.baseToken && ContentModel.value.isCanRead) {
@@ -117,6 +131,8 @@ async function useContentStore() {
     ContentExpandsModel,
     ContentPageLoading,
     ContentPageExpandsLoading,
+    IsSunPublishContent,
+    hasPowerToReadSunPublishContent,
     IsCustomTopNet
   }
 }
@@ -179,6 +195,8 @@ export {
   ContentPageLoading,
   ContentPageExpandsLoading,
   IsCustomTopNet,
+  hasPowerToReadSunPublishContent,
+  IsSunPublishContent,
   ContentIsError,
   ContentErrorText
 }

+ 125 - 44
apps/bigmember_pc/src/views/article-content/pages/Article.vue

@@ -16,6 +16,7 @@ import CollectInfo from '@/components/collect-info/CollectInfo.vue'
 import Reward from '@/views/article-content/components/Reward.vue'
 import TimeLine from '@/components/time-line/TimeLine.vue'
 import ContentMask from '@/views/article-content/components/ContentMask.vue'
+import ContentTextMask from '@/views/article-content/components/ContentTextMask.vue'
 import Nps from '../components/Nps.vue'
 import adsense from '@/views/order/components/adsense/index.vue'
 import ContentLayout from '@/components/common/ContentLayout.vue'
@@ -32,6 +33,8 @@ import {
   ContentModel,
   ContentId,
   IsCustomTopNet,
+  hasPowerToReadSunPublishContent,
+  IsSunPublishContent,
   ContentPageLoading,
   ContentPageExpandsLoading,
   SummaryModel,
@@ -115,6 +118,12 @@ const tabContentShow = computed(() => {
     computedState['投标服务'] = false
     computedState['客户推荐'] = false
   }
+  // 阳关采购隐藏以下3个模块
+  if (IsSunPublishContent.value) {
+    computedState['商机推荐'] = false
+    computedState['客户推荐'] = false
+    computedState['投标服务'] = false
+  }
   const result = Object.assign({}, tabContentState.value, computedState)
   handleScroll()
   return result
@@ -125,6 +134,43 @@ const tabContentHeaderList = computed(() => {
   })
 })
 
+const showSelfSiteTip = computed(() => {
+  if (IsSunPublishContent.value) {
+    return false
+  } else {
+    return ContentModel.isSelfSite
+  }
+})
+
+// 当未抽取到“采购单位联系方式”时且有原文链接时,显示入库提示语
+const showGetInLibTip = computed(() => {
+  if (IsSunPublishContent.value) {
+    if (ContentModel.value?._summary) {
+      return ContentModel.value.originalShow && !ContentModel.value._summary?.buyerTel
+    } else {
+      return false
+    }
+  } else {
+    return false
+  }
+})
+
+const articleAdMap = computed(() => {
+  if (IsSunPublishContent.value) {
+    return {
+      contentRight: 'jy-sun-pccontent-right',
+      footer: 'jy-sun-pccontent-bottom',
+      footerFixed: 'jy-sun-pc-article-bottom-fixed'
+    }
+  } else {
+    return {
+      contentRight: undefined,
+      footer: 'jy-pccontent-bottom',
+      footerFixed: 'jy-pc-article-bottom-fixed'
+    }
+  }
+})
+
 function doHideTabContent(label) {
   tabContentState.value[label] = false
 }
@@ -171,6 +217,13 @@ const timeLineList = computed(() => {
   )
 })
 
+const showRightTimeLine = computed(() => {
+  if (IsSunPublishContent.value) {
+    return false
+  }
+  return timeLineList.value.length > 0
+})
+
 const isFixedHeader = ref(false)
 
 const handleScroll = throttle((e) => {
@@ -268,15 +321,23 @@ function doOpenCustomer() {
 }
 // 是否显示遮罩
 const canShowMask = computed(() => {
-  let type = 'free-max'
-  if (ContentModel.value.isNiJian) {
-    type = 'proposed'
-  } else if (ContentModel.value.isCaigouyixiang) {
-    type = 'purchase'
-  }
-  return {
-    show: !ContentModel.value.isCanRead,
-    type: type
+  if (IsSunPublishContent.value) {
+    return {
+      show: !ContentModel.value.isCanRead,
+      contentMaskShow: !ContentModel.value.hasPowerToReadSunPublishContent,
+      type: 'content-mask'
+    }
+  } else {
+    let type = 'free-max'
+    if (ContentModel.value.isNiJian) {
+      type = 'proposed'
+    } else if (ContentModel.value.isCaigouyixiang) {
+      type = 'purchase'
+    }
+    return {
+      show: !ContentModel.value.isCanRead,
+      type: type
+    }
   }
 })
 
@@ -396,9 +457,13 @@ const canAddContentMainEvent = computed(() => {
 function doClickFreeView() {
   doOpenCollectDialog('peugeot_view_infor')
 }
+
+const sunLeaveInfo = () => {
+  collectElement.value?.noCallApiFn('pc_sunlight_viewdetails', false)
+}
 </script>
 <template>
-  <ContentLayout :need-ad="true" class="article-page-container">
+  <ContentLayout :need-ad="!ContentPageLoading" class="article-page-container" :ad-code="articleAdMap.contentRight">
     <ContentError v-if="ContentIsError" :error-text="ContentErrorText"></ContentError>
     <el-skeleton
       v-else
@@ -431,7 +496,7 @@ function doClickFreeView() {
 
           <div v-if="!canShowMask.show">
             <!--  顶部提示 -->
-            <div class="content-header-tip" v-if="ContentModel.isSelfSite">
+            <div class="content-header-tip" v-if="showSelfSiteTip">
               <span>
                 该公告由业主方/采购单位直接发布,急寻供应商,立即报名直接联系业主方/采购单位参与投标采购。
               </span>
@@ -486,38 +551,54 @@ function doClickFreeView() {
                 v-if="tabContentShow['公告正文']"
               >
                 <div
-                  class="content-main-container"
-                  :data-content="canAddContentMainEvent"
-                >
-                  <div class="content-block-header">公告正文</div>
-                  <div
-                    class="content-detail-container"
-                    v-event-listener:click="doClickFreeView"
-                    data-event-selector=".free-view"
+                    class="content-main-container"
+                    :data-content="canAddContentMainEvent"
                   >
-                    <pre v-html="ContentModel.contentHighlighted"></pre>
+                    <div class="content-block-header">公告正文</div>
+
+                    <template v-if="canShowMask.contentMaskShow">
+                      <div class="content-mask-container">
+                        <ContentTextMask @see="sunLeaveInfo" />
+                      </div>
+                    </template>
+                    <template v-else>
+                      <div
+                        class="content-detail-container"
+                        v-event-listener:click="doClickFreeView"
+                        data-event-selector=".free-view"
+                      >
+                        <pre v-html="ContentModel.contentHighlighted"></pre>
+                      </div>
+
+                      <div class="origin-detail-action-container">
+                        <el-button
+                          v-if="ContentModel.originalShow"
+                          class="origin-detail-action"
+                          @click="doOpenOriginLink"
+                        >
+                          <span class="iconfont icon-chakanyuanwen"></span>
+                          查看原文链接
+                        </el-button>
+
+                        <span class="origin-link-suffix" v-if="showGetInLibTip">
+                          <span>由于该企业要求必须入库供应商才可以参与报价,请点击入库链接,进行入库申请</span>
+                          <button class="origin-link-button" @click="doOpenOriginLink">点击入库</button>
+                        </span>
+                      </div>
+                      
+
+                      <div>
+                        <attachment-download
+                          :id="ContentId"
+                          :title="ContentModel.title"
+                          :attachment-list="ContentModel.attachments"
+                          @doOpenCollect="doOpenCollectDialog"
+                        ></attachment-download>
+                      </div>
+
+                      <Reward :id="ContentId" />
+                    </template>
                   </div>
-
-                  <el-button
-                    v-if="ContentModel.originalShow"
-                    class="origin-detail-action"
-                    @click="doOpenOriginLink"
-                  >
-                    <span class="iconfont icon-chakanyuanwen"></span>
-                    查看原文链接
-                  </el-button>
-
-                  <div>
-                    <attachment-download
-                      :id="ContentId"
-                      :title="ContentModel.title"
-                      :attachment-list="ContentModel.attachments"
-                      @doOpenCollect="doOpenCollectDialog"
-                    ></attachment-download>
-                  </div>
-
-                  <Reward :id="ContentId" />
-                </div>
               </div>
             </div>
             <!--  nps评分  -->
@@ -569,7 +650,7 @@ function doClickFreeView() {
           </div>
           <!--  内容底部广告  -->
           <div class="article-content-footer-container">
-            <adsense code="jy-pccontent-bottom"></adsense>
+            <adsense :code="articleAdMap.footer"></adsense>
           </div>
         </div>
       </template>
@@ -600,7 +681,7 @@ function doClickFreeView() {
     <!--  底部悬浮广告  -->
     <footer-ad
       v-if="!canShowMask.show"
-      code="jy-pc-article-bottom-fixed"
+      :code="articleAdMap.footerFixed"
     ></footer-ad>
     <!--  查看原文链接  -->
     <origin-link
@@ -609,7 +690,7 @@ function doClickFreeView() {
       :id="ContentId"
       @click-collect="doOpenCollectDialog"
     ></origin-link>
-    <template #right-top v-if="timeLineList.length > 0">
+    <template #right-top v-if="showRightTimeLine">
       <!--  招标/采购进度  -->
       <content-right-time-line
         :content-id="ContentId"

+ 21 - 6
apps/bigmember_pc/src/views/order/components/big-member/info-data.js

@@ -166,8 +166,7 @@ export const serviceListData = [
       },
       {
         two: '招标采购信息发布',
-        three:
-          '认证后的企业,支持发布采购信息、招标预告、招标公告等招标采购信息。',
+        three: '认证后的企业,支持发布招标预告、招标公告等招标采购信息。',
         four: 'icon-tick',
         five: 'icon-tick'
       },
@@ -187,6 +186,22 @@ export const serviceListData = [
       }
     ]
   },
+  {
+    阳光直采: [
+      {
+        two: '采购信息',
+        three: '企业公开直采信息,直接对接采购人。',
+        four: '议价',
+        five: '议价'
+      },
+      {
+        two: '采购发布',
+        three: '采购发布成功后,可收到至少5家供应商参与报价。',
+        four: '限时免费',
+        five: '限时免费'
+      }
+    ]
+  },
   {
     人脉管理: [
       {
@@ -194,25 +209,25 @@ export const serviceListData = [
         three:
           '添加您密切关联的人和企业,自动生成熟人的项目信息,不漏掉任何最大概率能成单的项目。',
         four: '-',
-        five: '议'
+        five: '议'
       },
       {
         two: '潜客挖掘',
         three: '通过密切关联的人和企业,自动生成能够快速对接上的潜在客户。',
         four: '-',
-        five: '议'
+        five: '议'
       },
       {
         two: '商机挖掘',
         three: '通过密切关联的人和企业,自动生成能够快速对接的项目信息。',
         four: '-',
-        five: '议'
+        five: '议'
       },
       {
         two: '找人脉',
         three: '添加项目名称,自动生成精准的联系人通讯录。',
         four: '-',
-        five: '议'
+        five: '议'
       }
     ]
   },

+ 4 - 2
apps/bigmember_pc/src/views/search/bidding/components/search-filter-header.vue

@@ -57,13 +57,15 @@ function resetFilterItem(key) {
           </div>
           <div class="f-h-action">
             <span class="action-item save-item" @click="onSaveFilter">
-              <i class="icon-square-up"></i>保存
+              <i class="iconfont icon-baocun"></i>保存
             </span>
             <span class="action-item reset-item" @click="onResetFilter">
               <i class="iconfont icon-delete"></i>清空
             </span>
             <span class="action-item has-item" @click="onHasFilter">
-              <i class="icon-square-up"></i>已存{{ historyFilterCount || '' }}
+              <i class="iconfont icon-yicun"></i>已存{{
+                historyFilterCount || ''
+              }}
             </span>
           </div>
         </section>

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

@@ -94,6 +94,9 @@ export default function () {
   const isInApp = ref(InContainer.inApp) // InContainer.inApp
   const isInWeb = ref(InContainer.inWeb) // InContainer.inWeb
 
+  // isInApp.value = true
+  // isInWeb.value = false
+
   // 是否是渠道商
   const cooperateCode = ref(false)
   // 一切都好渠道商,是否是渠道商

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

@@ -141,10 +141,10 @@ export function useSearchFilterModel(conf) {
       publishTime: rPublishTime,
       selectType: filterState.value.selectType.join(','),
       subtype: rSubtype,
-      exclusionWords: notkey.join(','), // 排除词
-      buyer: filterState.value.buyer.join(','),
-      winner: filterState.value.winner.join(','),
-      agency: filterState.value.agency.join(','),
+      exclusionWords: notkey?.join(','), // 排除词
+      buyer: filterState.value.buyer?.join(','),
+      winner: filterState.value.winner?.join(','),
+      agency: filterState.value.agency?.join(','),
       industry: !isBidField ? rIndustry : '',
       province: area,
       city,

+ 5 - 0
apps/bigmember_pc/src/views/search/bidding/model/modules/tabs.js

@@ -36,6 +36,11 @@ export function useSearchTabsModel(conf) {
       key: 4,
       link: '/jylab/purSearch/index.html'
     },
+    {
+      label: '阳光直采',
+      key: 7,
+      link: '/swordfish/page_big_pc/search/sun'
+    },
     {
       label: '供应搜索',
       key: 5,

+ 4 - 2
apps/bigmember_pc/src/views/search/components/SearchHeader.vue

@@ -175,13 +175,15 @@ export default {
       })
     },
     mouseoverHandle () {
-      $('.search-right-wx .search-right-wx-inner').css({
+      const wrapper = this.$querySelector('.search-right-wx')
+      $(wrapper).find('.search-right-wx-inner').css({
         "transform":"scale(1)",
         "transition":"transform 0.8s"
       })
     },
     mouseleaveHandle () {
-      $('.search-right-wx .search-right-wx-inner').css({
+      const wrapper = this.$querySelector('.search-right-wx')
+      $(wrapper).find('.search-right-wx-inner').css({
         "transform":"scale(0)",
         "transition":"transform 0.2s"
       })

+ 11 - 3
apps/bigmember_pc/src/views/search/components/search-schema-filter.vue

@@ -54,6 +54,13 @@ const getPrefix = {
   component: 'search-component-',
   slot: 'search-slot-'
 }
+
+const getLabelStyle = (item) => {
+  return item.labelStyle || {}
+}
+const getClassName = (item) => {
+  return item.className || {}
+}
 </script>
 
 <template>
@@ -70,10 +77,11 @@ const getPrefix = {
         v-for="(item, index) in schema"
         :key="getPrefix.item + index"
         :class="{
-        'flex-items-start': showLabel && item.label
-      }"
+          'flex-items-start': showLabel && item.label,
+          ...getClassName(item)
+        }"
       >
-        <div class="search-schema-filter-label" v-if="showLabel && item.label" :style="{'line-height': item.labelHeight}">
+        <div class="search-schema-filter-label" v-if="showLabel && item.label" :style="{'line-height': item.labelHeight, ...getLabelStyle(item) }">
           <slot name="item-label">
             {{ item.label }}
           </slot>

+ 5 - 0
apps/bigmember_pc/src/views/search/ent/model/modules/tabs.js

@@ -20,6 +20,11 @@ export function useSearchTabsModel(conf) {
       key: 4,
       link: '/jylab/purSearch/index.html'
     },
+    {
+      label: '阳光直采',
+      key: 7,
+      link: '/swordfish/page_big_pc/search/sun'
+    },
     {
       label: '供应搜索',
       key: 5,

+ 3 - 1
apps/bigmember_pc/src/views/search/layout/search-list.vue

@@ -83,9 +83,11 @@ function onCurrentChange($event) {
       class="search-header-container flex flex-(row items-center justify-between)"
       :class="{'fixed': tableFixedTop}"
     >
-      <div class="p-l-16px flex flex-(row items-center)" v-if="showSelect">
+      <div class="flex flex-(row items-center)">
         <!--  全选按钮  -->
         <el-checkbox
+          class="p-l-16px"
+          v-if="showSelect"
           :value="isSelectAllCheckbox"
           @change="$emit('doChangeAllSelect', !isSelectAllCheckbox)"
         >

+ 128 - 0
apps/bigmember_pc/src/views/search/sun/components/current-filter-text.vue

@@ -0,0 +1,128 @@
+<script setup>
+import { computed, nextTick, ref } from 'vue'
+const props = defineProps({
+  data: {
+    type: Object,
+    default: {}
+  }
+})
+
+const emit = defineEmits(['closeCallBack'])
+
+function closeCallBack(key) {
+  emit('closeCallBack', key)
+}
+
+// 计算最大宽度
+// function calcItemWidth() {
+//   const ele = document.querySelector('.current-filter-text-box')
+//   if (ele) {
+//     const width = ele.clientWidth
+//     const childrenLen = ele.children.length || 0
+//     const maxWidth = Math.floor(width / 6)
+//     const fValueNode = document.querySelectorAll(
+//       '.current-filter-text-box .f-value'
+//     )
+//     if (childrenLen > 0) {
+//       Array.from(fValueNode).forEach((fNode) => {
+//         fNode.style.maxWidth = maxWidth + 'px'
+//       })
+//     }
+//   }
+// }
+// nextTick(() => {
+//   // calcItemWidth()
+// })
+
+const textKeyObj = computed(() => {
+  return {
+    publishTime: props.data.publishTimeText,
+    selectType: props.data.scopeText,
+    subtype: props.data.infoTypeText,
+    regionMap: props.data.regionMapText,
+    industry: props.data.industryText ? `行业:${props.data.industryText}` : '',
+    fileExists: props.data.fileExistsText,
+    price: props.data.priceText,
+    mobileTag:
+      props.data.mobileTagText && props.data.mobileTagText !== 'all'
+        ? props.data.mobileTagText
+        : '',
+    buyerclass: props.data.buyerClassText
+      ? `采购单位类型:${props.data.buyerClassText}`
+      : '',
+    buyertel: props.data.buyerTelText,
+    winnertel: props.data.winnerTelText,
+    notkey: props.data.notkey ? `排除词:${props.data.notkey}` : '',
+    buyer: props.data.buyer ? `采购单位:${props.data.buyer}` : '',
+    winner: props.data.winner ? `中标企业:${props.data.winner}` : '',
+    agency: props.data.agency ? `招标代理:${props.data.agency}` : ''
+  }
+})
+</script>
+
+<template>
+  <ul class="current-filter-text-box">
+    <template v-for="(value, key) in textKeyObj">
+      <el-tooltip
+        class="f-text-tip"
+        effect="dark"
+        placement="top"
+        :content="value"
+        popper-class="f-text-tip"
+        v-if="value"
+      >
+        <li>
+          <span class="f-value">{{ value }}</span>
+          <i
+            class="iconfont icon-close"
+            @click="closeCallBack(key)"
+            v-if="key !== 'publishTime' && key !== 'selectType'"
+          ></i>
+        </li>
+      </el-tooltip>
+    </template>
+  </ul>
+</template>
+
+<style lang="scss">
+.f-text-tip {
+  max-width: 300px;
+  &.el-tooltip__popper[x-placement^='top'] .popper__arrow {
+    bottom: -5px !important;
+  }
+}
+</style>
+
+<style lang="scss" scoped>
+.current-filter-text-box {
+  max-height: 102px;
+  overflow: hidden;
+  margin-bottom: 4px;
+  li {
+    padding: 0 8px;
+    border-radius: 12px;
+    border: 1px solid #2abed1;
+    background: linear-gradient(90deg, #eaf8fa 0%, #ffffff 100%);
+    color: #2abed1;
+    font-size: 14px;
+    line-height: 22px;
+    margin: 0 12px 12px 0;
+    display: flex;
+    align-items: center;
+    max-width: 223px;
+    .f-value {
+      display: inline-block;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      text-align: justify;
+      max-width: 180px;
+    }
+  }
+  .icon-close {
+    font-size: 16px;
+    margin-left: 10px;
+    cursor: pointer;
+  }
+}
+</style>

+ 258 - 0
apps/bigmember_pc/src/views/search/sun/components/history-filter-dialog.vue

@@ -0,0 +1,258 @@
+<script setup>
+import { SearchBidModel } from '../model'
+
+const { onSelectedFilter, disposeFilterActionModel } = SearchBidModel
+
+const {
+  filterHistoryList: filterData,
+  onHasToggle, // 展开收起
+  onDeleteFilter // 删除单条已存筛选条件
+} = disposeFilterActionModel
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  }
+})
+
+// 关键词
+function formatToSpace(keywords, additionalWords, wordsModeText) {
+  let str = ''
+  if (additionalWords) {
+    if (keywords) {
+      str += ','
+    }
+    str += additionalWords.replace(/,/g, ',') + '(' + wordsModeText + ')'
+  }
+  return str
+}
+
+const emit = defineEmits(['before-close'])
+function beforeClose() {
+  emit('before-close')
+}
+</script>
+
+<template>
+  <!-- 已存筛选弹框 -->
+  <el-dialog
+    custom-class="filter-dialog has-filter-dialog"
+    title="已存筛选条件"
+    :close-on-click-modal="true"
+    :close-on-press-escape="false"
+    width="750"
+    :center="true"
+    :visible.sync="visible"
+    :before-close="beforeClose"
+  >
+    <div class="filter-data-container">
+      <div v-for="item in filterData" class="filter-data-list">
+        <div class="f-l-title">
+          <div style="display: flex; align-items: center; flex: 1">
+            <i
+              class="iconfont icon-xiala"
+              :class="{ 'is-reverse': item.open }"
+              @click="onHasToggle(item)"
+            ></i>
+            <span class="f-l-title-text" @click="onSelectedFilter(item)">
+              {{ item.keywords }}
+              <span v-if="item.additionalWords">{{
+                formatToSpace(
+                  item.keywords,
+                  item.additionalWords,
+                  item.wordsModeText
+                )
+              }}</span>
+            </span>
+          </div>
+          <i class="iconfont icon-delete" @click="onDeleteFilter(item)"></i>
+        </div>
+        <div class="has-search-model">搜索模式:{{ item.searchModeText }}</div>
+        <el-collapse-transition :collapse-transition="false">
+          <div v-show="item.open">
+            <div class="f-l-content" @click="onSelectedFilter(item)">
+              <p class="f-l-c-item">
+                搜索范围:<em class="i-value">{{ item.scopeText }}</em>
+              </p>
+              <p class="f-l-c-item" v-if="item.industry">
+                行业:<em class="i-value">{{ item.industryText }}</em>
+              </p>
+              <p class="f-l-c-item">
+                <span v-if="item.price"
+                  >金额:<em class="i-value">{{ item.priceText }}</em></span
+                >
+                <span v-if="item.publishTimeText"
+                  >发布时间:<em class="i-value">{{
+                    item.publishTimeText
+                  }}</em></span
+                >
+                <span v-if="item.fileExistsText != '全部'"
+                  >附件:<em class="i-value">{{
+                    item.fileExistsText
+                  }}</em></span
+                >
+              </p>
+              <p class="f-l-c-item" v-if="item.regionMapText">
+                地区:<em class="i-value">{{ item.regionMapText }}</em>
+              </p>
+              <p class="f-l-c-item" v-if="item.infoTypeText">
+                信息类型:<em class="i-value">{{ item.infoTypeText }}</em>
+              </p>
+              <p class="f-l-c-item" v-if="item.buyerClassText">
+                采购单位类型:<em class="i-value">{{ item.buyerClassText }}</em>
+              </p>
+              <p class="f-l-c-item">
+                <span v-if="item.buyerTel === 'y'"
+                  >采购单位联系方式:<em class="i-value">{{
+                    item.buyerTelText
+                  }}</em></span
+                >
+                <span v-if="item.winnerTel === 'y'"
+                  >中标单位联系方式:<em class="i-value">{{
+                    item.winnerTelText
+                  }}</em></span
+                >
+                <span v-if="item.notkey"
+                  >排除词:<em class="i-value">{{ item.notkey }}</em></span
+                >
+              </p>
+              <p class="f-l-c-item" v-if="item.buyer">
+                采购单位:<em class="i-value">{{ item.buyer }}</em>
+              </p>
+              <p class="f-l-c-item" v-if="item.winner">
+                中标企业:<em class="i-value">{{ item.winner }}</em>
+              </p>
+              <p class="f-l-c-item" v-if="item.agency">
+                招标代理:<em class="i-value">{{ item.agency }}</em>
+              </p>
+            </div>
+          </div>
+        </el-collapse-transition>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+::v-deep {
+  .filter-dialog {
+    padding: 32px;
+    border-radius: 8px !important;
+    & ::-webkit-scrollbar {
+      /*滚动条整体样式*/
+      width: 2px !important;
+    }
+  }
+  .el-dialog {
+    width: 750px !important;
+  }
+  .el-dialog__body {
+    padding: 0;
+  }
+}
+.filter-dialog {
+  .el-dialog__header {
+    padding: 0;
+    .el-dialog__title {
+      color: #1d1d1d;
+    }
+  }
+  .el-dialog__body {
+    width: 686px;
+    overflow: hidden;
+  }
+  .filter-data-container {
+    max-height: 400px;
+    overflow-y: scroll;
+    //width: 694px;
+
+    .f-l-title {
+      padding: 8px 0 2px;
+    }
+    .f-l-content {
+      padding: 12px 0;
+      border-top: 1px dashed #ddd;
+      font-size: 12px;
+      color: #686868;
+      cursor: pointer;
+    }
+
+    .filter-data-list {
+      margin-top: 12px;
+      padding: 0 16px;
+      background: #f5f6f7;
+      border-radius: 4px;
+      .has-search-model {
+        padding: 0 0 8px 22px;
+        font-size: 12px;
+        line-height: 18px;
+      }
+      .f-l-title {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        cursor: pointer;
+        font-size: 14px;
+        min-height: 22px;
+        color: #1d1d1d;
+        .f-l-title-text {
+          flex: 1;
+          text-align: justify;
+          font-size: 14px;
+          line-height: 22px;
+          max-width: 582px;
+        }
+      }
+      .f-l-title:hover {
+        .f-l-title-text {
+          color: #2cb7ca;
+        }
+        .icon-xiala {
+          color: #2cb7ca;
+        }
+        .icon-delete {
+          font-size: 18px;
+          color: #2cb7ca;
+        }
+      }
+      .f-l-c-item {
+        margin-top: 8px;
+        line-height: 18px;
+        span {
+          margin-right: 16px;
+        }
+        .i-value {
+          color: #1d1d1d;
+        }
+      }
+    }
+  }
+}
+.has-filter-dialog {
+  .el-dialog__headerbtn {
+    font-size: 18px;
+    top: 34px;
+    right: 32px;
+  }
+}
+
+.icon-xiala {
+  display: inline-block;
+  font-size: 18px;
+  flex-shrink: 0;
+  transform: rotate(0deg);
+  transition: transform 0.5s;
+  margin-right: 4px;
+  cursor: pointer;
+  &.is-reverse {
+    transform: rotate(180deg);
+  }
+}
+.icon-delete {
+  display: inline-block;
+  font-size: 22px;
+  color: #aeaeae;
+  cursor: pointer;
+}
+</style>

+ 610 - 0
apps/bigmember_pc/src/views/search/sun/components/recommend-card.vue

@@ -0,0 +1,610 @@
+<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-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-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-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-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-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-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>
+

+ 223 - 0
apps/bigmember_pc/src/views/search/sun/components/save-filter-dialog.vue

@@ -0,0 +1,223 @@
+<script setup>
+import { computed } from 'vue'
+import { SearchBidModel } from '../model'
+const { disposeFilterActionModel } = SearchBidModel
+const { viewFilterParams: currentFilter } = disposeFilterActionModel
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    default: false
+  }
+})
+// 关键词
+const additionalWordsCon = computed(() => {
+  let str = ''
+  if (currentFilter.value.additionalWords) {
+    if (currentFilter.value.keywords) {
+      str += ','
+    }
+    str +=
+      currentFilter.value.additionalWords.replace(/,/g, ',') +
+      '(' +
+      currentFilter.value.wordsModeText +
+      ')'
+  }
+  return str
+})
+
+// 处理筛选数据-英文逗号转空格
+function formatToSpace(val) {
+  if (!val) return
+  return val.replace(/,/g, ' ')
+}
+
+const emit = defineEmits(['cancel', 'confirm'])
+function cancelHandle() {
+  emit('cancel')
+}
+function confirmHandle() {
+  emit('confirm')
+}
+</script>
+
+<template>
+  <!-- 保存筛选弹框 -->
+  <el-dialog
+    custom-class="filter-dialog save-filter-dialg"
+    title="保存筛选条件"
+    :show-close="false"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    width="750"
+    :center="true"
+    :visible.sync="visible"
+  >
+    <div class="filter-save-item">
+      <div class="save-label">关键词:</div>
+      <div class="save-value">
+        {{ currentFilter.keywords }}
+        <span v-if="additionalWordsCon">{{ additionalWordsCon }}</span>
+        <div class="search_model">
+          搜索模式:{{ currentFilter.searchModeText }}
+        </div>
+      </div>
+    </div>
+    <div class="filter-save-item">
+      <div class="save-label">筛选条件:</div>
+      <div class="save-value">
+        <div class="save-value-bg">
+          <span>搜索范围:</span>{{ currentFilter.scopeText }}
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.industry">
+          <span>行业:</span>{{ currentFilter.industryText }}
+        </div>
+        <div style="display: flex">
+          <div class="save-value-bg" v-if="currentFilter.priceText">
+            <span>金额:</span>{{ currentFilter.priceText }}
+          </div>
+          <div class="save-value-bg" v-if="currentFilter.publishTime">
+            <span>发布时间:</span>{{ currentFilter.publishTimeText }}
+          </div>
+          <div
+            class="save-value-bg"
+            v-if="currentFilter.fileExistsText != '全部'"
+          >
+            <span>附件:</span>{{ currentFilter.fileExistsText }}
+          </div>
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.regionMapText">
+          <span>地区:</span>{{ currentFilter.regionMapText }}
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.infoType">
+          <span>信息类型:</span>{{ currentFilter.infoTypeText }}
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.buyerClass">
+          <span>采购单位类型:</span>{{ currentFilter.buyerClassText }}
+        </div>
+        <div style="display: flex">
+          <div class="save-value-bg" v-if="currentFilter.buyerTel">
+            <span>采购单位联系方式:</span>{{ currentFilter.buyerTelText }}
+          </div>
+          <div class="save-value-bg" v-if="currentFilter.winnerTel">
+            <span>中标单位联系方式:</span>{{ currentFilter.winnerTelText }}
+          </div>
+          <div class="save-value-bg" v-if="currentFilter.notkey">
+            <span>排除词:</span>{{ formatToSpace(currentFilter.notkey) }}
+          </div>
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.buyer">
+          <span>采购单位:</span>{{ formatToSpace(currentFilter.buyer) }}
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.winner">
+          <span>中标企业:</span>{{ formatToSpace(currentFilter.winner) }}
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.agency">
+          <span>招标代理:</span>{{ formatToSpace(currentFilter.agency) }}
+        </div>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button
+        type="primary"
+        class="btn-group confirm-btn"
+        @click="confirmHandle"
+        >确 定</el-button
+      >
+      <el-button class="btn-group cancel-btn" @click="cancelHandle"
+        >取 消</el-button
+      >
+    </span>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+::v-deep {
+  .filter-dialog {
+    padding: 32px;
+    border-radius: 8px !important;
+  }
+  .el-dialog {
+    width: 750px !important;
+  }
+}
+
+.filter-dialog {
+  width: 750px;
+  .el-dialog__header {
+    padding: 0;
+    .el-dialog__title {
+      color: #1d1d1d;
+    }
+  }
+  &.save-filter-dialog {
+    .el-dialog__body {
+      padding: 0 0 32px !important;
+    }
+  }
+  .btn-group {
+    width: 132px;
+    height: 36px;
+    padding: 0;
+    border-radius: 6px;
+    font-size: 16px;
+  }
+  .btn-group.confirm-btn {
+    background: #2cb7ca;
+    margin-right: 52px;
+    border: 0;
+    color: #fff;
+  }
+  .btn-group.confirm-btn:hover,
+  .btn-group.confirm-btn:focus {
+    color: #fff;
+  }
+  .btn-group.cancel-btn:hover {
+    background: unset;
+    border-color: #dcdfe6;
+    color: #606266;
+  }
+  .filter-save-item {
+    display: flex;
+    margin-top: 20px;
+    line-height: 18px;
+
+    .save-label {
+      min-width: 60px;
+      text-align: right;
+      color: #636467;
+      font-size: 12px;
+    }
+
+    .save-value {
+      margin-left: 8px;
+      flex: 1;
+      color: #1d1d1d;
+      text-align: left;
+      font-size: 12px;
+    }
+
+    .save-value-bg {
+      margin-bottom: 8px;
+      margin-right: 8px;
+      padding: 6px 8px;
+      background: #f5f6f7;
+      border-radius: 4px;
+      font-size: 12px;
+      color: #1d1d1d;
+      line-height: 18px;
+
+      & > span {
+        color: #636467;
+      }
+    }
+
+    .search_model {
+      margin-top: 4px;
+      font-size: 12px;
+      line-height: 18px;
+      color: #686868;
+    }
+  }
+}
+</style>

+ 193 - 0
apps/bigmember_pc/src/views/search/sun/components/search-bid-filter.vue

@@ -0,0 +1,193 @@
+<script setup>
+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'
+
+const {
+  isVip,
+  isLogin,
+  isInApp,
+  isInWeb,
+  inBIPropertyIframe,
+  inResourceBIIframe,
+  activeTab,
+  guideGoWorkSpace,
+  filterState,
+  updateFilterBase,
+  doQuery,
+  goLogin,
+  disposeFilterSchema,
+  onChangeTab
+} = SearchBidModel
+
+const {
+  SearchBidBaseSchema,
+  SearchBidMoreSchema,
+  searchBidMoreFreeSchema,
+  searchBidMoreVipSchema,
+  doUpdateData
+} = disposeFilterSchema()
+
+watch(activeTab, (newVal) => {
+  doUpdateData()
+})
+
+
+
+const customMoreSchema = computed(() => {
+  return {
+    vipModuleShow: true,
+    commonConf: {
+      showLabel: false,
+      styleType: 'row'
+    },
+    freeConf: {
+      showRowLabel: true,
+      rowLabelText: '更多筛选:',
+      schema: searchBidMoreFreeSchema.value
+    },
+    vipConf: {
+      schema: searchBidMoreVipSchema.value
+    }
+  }
+})
+
+function noPower() {
+  $bus.$emit('search:filter:no-power')
+}
+
+function toLogin() {
+  $bus.$emit('bidding:goLogin')
+}
+
+function doChangeFilter() {
+  doQuery()
+}
+</script>
+
+<template>
+  <div class="search-bid-filter">
+    <div class="guide-go-workspace" v-if="isInWeb && false">
+      最近五年数据查询以及更多筛选条件请
+      <span class="highlight-text">"</span>
+      <span class="cursor-button highlight-text" @click="guideGoWorkSpace"
+        >{{ isLogin ? '' : '登录后' }}进入工作台</span
+      >
+      <span class="highlight-text">"</span>
+      检索
+    </div>
+    <!--  标准筛选  -->
+    <search-schema-filter
+      v-model="filterState"
+      :schema="SearchBidBaseSchema"
+      @change="doChangeFilter"
+    ></search-schema-filter>
+
+    <!--  更多筛选  -->
+    <div class="more-filters-container" :class="{ 'wrap-line': !isVip }">
+      <search-schema-filter
+        v-if="isVip && isInApp"
+        v-model="filterState"
+        :schema="SearchBidMoreSchema"
+        :show-label="false"
+        showRowLabel
+        style-type="row"
+        @change="doChangeFilter"
+      >
+        <span slot="row-label-text">更多筛选:</span>
+      </search-schema-filter>
+      <template v-else>
+        <SelectorWithBasePower
+          :component="SearchSchemaFilter"
+          :commonConf="customMoreSchema.commonConf"
+          :freeConf="customMoreSchema.freeConf"
+          :vipConf="customMoreSchema.vipConf"
+          v-model="filterState"
+          vipMaskShow
+          :baseMaskShow="false"
+          :vipModuleShow="isInApp && !inBIPropertyIframe"
+          @clickVipMask="noPower"
+          @clickBaseMask="toLogin"
+          @change="doChangeFilter"
+        ></SelectorWithBasePower>
+      </template>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+@media (min-width: 1456px) {
+  .in-app {
+    .search-bid-filter {
+      .wrap-line {
+        ::v-deep {
+          .vip-module {
+            margin-top: 4px;
+            margin-left: 0;
+          }
+        }
+      }
+    }
+  }
+}
+.search-bid-filter {
+  position: relative;
+  padding: 16px 32px;
+  .wrap-line {
+    ::v-deep {
+      .filter-layout .select-prefix {
+        background: transparent;
+      }
+      .vip-module {
+        margin-top: 4px;
+        margin-left: 82px;
+      }
+    }
+  }
+  .guide-go-workspace {
+    left: 345px;
+    position: absolute;
+    border-radius: 4px;
+    background: linear-gradient(
+      270deg,
+      rgba(42, 190, 209, 0.24) 1.5%,
+      rgba(42, 190, 209, 0.12) 97.45%
+    );
+    padding: 2px 8px;
+    color: #1d1d1d;
+    font-size: 16px;
+    font-style: normal;
+    font-weight: 400;
+    line-height: 22px;
+
+    .highlight-text {
+      color: #2abed1;
+      font-weight: 700;
+      font-family:
+        -apple-system,
+        system-ui,
+        BlinkMacSystemFont,
+        Helvetica Neue,
+        PingFang SC,
+        Hiragino Sans GB,
+        Microsoft YaHei,
+        Arial,
+        sans-serif;
+    }
+    .cursor-button {
+      text-decoration-line: underline;
+      background: transparent;
+      padding: 0;
+      cursor: pointer;
+    }
+  }
+  .more-filters-container {
+    margin-top: 6px;
+    margin-right: 38px;
+  }
+}
+</style>

+ 209 - 0
apps/bigmember_pc/src/views/search/sun/components/search-bid-header.vue

@@ -0,0 +1,209 @@
+<script setup>
+import { getCurrentInstance, computed } from 'vue'
+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 TooltipHelpClass from './tooltip-help-class.vue'
+import { SearchBidModel } from '../model/index'
+import $bus from '@/utils/bus'
+
+const {
+  isInApp,
+  isLogin,
+  isInBI,
+  inResourceBIIframe,
+  inBIPropertyIframe,
+  isBidField,
+  cooperateCode,
+  inputKeywordsState,
+  doQuery,
+  onChangeTab,
+  searchModelOptions,
+  SearchTabsModel,
+  goWorkSpace,
+  clearHistoryQuery,
+  searchHistoryList,
+  toggleFilter,
+  showFilter
+} = SearchBidModel
+
+const { searchTabs } = SearchTabsModel
+
+const that = getCurrentInstance().proxy
+
+function doSearch($event) {
+  const firstSearch = !$event ? 'firstSearch' : undefined
+
+  return doQuery({}, firstSearch)
+}
+// 跳转信息发布
+function goToPublish() {
+  if (!isLogin.value) {
+    location.href = '/swordfish/frontPage/InformationDistribution/free/index'
+    return
+  }
+  // 工作台内
+  if (isInApp.value) {
+    that.$BRACE.methods.open({
+      route: {
+        link: '/swordfish/page_web_pc/issued/info',
+        appType: 'iframe'
+      }
+    })
+  } else {
+    window.open('/swordfish/page_web_pc/issued/info')
+  }
+}
+
+function checkPower($event) {
+  return true
+  if (!isLogin.value) {
+    $bus.$emit('bidding:goLogin')
+    return false
+  } else {
+    return true
+  }
+}
+// 未登录--多个关键词切换处理
+function onSelectMoreKey() {
+  // if (!isLogin.value) {
+  //   inputKeywordsState.value.selectMoreKey = false
+  //   $bus.$emit('bidding:goLogin')
+  // }
+}
+
+const showNewPublish = computed(() => {
+  // return !isInBI.value && !isBidField
+  return false
+})
+
+function onClearSearchHistory() {
+  if (!searchHistoryList.value || searchHistoryList.value.length === 0) return
+  clearHistoryQuery({ type: 6 })
+}
+</script>
+
+<template>
+  <div class="search-bid-header">
+    <search-header-card
+      v-model="inputKeywordsState.input"
+      :tabs="searchTabs"
+      placeholder="请输入产品或业务名称关键词,例如:钢板"
+      @change-tab="onChangeTab"
+      @search="doSearch"
+      :show-wx-qr="false"
+      :show-workspace-button="isLogin && !isInApp"
+      :showTab="!inBIPropertyIframe"
+      :historyEnabled="true"
+      @goWorkSpace="goWorkSpace"
+      @clearSearch="onClearSearchHistory"
+      :immediateSearch="false"
+    >
+      <template slot="tab-other" v-if="showNewPublish">
+        <!-- 工作台内外样式不同-->
+        <div class="search-tab-right news-publish-btn">
+          <el-button
+            class="use-badge"
+            data-badge="限免"
+            :class="{ outside: !isInApp }"
+            type="primary"
+            @click="goToPublish"
+          >
+            {{ isLogin ? '信息发布' : '免费发布信息' }}
+          </el-button>
+        </div>
+      </template>
+      <div class="flex flex-(row items-center)">
+        <div class="flex flex-(row items-center)">
+          <common-single-choice
+            class="m-l-6px"
+            v-model="inputKeywordsState.searchMode"
+            placeholder="精准搜索"
+            :options="searchModelOptions"
+            @change="doSearch"
+            :beforeChange="checkPower"
+          ></common-single-choice>
+
+          <TooltipHelpClass />
+        </div>
+
+        <el-checkbox
+          class="m-l-16px"
+          v-model="inputKeywordsState.selectMoreKey"
+          @input="onSelectMoreKey"
+        >
+          多个关键词
+        </el-checkbox>
+        <div class="show-filter-handle m-l-16px" @click="toggleFilter">
+          <span>筛选</span>
+          <i
+            class="iconfont"
+            :class="showFilter ? 'icon-a-zhankai1' : 'icon-shouqi1'"
+          ></i>
+        </div>
+      </div>
+    </search-header-card>
+    <keyword-tags-pc
+      class="p-l-32px p-b-16px"
+      v-if="inputKeywordsState.selectMoreKey"
+      :list="inputKeywordsState.additionalWords"
+      placeholder="请输入关键词"
+      @change="doSearch"
+    >
+      <div
+        class="m-l-16px"
+        slot="radio"
+        v-show="inputKeywordsState.additionalWords.length > 0"
+      >
+        <el-radio-group
+          class="keyword-radio-group"
+          v-model="inputKeywordsState.wordsMode"
+          @input="doSearch"
+        >
+          <el-radio :label="0">包含所有关键词</el-radio>
+          <el-radio :label="1">包含任意关键词</el-radio>
+        </el-radio-group>
+      </div>
+    </keyword-tags-pc>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.search-bid-header {
+  .show-filter-handle {
+    font-size: 14px;
+    line-height: 22px;
+    color: #1d1d1d;
+    cursor: pointer;
+    .iconfont {
+      color: #686868;
+      font-size: 16px;
+      margin-left: 4px;
+    }
+    &::before {
+      content: '';
+      display: inline-block;
+      height: 14px;
+      width: 1px;
+      background: #ececec;
+      margin-right: 16px;
+      vertical-align: middle;
+    }
+  }
+  .news-publish-btn {
+    .el-button.outside {
+      font-size: 16px;
+      line-height: 24px;
+      border-radius: 4px;
+      padding: 4px 8px;
+      margin-left: 12px;
+    }
+    .el-button {
+      border-radius: 8px;
+      font-size: 14px;
+      line-height: 22px;
+      padding: 7px 24px;
+    }
+  }
+}
+</style>

+ 183 - 0
apps/bigmember_pc/src/views/search/sun/components/search-filter-header.vue

@@ -0,0 +1,183 @@
+<script setup>
+import { ref } from 'vue'
+import SaveFilterDialog from '../components/save-filter-dialog.vue'
+import HistoryFilterDialog from '../components/history-filter-dialog.vue'
+import CurrentFilterText from '../components/current-filter-text'
+import { SearchBidModel } from '../model/index'
+const {
+  isLogin,
+  isInApp,
+  inBIPropertyIframe,
+  disposeFilterActionModel,
+  onSaveFilter,
+  onResetFilter,
+  toggleFilter,
+  fixedTop,
+  showCurrentFilterText: filterText,
+  doQuery,
+  updateFilterBase
+} = SearchBidModel
+
+const {
+  historyFilterCount, // 已存筛选条件个数
+  saveFilterDialogVisible, // 保存筛选条件弹窗
+  historyFilterDialogVisible, //已存筛选条件弹窗
+  saveFilterConfirm, // 确认保存筛选条件
+  saveFilterCancel, // 取消保存筛选条件
+  onHasFilter // 已存筛选条件操作
+} = disposeFilterActionModel
+
+function closeHistoryFilterDialog() {
+  historyFilterDialogVisible.value = false
+}
+
+// 已选筛选,删除某一项筛选项
+function resetFilterItem(key) {
+  updateFilterBase({ key }, 'resetItem')
+  doQuery({})
+}
+</script>
+
+<template>
+  <div class="searchTender">
+    <div
+      class="filter-header-container"
+      :class="{ 'header-fixed': fixedTop }"
+      v-if="false"
+    >
+      <div class="filter-header" :class="{ 'fixed-top': fixedTop }">
+        <!--        已选条件-->
+        <section v-if="isLogin && isInApp && !inBIPropertyIframe">
+          <div class="f-h-current">
+            <span class="f-h-c-label">
+              <span>已选条件:</span>
+            </span>
+            <div class="f-h-c-container">
+              <current-filter-text
+                :data="filterText"
+                @closeCallBack="resetFilterItem"
+              ></current-filter-text>
+            </div>
+          </div>
+          <div class="f-h-action">
+            <span class="action-item save-item" @click="onSaveFilter">
+              <i class="iconfont icon-baocun"></i>保存
+            </span>
+            <span class="action-item reset-item" @click="onResetFilter">
+              <i class="iconfont icon-delete"></i>清空
+            </span>
+            <span class="action-item has-item" @click="onHasFilter">
+              <i class="iconfont icon-yicun"></i>已存{{
+                historyFilterCount || ''
+              }}
+            </span>
+          </div>
+        </section>
+      </div>
+    </div>
+    <save-filter-dialog
+      v-if="saveFilterDialogVisible"
+      :visible="saveFilterDialogVisible"
+      @cancel="saveFilterCancel"
+      @confirm="saveFilterConfirm"
+    ></save-filter-dialog>
+    <history-filter-dialog
+      v-if="historyFilterDialogVisible"
+      :visible="historyFilterDialogVisible"
+      @before-close="closeHistoryFilterDialog"
+    >
+    </history-filter-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+$mainColor: #2abed1;
+//.in-app {
+//  .filter-header-container.header-fixed {
+//    position: fixed;
+//    top: 0;
+//    left: 0;
+//    width: 100%;
+//    padding: 0 24px;
+//    box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.05);
+//    background: #ffffff;
+//    z-index: 99;
+//  }
+//  .filter-header-container.header-fixed .fixed-top {
+//    position: relative;
+//    width: auto;
+//    top: unset;
+//    border-bottom: unset;
+//  }
+//}
+.filter-header {
+  width: 100%;
+  padding: 0 32px;
+  background: #fff;
+
+  //&.fixed-top {
+  //  position: fixed;
+  //  top: 52px;
+  //  width: 1200px;
+  //  z-index: 99;
+  //}
+  section {
+    display: flex;
+    border-bottom: 1px solid #ececec;
+  }
+
+  .f-h-current {
+    flex: 1;
+    display: flex;
+    margin-bottom: 4px;
+    .f-h-c-label {
+      font-size: 14px;
+      line-height: 22px;
+      color: #686868;
+      margin-right: 12px;
+    }
+    .f-h-c-container {
+      flex: 1;
+    }
+  }
+  .f-h-action {
+    display: flex;
+    align-items: center;
+    width: 186px;
+    margin-left: 13px;
+    align-self: flex-start;
+
+    .action-item {
+      cursor: pointer;
+      font-size: 14px;
+      line-height: 22px;
+      color: #2abed1;
+      display: flex;
+      align-items: center;
+      &:not(:last-of-type) {
+        margin-right: 12px;
+      }
+      i {
+        margin-right: 4px;
+      }
+    }
+  }
+  .icon-square-up {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    background: url(@/assets/images/icon/square-up.png) no-repeat center;
+    background-size: contain;
+  }
+  //.icon-xiala {
+  //  display: inline-block;
+  //  font-size: 16px;
+  //  flex-shrink: 0;
+  //  transform: rotate(0deg);
+  //  transition: transform 0.5s;
+  //  &.is-reverse {
+  //    transform: rotate(180deg);
+  //  }
+  //}
+}
+</style>

+ 260 - 0
apps/bigmember_pc/src/views/search/sun/components/search-list-table.vue

@@ -0,0 +1,260 @@
+<script setup>
+import { ref, computed } from 'vue'
+import { SearchBidModel } from '../model'
+const { onClickDataExport } = SearchBidModel
+import {
+  dateFromNow,
+  replaceKeyword,
+  moneyUnit,
+  openLinkInWorkspace
+} from '@/utils/'
+
+const props = defineProps({
+  list: {
+    type: Array,
+    default: () => []
+  },
+  listState: {
+    type: Object,
+    default: () => ({
+      finished: false, // 是否已经搜索过
+      loading: false,
+      pageNum: 1, // 当前页
+      pageSize: 50, // 每页多少条数据
+      total: 0 // 一共多少条数据
+    })
+  },
+  matchKeys: {
+    type: Array,
+    default: () => []
+  },
+  tableFixedTop: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const showTableMore = computed(() => {
+  return props.list.length >= 20 && props.listState.total > 20
+})
+
+function calcTitle(item) {
+  if (!item.projectName) {
+    item.projectName = item.title
+  }
+  const { projectName, matchKeys } = item
+  const highKeys = getMatchKeys(matchKeys || [])
+  const hightLightedTitle = replaceKeyword(projectName, highKeys, [
+    '<span class="highlight-text-orange-bd">',
+    '</span>'
+  ])
+  return `${hightLightedTitle}`
+}
+
+function calcMoney(budget) {
+  if (budget && isNaN(budget)) {
+    return budget
+  } else if (budget && !isNaN(budget) && budget !== '0') {
+    return (budget / 10000).toFixed(2).replace('.00', '')
+  } else {
+    return ''
+  }
+}
+function getMatchKeys(matchKeys) {
+  return props.matchKeys.concat(matchKeys)
+}
+
+const emit = defineEmits(['before-close'])
+function toDetail(item) {
+  emit('to-detail', item)
+}
+</script>
+
+<template>
+  <div
+    class="info-list search-table-list"
+    element-loading-background="rgba(255,255,255, .4)"
+    element-loading-custom-class="self-export-loading"
+  >
+    <div class="fixed-table" v-if="tableFixedTop">
+      <table class="table">
+        <thead class="thead fixed-head">
+          <tr>
+            <td width="49.7">序号</td>
+            <td width="326.4" class="deep-border">项目名称</td>
+            <td width="87">公告类型</td>
+            <td width="75.6" class="deep-border">预算<br />(万元)</td>
+            <td width="187">招标单位</td>
+            <td width="106.7" class="deep-border">开标日期</td>
+            <td width="180.3">中标单位</td>
+            <td width="78.7" class="deep-border">中标金额<br />(万元)</td>
+            <td width="106.8">发布日期</td>
+          </tr>
+        </thead>
+      </table>
+    </div>
+    <table class="table" v-show="list.length">
+      <thead class="thead">
+        <tr>
+          <td width="48">序号</td>
+          <td width="315" class="deep-border">项目名称</td>
+          <td width="84">公告类型</td>
+          <td width="73" class="deep-border">预算<br />(万元)</td>
+          <td width="181">招标单位</td>
+          <td width="103" class="deep-border">开标日期</td>
+          <td width="174">中标单位</td>
+          <td width="76" class="deep-border">中标金额<br />(万元)</td>
+          <td width="103">发布日期</td>
+        </tr>
+      </thead>
+      <tbody>
+        <tr
+          v-for="(item, index) in list"
+          :class="{ visited: item.visited }"
+          :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 ? 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">
+            {{
+              dateFromNow(
+                item.bidOpenTime ? item.bidOpenTime * 1000 : null,
+                'yyyy-MM-dd HH:mm'
+              )
+            }}
+          </td>
+          <td width="174" class="tt-l">{{ item.winner }}</td>
+          <td width="76" class="tt-r">{{ calcMoney(item.bidAmount) }}</td>
+          <td width="103">{{ dateFromNow(item.publishTime * 1000) }}</td>
+        </tr>
+      </tbody>
+    </table>
+    <div class="shade_table" v-if="showTableMore">
+      <div
+        class="more"
+        data-need-bind-phone=""
+        @click="onClickDataExport('table')"
+      >
+        查看更多&gt;
+      </div>
+    </div>
+    <div class="shade_table_blank" v-if="list.length"></div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+/* table */
+.in-app {
+  .search-table-list {
+    .fixed-table {
+      width: 100%;
+      background: #fff;
+      top: 48px;
+      left: 0;
+      padding: 0 24px;
+      box-sizing: border-box;
+    }
+  }
+}
+
+.search-table-list {
+  /*全文搜索 表格*/
+  width: 100%;
+  position: relative;
+  //border-bottom: 1px solid #e0e0e0;
+  table {
+    width: 100%;
+    border-collapse: collapse;
+    table-layout: fixed;
+    tr {
+      td {
+        border: 1px solid #e0e0e0;
+        vertical-align: middle;
+        text-align: center;
+        line-height: 26px;
+      }
+      td.tt-l {
+        text-align: left;
+      }
+      td.tt-c {
+        text-align: center;
+      }
+      td.tt-r {
+        text-align: right;
+      }
+    }
+  }
+  .thead {
+    tr {
+      font-size: 14px;
+      color: #888;
+      background-color: #f3fbff;
+      border-top: 2px solid #2cb7ca;
+
+      td {
+        color: #888888;
+        padding: 6px 0;
+        //border-top: 3px solid #2cb7ca;
+      }
+    }
+    .deep {
+      border-top: 3px solid #2c90cb;
+    }
+  }
+  tbody {
+    tr {
+      cursor: pointer;
+      &:hover {
+        background-color: #f5f6f7;
+        box-shadow: inset 0px -1px 0px rgb(0, 0, 0, 0.05);
+        cursor: pointer;
+      }
+      &:nth-of-type(2n) {
+        background-color: #f5f5fb;
+      }
+    }
+  }
+  .shade_table {
+    width: 100%;
+    position: absolute;
+    margin-top: -110px;
+    height: 150px;
+    background: linear-gradient(
+      to bottom,
+      rgba(255, 255, 255, 0),
+      rgba(255, 255, 255, 0.8),
+      rgba(255, 255, 255, 1)
+    );
+    .more {
+      position: absolute;
+      height: 35px;
+      width: 120px;
+      left: 50%;
+      margin-left: -60px;
+      bottom: 20px;
+      color: #2cb7ca;
+      border-radius: 5px;
+      border: 1px solid #2cb7ca;
+      line-height: 35px;
+      text-align: center;
+      cursor: pointer;
+    }
+  }
+  .shade_table_blank {
+    height: 50px;
+  }
+  .fixed-table {
+    position: fixed;
+    top: 100px;
+    width: 1200px;
+    table {
+      margin-bottom: 0;
+    }
+  }
+}
+</style>

+ 39 - 0
apps/bigmember_pc/src/views/search/sun/components/tooltip-help-class.vue

@@ -0,0 +1,39 @@
+<template>
+  <div class="tooltip-help-class-container">
+    <el-tooltip
+      ref="elTooltip"
+      popper-class="tooltip-help-class"
+      effect="dark"
+      :append-to-body="false"
+      placement="bottom"
+      :offset="0"
+    >
+      <i
+        class="iconfont icon-help highlight-text cursor-pointer m-l-4px"
+      ></i>
+      <template slot="content">
+        <div class="tooltip-slot-content w-360px">
+          精准搜索: 搜索结果必须完全包含完整的关键词。如搜索"医疗设备"
+          ,搜索结果一定完整包含“医疗设备”才能被搜索到,而“医疗的设备”或“设备医疗”的项目不会被搜索到。
+          <br />
+          <br />
+          模糊搜索: 系统会先自动智能分词然后再进行搜索。如搜索"医疗设备"
+          ,系统会自动分成“医疗”“设备”然后进行搜索,只要项目中出现“医疗”和“设备”都会被搜索到,前提是两个词必须一同出现在一则公告内,不分先后顺序。
+        </div>
+      </template>
+    </el-tooltip>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ToolTipHelpClass',
+  mounted() {
+    this.$el.appendChild(this.$refs.elTooltip.popperVM.$el)
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 127 - 0
apps/bigmember_pc/src/views/search/sun/composables/list-header-actions.js

@@ -0,0 +1,127 @@
+/**
+ * ListHeaderActionItem类用于创建列表头部操作项。
+ * @param {Object} config 配置对象,包含key, icon, label, badge等属性。
+ */
+class ListHeaderActionItem {
+  constructor(config) {
+    this.key = config.key // 操作项的唯一标识
+    this.isActive = config.isActive || false // 是否激活状态
+    this.icon = config.icon || '' // 操作项的图标
+    this.label = config.label || '' // 操作项的标签文本
+    this.badge = config.badge || '' // 操作项的徽标文本(如未使用,则为空)
+  }
+}
+
+/**
+ * 创建一个具有激活状态的列表头部操作项。
+ * @param {Object} config 配置对象,用于创建操作项的初始状态。
+ * @param {Object} activeConfig 激活状态下的配置对象,可选。
+ * @returns {ListHeaderActionItem} 返回一个包含激活状态的操作项实例。
+ */
+function createListHeaderActionItem(config, activeConfig = {}) {
+  const item = new ListHeaderActionItem(config)
+  item.active = new ListHeaderActionItem(
+    Object.assign({}, config, activeConfig) // 结合config和activeConfig创建激活状态的实例
+  )
+  return item
+}
+
+// 列表头部操作项集合
+const ListHeaderActions = {}
+
+/**
+ * 安装列表头部操作项,初始化ListHeaderActions数组。
+ * @returns {Array} 返回初始化后的ListHeaderActions数组。
+ */
+function installListHeader() {
+  const list = []
+  // 精简列表操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'refined-list',
+      icon: 'icon-liebiao',
+      label: '精简列表',
+      isActive: true
+    })
+  )
+
+  // 详细列表操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'detailed-list',
+      icon: 'icon-xiangxiliebiao',
+      label: '详细列表',
+      // badge: 'icon-have-vip'
+    })
+  )
+
+  // 表格操作项,带vip徽标
+  list.push(
+    createListHeaderActionItem({
+      key: 'table',
+      icon: 'icon-biaoge',
+      label: '表格'
+    })
+  )
+
+  // 数据导出操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'data-export',
+      icon: 'icon-shujudaochu',
+      label: '数据导出'
+    })
+  )
+
+  // 标讯收藏操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'collect-bid',
+      icon: 'icon-shoucang_weishoucang',
+      label: '标讯收藏'
+    })
+  )
+
+  // 分发操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'distribute-bid',
+      icon: 'icon-shoudongfenfa',
+      label: '分发'
+    })
+  )
+  // 收录操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'employ-bid',
+      icon: 'icon-a-Property1shoulu',
+      label: '收录'
+    })
+  )
+  // 修改标签操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'edit-tags',
+      icon: 'icon-edit',
+      label: '修改标签'
+    })
+  )
+  // 取消操作项
+  list.push(
+    createListHeaderActionItem({
+      key: 'cancel-collect',
+      icon: 'icon-shoucang_weishoucang',
+      label: '取消收藏'
+    })
+  )
+
+  list.forEach((item) => {
+    ListHeaderActions[item.key] = item
+  })
+}
+
+// 调用installListHeader安装列表头部操作项
+installListHeader()
+
+// 导出ListHeaderActions
+export { ListHeaderActions }

+ 17 - 0
apps/bigmember_pc/src/views/search/sun/constant/index.js

@@ -0,0 +1,17 @@
+import { createBiSearchBidBaseSchema, createBiSearchBidMoreSchema } from './search-filters-bi.js'
+import { createSearchBidBaseSchema, createSearchBidMoreSchema  } from './search-filters.js'
+
+export function getCreateSearchSchema(conf) {
+  const { bi } = conf
+  if(bi) {
+    return {
+      createSearchBidBaseSchema: createBiSearchBidBaseSchema,
+      createSearchBidMoreSchema: createBiSearchBidMoreSchema
+    }
+  } else {
+    return {
+      createSearchBidBaseSchema,
+      createSearchBidMoreSchema
+    }
+  }
+}

+ 264 - 0
apps/bigmember_pc/src/views/search/sun/constant/search-filters-bi.js

@@ -0,0 +1,264 @@
+import RegionCollapseSelector from '@/components/filter-items/RegionCollapseSelector'
+import CommonCheckboxSelector from '@/components/filter-items/CommonCheckboxSelector'
+import PriceSelector from '@/components/selector/PriceSelector'
+// 信息类型
+import InfoTypeDropdown from '@/components/filter-items/InfoTypeDropdown.vue'
+// 搜索范围
+import SearchRangeDropdown from '@/components/filter-items/SearchRangeDropdown.vue'
+// 发布时间
+import TimeDropdown from '@/components/selector/timeDropdown'
+// 附件
+import AttachmentSelector from '@/components/filter-items/AttachmentSelector.vue'
+// 换手率
+import ChangeHandsDropdown from '@/components/filter-items/ChangeHandsDropdown.vue'
+import SearchTimeScopeSelector from '@/components/selector/SearchTimeScopeSelector.vue'
+import ContactSelector from '@/components/filter-items/ContactSelector.vue'
+import KeywordTagsSelector from '@/components/filter-items/KeywordTagsSelector'
+
+
+function createBiSearchBidBaseSchema(propertyListData = {}) {
+  const businessList = propertyListData['业务类型'] || []
+  const priceList =  propertyListData['价格区间'] || []
+  const periodList =  propertyListData['合同周期'] || []
+  const propertyList =  propertyListData['物业业态'] || []
+  const SearchBidBaseSchema = [
+    {
+      key: 'regionMap',
+      label: '地区:',
+      defaultVal: {},
+      _name: 'regionMap',
+      _type: 'component',
+      expand: {
+        component: RegionCollapseSelector,
+        props: {
+          showSelectedList: false
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'expireTime',
+      label: '到期时间:',
+      defaultVal: 'all',
+      _name: 'expireTime',
+      _type: 'component',
+      expand: {
+        component: SearchTimeScopeSelector,
+        props: {
+          type: 'expire',
+          showConfirmButton: true,
+          exactCanHalf: true,
+          isDateRange: true
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'subInformation',
+      label: '业务类型:',
+      defaultVal: [],
+      _name: 'subInformation',
+      _type: 'component',
+      expand: {
+        component: CommonCheckboxSelector,
+        props: {
+          field: 'value',
+          options: {
+            value: businessList
+          }
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'scale',
+      label: '价格区间:',
+      defaultVal: '',
+      _name: 'scale',
+      _type: 'component',
+      expand: {
+        component: CommonCheckboxSelector,
+        props: {
+          field: 'value',
+          options: {
+            value: priceList
+          }
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'period',
+      label: '合同周期:',
+      defaultVal: '',
+      _name: 'period',
+      _type: 'component',
+      expand: {
+        component: CommonCheckboxSelector,
+        props: {
+          single: true,
+          field: 'value',
+          options: {
+            value: periodList
+          }
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'propertyForm',
+      label: '物业业态:',
+      defaultVal: '',
+      _name: 'propertyForm',
+      _type: 'component',
+      expand: {
+        component: CommonCheckboxSelector,
+        props: {
+          field: 'value',
+          options: {
+            value: propertyList
+          }
+        },
+        hooks: {}
+      }
+    }
+  ]
+
+  return SearchBidBaseSchema
+}
+
+function createBiSearchBidMoreSchema() {
+  const SearchBidMoreSchema = [
+    {
+      key: 'selectType',
+      label: '搜索范围',
+      defaultVal: ['title', 'content'],
+      _name: 'selectType',
+      _type: 'component',
+      expand: {
+        component: SearchRangeDropdown,
+        hooks: {}
+      }
+    },
+    {
+      key: 'subtype',
+      label: '信息类型',
+      defaultVal: '',
+      _name: 'subtype',
+      _type: 'component',
+      expand: {
+        component: InfoTypeDropdown,
+        props: {
+          notNJ: true
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'publishTime',
+      label: '发布时间',
+      defaultVal: 'fiveyear',
+      _name: 'publishTime',
+      _type: 'component',
+      expand: {
+        component: TimeDropdown,
+        props: {
+          placeholder: '发布时间',
+          selectData: [{
+            value: 'lately-7',
+            label: '最近7天'
+          }, {
+            value: 'lately-30',
+            label: '最近30天'
+          }, {
+            value: 'thisyear',
+            label: '最近1年'
+          }, {
+            value: 'threeyear',
+            label: '最近3年'
+          }, {
+            value: 'fiveyear',
+            label: '最近5年'
+          }, {
+            value: '0',
+            label: '自定义',
+            disabled: true
+          }],
+          selectorType: 'line',
+          singleChoice: true,
+
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'changeHand',
+      label: '换手率',
+      defaultVal: '',
+      _name: 'changeHand',
+      _type: 'component',
+      expand: {
+        component: ChangeHandsDropdown,
+        hooks: {}
+      }
+    },
+    {
+      key: 'fileExists',
+      label: '附件',
+      defaultVal: '',
+      _name: 'fileExists',
+      _type: 'component',
+      expand: {
+        component: AttachmentSelector,
+        hooks: {}
+      }
+    },
+    {
+      key: 'buyerTel',
+      label: '采购单位联系方式',
+      defaultVal: '',
+      _name: 'buyerTel',
+      _type: 'component',
+      expand: {
+        component: ContactSelector,
+        props: {
+          source: 'buyer'
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'winnerTel',
+      label: '中标企业联系方式',
+      defaultVal: '',
+      _name: 'winnerTel',
+      _type: 'component',
+      expand: {
+        component: ContactSelector,
+        props: {
+          source: 'winner'
+        },
+        hooks: {}
+      }
+    },
+    {
+      key: 'notKey',
+      label: '排除词',
+      defaultVal: [],
+      _name: 'notKeyComponent',
+      _type: 'component',
+      expand: {
+        component: KeywordTagsSelector,
+        props: {
+          placeholder: '排除词',
+          inputPlaceholder: '请输入不希望包含的关键词',
+          maxTip: '排除词个数已达上限'
+        },
+        hooks: {}
+      }
+    }
+  ]
+  return SearchBidMoreSchema
+}
+
+export { createBiSearchBidBaseSchema, createBiSearchBidMoreSchema }

+ 360 - 0
apps/bigmember_pc/src/views/search/sun/constant/search-filters.js

@@ -0,0 +1,360 @@
+import TagSelector from '@/components/selector/TagSelector.vue'
+import SearchScopeSelector from '@/components/filter-items/SearchScopeSelector.vue'
+import SearchTimeScopeSelector from '@/components/selector/SearchTimeScopeSelector.vue'
+import ContactSelector from '@/components/filter-items/ContactSelector.vue'
+import AttachmentSelector from '@/components/filter-items/AttachmentSelector.vue'
+import IndustrySelector from '@/components/filter-items/IndustrySelector.vue'
+import RegionSelector from '@/components/filter-items/RegionSelector'
+import CommonThreeSelector from '@/components/filter-items/CommonThreeSelector'
+import SelectorWithBasePower from '@/components/filter-items/SelectorWithBasePower.vue'
+import { lingyuDataSource } from '@/assets/js/selector/source'
+import { cloneDeep } from 'lodash'
+import $bus from '@/utils/bus'
+
+function noPower() {
+  $bus.$emit('search:filter:no-power')
+}
+
+function beforeChangeHandle($event, char, isLogin) {
+  return true
+  if (isLogin) {
+    return true
+  }
+  // 发布时间
+  if (char === 'publishTime') {
+    if ($event.value === 'sinceLastYear') {
+      return true
+    }
+    else {
+      $bus.$emit('bidding:goLogin')
+      return false
+    }
+  }
+  else if (char === 'selectType') {
+    if ($event.value === 'file') {
+      $bus.$emit('bidding:goLogin')
+      return false
+    }
+    else {
+      return true
+    }
+  }
+  else if (char === 'subtype') {
+    if ($event.value) {
+      $bus.$emit('bidding:goLogin')
+    }
+    else {
+      return true
+    }
+  }
+}
+
+// 更多筛选中需要vip的筛选项
+const moreFiltersNeedVipKeyList = []
+
+function createSearchBidBaseSchema(conf = {}) {
+  const isLogin = conf.isLogin || false
+  const vipUser = conf.vipUser || false
+  const oldUser = conf.oldUser || false
+  const showVip = conf.showVip || false
+  const infoType = conf.infoType
+  const inInjectBI = conf.inInjectBI || false
+  const isInApp = conf.isInApp || false
+  const isVip = conf.isVip || false
+
+  // 发布时间
+  const publishTimeExpandFree = {
+    component: SelectorWithBasePower,
+    props: {
+      component: SearchTimeScopeSelector,
+      vipMaskShow: true,
+      vipModuleShow: showVip,
+      freeConf: {
+        exactCanHalf: true,
+        showConfirmButton: true,
+        beforeChange($event) {
+          return beforeChangeHandle($event, 'publishTime', isLogin)
+        },
+        options: ['lately7', 'lately30', 'sinceLastYear'],
+        isDateRange: true
+      },
+      vipConf: {
+        exactCanHalf: true,
+        showConfirmButton: true,
+        options: ['sinceLastThreeYear', 'sinceLastFiveYear', 'exact'],
+        isDateRange: true
+      }
+    },
+    hooks: {
+      clickVipMask: noPower
+    }
+  }
+  const publishTimeExpandVip = {
+    component: SearchTimeScopeSelector,
+    props: {
+      exactCanHalf: true,
+      showConfirmButton: true,
+      isDateRange: true
+    },
+    hooks: {}
+  }
+  // 搜索范围
+  const defaultScopeOptions = [
+    {
+      label: '标题',
+      value: 'title'
+    },
+    {
+      label: '标的名称',
+      value: 'purchasing'
+    },
+  ]
+  const freeOptions = defaultScopeOptions
+  const vipOptions = []
+  const searchScopeExpandFree = {
+    component: SelectorWithBasePower,
+    props: {
+      vipModuleShow: showVip,
+      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: {
+      noPower
+    }
+  }
+  const searchScopeExpandVip = {
+    component: SearchScopeSelector,
+    props: {
+      options: defaultScopeOptions,
+      isOld: oldUser && !vipUser,
+      keepOne: true
+    },
+    hooks: {}
+  }
+
+  const SearchBidBaseSchema = [
+    {
+      key: 'publishTime',
+      label: '发布时间:',
+      labelHeight: isVip || !isInApp ? '22px' : '32px',
+      defaultVal: 'thisyear',
+      _name: 'time',
+      _type: 'component',
+      // expand: vipUser ? publishTimeExpandVip : publishTimeExpandFree
+      expand: publishTimeExpandVip
+    },
+    {
+      key: 'selectType',
+      label: '搜索范围:',
+      labelHeight: isVip || !isInApp ? '22px' : '30px',
+      defaultVal: ['content', 'title'],
+      _name: 'selectType',
+      _type: 'component',
+      // expand: vipUser ? searchScopeExpandVip : searchScopeExpandFree
+      expand: searchScopeExpandVip
+    },
+    // {
+    //   key: 'bmjzzt',
+    //   label: '报名截止状态:',
+    //   defaultVal: '0',
+    //   _name: 'bmjzzt',
+    //   _type: 'component',
+    //   labelHeight: '25px',
+    //   className: {
+    //     'search-filter-bmjzzt-line': true
+    //   },
+    //   labelStyle: {
+    //     width: '98px',
+    //     'white-space': 'nowrap'
+    //   },
+    //   expand: {
+    //     component: TagSelector,
+    //     props: {
+    //       sourceList: [
+    //         {
+    //           label: '全部',
+    //           value: '0'
+    //         },
+    //         {
+    //           label: '未截止',
+    //           value: '1'
+    //         },
+    //         {
+    //           label: '已截止',
+    //           value: '2'
+    //         }
+    //       ],
+    //       showHeader: false
+    //     },
+    //     hooks: {}
+    //   },
+    // },
+    // {
+    //   key: 'signUpEndTime',
+    //   label: '报名截止日期:',
+    //   defaultVal: '',
+    //   _name: 'signUpEndTime',
+    //   _type: 'component',
+    //   labelStyle: {
+    //     width: '98px',
+    //     'white-space': 'nowrap'
+    //   },
+    //   labelHeight: isVip || !isInApp ? '22px' : '32px',
+    //   expand: {
+    //     component: SearchTimeScopeSelector,
+    //     props: {
+    //       type: 'bidDeadline',
+    //       exactCanHalf: true,
+    //       showConfirmButton: true,
+    //       isDateRange: true
+    //     },
+    //     hooks: {}
+    //   }
+    // },
+  ]
+
+  return SearchBidBaseSchema
+}
+
+function createSearchBidMoreSchema(filterItems, conf) {
+  const isLogin = conf.isLogin || false
+  const isVip = conf.isVip || false
+  const isBidField = conf.isBidField || false
+
+  let SearchBidMoreSchema = [
+    {
+      key: 'jfArea',
+      label: '交付地点',
+      defaultVal: '',
+      _name: 'type',
+      _type: 'component',
+      expand: {
+        component: RegionSelector,
+        props: {
+          // vip: isVip && isLogin,
+          vip: true,
+          showCount: false,
+          placeholder: '交付地点'
+        },
+        hooks: {
+          limit: noPower
+        }
+      }
+    },
+    {
+      key: 'projectArea',
+      label: '项目地区',
+      defaultVal: '',
+      _name: 'type',
+      _type: 'component',
+      expand: {
+        component: RegionSelector,
+        props: {
+          vip: true,
+          showCount: false,
+          placeholder: '项目地区'
+        },
+        hooks: {
+          limit: noPower
+        }
+      }
+    },
+    // {
+    //   key: 'lingyu',
+    //   label: '领域',
+    //   defaultVal: '',
+    //   _name: 'type',
+    //   _type: 'component',
+    //   expand: {
+    //     component: CommonThreeSelector,
+    //     props: {
+    //       sourceList: lingyuDataSource,
+    //       vip: true,
+    //       showCount: false,
+    //       placeholder: '领域'
+    //     },
+    //     hooks: {
+    //       limit: noPower
+    //     }
+    //   }
+    // },
+    {
+      key: 'industry',
+      label: '行业',
+      defaultVal: '',
+      _name: 'type',
+      _type: 'component',
+      expand: {
+        component: IndustrySelector,
+        hooks: {}
+      }
+    },
+    {
+      key: 'fileExists',
+      label: '附件',
+      defaultVal: '0',
+      _name: 'type',
+      _type: 'component',
+      expand: {
+        component: AttachmentSelector,
+        hooks: {}
+      }
+    },
+    {
+      key: 'publisher',
+      label: '发布方',
+      defaultVal: '0',
+      _name: 'type',
+      _type: 'component',
+      expand: {
+        component: ContactSelector,
+        props: {
+          allValue: '0',
+          placeholder: '发布方',
+          options: [
+            {
+              label: '全部',
+              value: '0'
+            },
+            {
+              label: '用户发布',
+              value: '1'
+            },
+            {
+              label: '平台发布',
+              value: '2'
+            }
+          ]
+        },
+        hooks: {}
+      }
+    },
+  ]
+
+  // VIP处理
+  SearchBidMoreSchema.forEach((schema) => {
+    const key = schema.key
+    if (moreFiltersNeedVipKeyList.includes(key)) {
+      schema.vipMark = 1
+    }
+  })
+
+  return SearchBidMoreSchema
+}
+
+export { createSearchBidBaseSchema, createSearchBidMoreSchema }

+ 748 - 0
apps/bigmember_pc/src/views/search/sun/index.vue

@@ -0,0 +1,748 @@
+<script setup>
+import { ref } from 'vue'
+import SearchBidHeader from './components/search-bid-header.vue'
+import SearchBidFilter from './components/search-bid-filter.vue'
+import searchFilterHeader from './components/search-filter-header.vue'
+import SearchListTable from './components/search-list-table.vue'
+import SearchList from '@/views/search/layout/search-list.vue'
+import ArticleItem from '@/components/article-item/ArticleItem.vue'
+import Adsense from '@/views/order/components/adsense/index.vue'
+import CollectInfo from '@/components/collect-info/CollectInfo.vue'
+import CustomDialog from '@/components/dialog/Dialog.vue'
+import ExportTip from '@/views/portrayal/components/DataExportTip.vue'
+import powerPerson from '@/components/subscribe-manager/powerPerson.vue'
+import Empty from '@/components/common/Empty.vue'
+import { openLinkInWorkspace } from '@/utils/'
+// 导入业务模型
+import { useSearchBidModel, SearchBidModel } from './model/index'
+import { useRouter } from 'vue-router/composables'
+
+const router = useRouter()
+
+// 初始化模型
+useSearchBidModel()
+
+// 初始化模型 解构业务所需 model \ fn
+const {
+  isLogin,
+  isInApp,
+  inBIPropertyIframe,
+  inResourceBIIframe,
+  inBITopNet,
+  inInjectBI,
+  isInBI,
+  isVip,
+  isFree,
+  vt,
+  filterState,
+  inputKeywordsState,
+  listState,
+  activeItemStyleType,
+  searchListProps,
+  doQuery,
+  doListHeaderAction,
+  doChangeAllSelect,
+  doChangeSelect,
+  doChangePageNum,
+  doChangePageSize,
+  toDetail,
+  list,
+  tableList,
+  tableFixedTop,
+  tagToDetail,
+  setExport,
+  exportDialogChange,
+  showDataExportDialog,
+  onClickSingleCollect,
+  usePowerRef,
+  doSubmitDistribute,
+  // onJoinBid,
+  // BidrenewalDialogRef,
+  showPropertyDialog,
+  propertyIframeSrc,
+  onSingleEmploy,
+  onAddInfoOfBI,
+  onSingleAddInfo,
+  vipDialogConf,
+  closeVipDialog,
+  timeSelectorText,
+  collectElementRef,
+  onFreeTaste,
+  interceptKeywords,
+  toggleBlurModeTip,
+  doToggleSearchBlurMode,
+  goToBidSearchPage,
+  showFilter
+} = SearchBidModel
+
+const {
+  show: showVipDialog,
+  text: vipDialogText,
+  type: vipDialogType
+} = vipDialogConf
+
+// 开通超级订阅
+function toBuySvip() {
+  window.open('/swordfish/page_big_pc/free/svip/buy?type=buy')
+}
+
+// 列表-单条-配置
+const articleRef = ref({
+  bidding: true,
+  detail: false,
+  gray: true,
+  joinBid: false,
+  table: false,
+  collect: false,
+  push: false,
+  tagUserPublishState: true,
+  tagSignupEnd: true,
+  jfArea: true
+})
+
+const leaveSource = () => {
+  // collectElementRef.value.isNeedSubmit('pc_sunlightlist_viewdetails', () => {})
+  collectElementRef.value.noCallApiFn('pc_sunlightlist_viewdetails', false)
+}
+
+</script>
+
+<template>
+  <div class="search-sun-page">
+    <div
+      class="search-bidding-header-container"
+      :class="{ 'b-rd-self': !showFilter }"
+    >
+      <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">
+      <el-collapse-transition>
+        <div v-show="showFilter">
+          <search-filter-header></search-filter-header>
+          <search-bid-filter></search-bid-filter>
+        </div>
+      </el-collapse-transition>
+    </div>
+    <!-- <div>
+      <recommend-card></recommend-card>
+    </div> -->
+    <div class="search-bidding-list-container">
+      <search-list
+        class="b-rd-8px"
+        v-bind="searchListProps"
+        @doAction="doListHeaderAction"
+        @doChangeAllSelect="doChangeAllSelect"
+        @doChangeSelect="doChangeSelect"
+        :show-select="false"
+        :show-pagination="activeItemStyleType !== 'T'"
+        :is-table="activeItemStyleType === 'T'"
+        :table-fixed-top="tableFixedTop"
+      >
+        <template #other-action-item v-if="inInjectBI">
+          <div class="all-add bi-add-button" @click="onAddInfoOfBI()">添加</div>
+        </template>
+        <!--      <template #list-before v-if="listState.pageNum === 1">-->
+        <!--        <span>如对搜索结果满意,可直接订阅及时接收项目信息。</span>-->
+        <!--      </template>-->
+        <template #item-checkbox v-if="activeItemStyleType === 'T'">
+          <span></span>
+        </template>
+        <template v-slot:table="{ list }" v-if="activeItemStyleType === 'T'">
+          <search-list-table
+            :list="tableList"
+            :list-state="listState"
+            :match-keys="inputKeywordsState.matchKeys"
+            @to-detail="toDetail"
+            :table-fixed-top="tableFixedTop"
+          ></search-list-table>
+        </template>
+        <template
+          v-slot:item="{ item, index }"
+          v-if="activeItemStyleType !== 'T'"
+        >
+          <div>
+            <article-item
+              class="list-item"
+              :model="activeItemStyleType"
+              :class="{ visited: item.visited || item.ca_isvisit }"
+              :match-keys="inputKeywordsState.matchKeys"
+              :article="item"
+              :index="index"
+              :tag-click-list="['area', 'subtype']"
+              @onClick="toDetail(item)"
+              @tag-click="tagToDetail(item, $event)"
+              @onCollect="onClickSingleCollect"
+              @leaveInfo="leaveSource"
+              :config="articleRef"
+            >
+              <template #bi-slot="{ item }"> </template>
+            </article-item>
+          </div>
+        </template>
+        <template #empty>
+          <empty :mtb60="false" images="jy-back.png">
+            <div class="hasNoData_tiptext">
+              <p>未匹配到您所搜索的信息,</p>
+              <p>
+                您可跳转到
+                <span class="highlight-text">“招标采购搜索”</span>
+                查看是否有对应信息
+              </p>
+              <p class="empty-action-button">
+                <el-button type="primary" size="middle" @click="goToBidSearchPage">点击跳转</el-button>
+              </p>
+            </div>
+          </empty>
+        </template>
+        <template
+          #list-after
+          v-if="listState.finished && 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">
+          <div class="tag-input">
+            <div class="tag-labels"></div>
+            <input
+              type="text"
+              class="clear-input"
+              maxlength="10"
+              oninput="this.value=this.value.replace(/\s+/g,'')"
+            />
+            <div class="tag-placeholder">新增标签回车保存</div>
+          </div>
+          <div class="add-tag-button">添加并使用</div>
+        </div>
+        <div class="tags-list clearfix"></div>
+        <div class="tags-footer">
+          <div class="tags-button button-confirm">确认添加</div>
+          <div class="tags-button button-cancel">暂不添加</div>
+        </div>
+      </div>
+    </div>
+
+    <adsense class="footer-look-container" code="pc-sunlightlist-search-bottom"></adsense>
+
+    <!--  留资弹窗  -->
+    <collect-info ref="collectElementRef"></collect-info>
+    <!--数据导出提示框-->
+    <custom-dialog
+      @close="showDataExportDialog = false"
+      customClass="export-class-dialog"
+      width="388px"
+      :show-footer="false"
+      :show-close="true"
+      :visible="showDataExportDialog"
+    >
+      <export-tip
+        @setExport="setExport"
+        @checkBoxChange="exportDialogChange"
+      ></export-tip>
+    </custom-dialog>
+    <CustomDialog
+      title="开通超级订阅"
+      customClass="open-vip-dialog"
+      width="380px"
+      top="30vh"
+      center
+      :visible.sync="showVipDialog"
+    >
+      {{ vipDialogText }}
+      <template #footer>
+        <button class="action-button confirm" @click="toBuySvip">去开通</button>
+        <button class="action-button cancel" @click="closeVipDialog">
+          取消
+        </button>
+      </template>
+    </CustomDialog>
+    <!-- 分发企业选择, 只有企业展示,默认vt为企业-->
+    <power-person
+      @manualDiatribution="doSubmitDistribute"
+      vt="q"
+      :list="list"
+      ref="usePowerRef"
+    ></power-person>
+    <!-- 参标更新状态弹窗 -->
+    <!-- <bidrenewal-dialog ref="BidrenewalDialogRef"> </bidrenewal-dialog> -->
+
+    <!--    物业专版收录-->
+    <el-dialog
+      custom-class="property-employ-dialog"
+      :visible.sync="showPropertyDialog"
+    >
+      <iframe
+        width="600"
+        height="650"
+        :src="propertyIframeSrc"
+        frameborder="0"
+      ></iframe>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss">
+.open-vip-dialog {
+  width: 380px !important;
+
+  .el-dialog__header {
+    padding: 32px 32px 20px !important;
+  }
+
+  .el-dialog__body {
+    padding: 0 32px 0 !important;
+    text-align: center !important;
+  }
+
+  .el-dialog__footer {
+    padding: 32px !important;
+  }
+
+  .el-button {
+    width: 132px;
+    font-size: 16px;
+  }
+}
+.search-sun-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;
+    padding: 0 8px;
+    margin: 10px 8px 0 0;
+    height: 24px;
+    line-height: 24px;
+    border-radius: 4px;
+    border: 1px solid #ececec;
+    box-sizing: border-box;
+    color: #1d1d1d;
+    text-align: center;
+    font-size: 14px;
+    background: #f5f6f7;
+    cursor: pointer;
+  }
+
+  .tags-list .tags-item {
+    float: left;
+    min-width: 44px;
+    padding: 0 8px;
+    margin: 10px 8px 0 0;
+    height: 24px;
+    line-height: 24px;
+    border-radius: 4px;
+    border: 1px solid #ececec;
+    box-sizing: border-box;
+    color: #1d1d1d;
+    text-align: center;
+    font-size: 14px;
+    background: #f5f6f7;
+    cursor: pointer;
+  }
+
+  .tags-item.tags-active {
+    padding: 0 8px 0 24px !important;
+    background: #2cb7ca
+      url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADPSURBVHgB7ZNREcIwDIYjYRImYRLmZHPAHAwHlUAdIAEJSKiESgjpEY7Qg7uFdXnKd5eXdMmfpX8BHMdxDEDEgSJQ9GANiU4UGZ9cwBISXPFNGWIAKyrxZLr+suq/xdkwV4oFlFBNR3ET4veS0zaJosGoqOtZ8EVUi4tGWbM+rklC/Ax7KH++dY18ZbmZuGi8iKbhxzdTJT5DSyo/LNXZSZxljV80A3T4aayR86vIJTzyjX8xZTATF0NIU24y5xFDSFNGU3ExxNzsmTmO4zAPYEiZdz83IV0AAAAASUVORK5CYII=)
+      no-repeat 6px center !important;
+    color: #fff !important;
+    background-size: 16px !important;
+    border: 0 !important;
+  }
+
+  .tags-item.disabled {
+    color: #8e8e8e !important;
+  }
+  .tags-inputs .tag-input::-webkit-scrollbar {
+    width: 8px;
+  }
+
+  .tags-inputs .tag-input::-webkit-scrollbar-thumb {
+    border-radius: 4px;
+    background-color: #ececec;
+  }
+
+  .tags-list::-webkit-scrollbar {
+    width: 8px;
+  }
+
+  .tags-list::-webkit-scrollbar-thumb {
+    border-radius: 4px;
+    background-color: #ececec;
+  }
+  .tag-close {
+    display: inline-block;
+    width: 16px;
+    height: 16px;
+    margin-left: 8px;
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAEwSURBVHgB7ZWxDYMwEEXPER1NRmEFJggUiJpJQiahRhSECViBUdzQIYgdgYQIPnwXmkT+DcbY/s9n3wHg5PTPKoriejRGYB+rqrpP0xT1fR9mWSaBaO77fiuEeCZJ8gAqwGyez68dBWIxV83gbSJEboK47HXWda1DF626Ar2gTUi35lo6iqbxuwBxHEvP80JF3lEg9sxhjp5pDnoHdCSGYWjVDj4W3B4HZo4dHQpgC8E1twI4gtANrrk1AAYxP1nmJAAEArjmZIADiI5TsC5AlJRS5zWcJVIEDLd9LXIUrCNgSjVqsWIBYHnOqZhrHR6BTZGhVEwSAKXCcSHEGebfQBjvAKe8cv6iRgC1i4ZijkHotUxz0TtQlmWuFrpxKtxyHOM4Nmma5sAVJaednH5SLxmyS6JIrGjkAAAAAElFTkSuQmCC);
+    background-position: center 2px;
+    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;
+  }
+}
+
+.empty-action-button {
+  margin-top: 12px;
+  .el-button {
+    padding: 6px 36px;
+    height: 36px;
+  }
+}
+</style>
+
+<style lang="scss" scoped>
+.in-app {
+  .search-sun-page {
+    margin-top: 0;
+    width: 100%;
+    padding: 24px;
+  }
+}
+.in-web {
+  .search-sun-page {
+    margin-top: 24px;
+  }
+}
+.search-sun-page {
+  width: 1200px;
+  margin: 0 auto;
+
+  ::v-deep {
+    .search-filter-bmjzzt-line {
+      height: 24px;
+    }
+    .more-filters-container {
+      margin-top: 12px;
+    }
+  }
+
+  .search-bidding-header-container {
+    background-color: #fff;
+    border-radius: 8px 8px 0 0;
+    &.b-rd-self {
+      border-radius: 8px;
+    }
+  }
+  .search-bidding-filter-container {
+    width: 100%;
+    background-color: #fff;
+    margin-bottom: 16px;
+    border-radius: 0 0 8px 8px;
+  }
+
+  .search-bidding-list-container {
+    position: relative;
+  }
+  .footer-look-container.adsense {
+    padding: 0;
+    padding-bottom: 16px;
+    ::v-deep {
+      .content {
+        border: none;
+      }
+    }
+  }
+
+  .bi-employ-bid {
+    display: inline-block;
+    cursor: pointer;
+    font-size: 14px;
+    color: #1d1d1d;
+    .iconfont {
+      margin-right: 3px;
+      font-size: 20px !important;
+      vertical-align: top;
+    }
+    .icon-a-Property1shoulu {
+      color: #afafaf;
+    }
+    .icon-a-Property1yishoulu {
+      color: #2abed1;
+    }
+  }
+  .bi-add-button {
+    &.all-add {
+      margin-right: 8px;
+    }
+    display: inline-block;
+    margin-left: 16px;
+    border: 1px solid #2cb7ca;
+    color: #2cb7ca;
+    background-color: #fff;
+    padding: 0 6px;
+    height: 22px;
+    line-height: 22px;
+    border-radius: 4px;
+    font-size: 14px;
+    text-align: center;
+    cursor: pointer;
+    box-sizing: content-box;
+    min-width: 42px;
+  }
+  ::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;
+
+      text-align: center;
+      color: #999999;
+      line-height: 22px;
+    }
+    .el-date-editor.el-input,
+    .el-date-editor.el-input__inner {
+      width: unset;
+      background: transparent;
+      padding: 0;
+    }
+  }
+  .over-run-tips {
+    color: #686868;
+    text-align: right;
+    font-size: 14px;
+    line-height: 22px;
+    padding: 0 20px 16px 0;
+    cursor: pointer;
+  }
+}
+.search-bidding-keywords-tip {
+  background: #fff;
+  padding: 0 0 10px 30px;
+  font-size: 14px;
+  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;
+  }
+  button {
+    margin-left: 12px;
+    background: #2abed1;
+    padding: 5px 12px;
+    border-radius: 4px;
+    color: #f7f9fa;
+  }
+}
+</style>

+ 1708 - 0
apps/bigmember_pc/src/views/search/sun/model/base.js

@@ -0,0 +1,1708 @@
+import { computed, reactive, ref, toRefs, onMounted,onBeforeMount, onBeforeUnmount, getCurrentInstance, provide } from 'vue'
+import { without, throttle, uniqBy } from 'lodash'
+import { useStore } from '@/store'
+import { useRoute, useRouter } from 'vue-router/composables'
+import { MessageBox } from 'element-ui'
+import  {
+  FilterHistoryAjaxModelRestore,
+  FilterHistoryAjaxModel2ViewModel,
+  FilterModel2ViewModel,
+  getParam,
+  dateFormatter,
+  openLinkInWorkspace,
+  InContainer
+} from '@/utils'
+import $bus from '@/utils/bus'
+// 筛选条件动态组件方法
+import { getCreateSearchSchema } from '../constant/index'
+// API 业务模型
+import useQuickSearchModel from '@jy/data-models/modules/quick-search/model'
+// 扩展业务模型
+import { useSearchFilterModel } from './modules/filter'
+import { useSearchInputKeywordsModel } from './modules/filter-keywords'
+import { useSearchListHeaderActionsModel } from './modules/list-header-actions'
+import { useSearchTabsModel } from './modules/tabs'
+// 保存、重置、查看筛选条件业务
+import { saveFilterActionsModel } from './modules/save-filter-actions'
+// 数据导出业务
+import { dataExportActionsModel } from './modules/data-export-actions'
+// 标讯收藏业务
+import { dataCollectActionModel } from './modules/data-collect-actions'
+// 企业分发业务
+import { dataDistributeActionsModel } from './modules/data-distribute-actions'
+// 参标业务
+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'
+// 搜索历史业务模型
+import useSearchHistoryModel from '@jy/data-models/modules/quick-search-history/model'
+
+export default function () {
+  const that = getCurrentInstance().proxy
+
+  const router = useRouter()
+  // 是否是免费用户
+  const isFree = computed(() => {
+    return false
+    // return useStore().getters['user/isFree']
+  })
+  // 企业管理员
+  const isEntAdmin = computed(() => {
+    return false
+    // return useStore().getters['user/isEntAdmin']
+  })
+  // 部门管理员
+  const isDepartmentAdmin = computed(() => {
+    // return false
+    // return useStore().getters['user/isDepartmentAdmin']
+  })
+  // 是否登录
+  const isLogin = computed(() => {
+    return useStore().getters['user/loginFlag']
+  })
+  // 用户权限
+  const userPowerInfo = computed(() => {
+    return useStore().getters['user/getInfo']
+  })
+  // 是否是vip
+  const isVip = computed (() => {
+    // const { entniche, memberStatus, vipStatus } = userPowerInfo.value
+    // return entniche || memberStatus > 0 || vipStatus > 0
+    return true
+  })
+  // 是否是老用户
+  const isOld = computed (() => {
+    const { isOld } = userPowerInfo.value
+    return isOld
+  })
+
+  // 获取搜索历史业务数据模型
+  const searchHistoryModel = useSearchHistoryModel({ type: 6 })
+  const { getHistoryQuery, clearHistoryQuery, searchHistoryList } = searchHistoryModel
+
+  // 标讯搜索历史列表
+  const bidHistoryList = computed(() => {
+    return searchHistoryList.value
+  })
+
+  // 跨组件通信 将历史搜索列表数据注入 在SearchHeader子组件中引用
+  provide('searchHistoryList', bidHistoryList)
+
+  // 是否在工作台内
+  // 本地调试,可改为工作台内isInApp = ref(true),  isInWeb = ref(false)   提交记得改回!
+  const isInApp = ref(InContainer.inApp) // InContainer.inApp
+  const isInWeb = ref(InContainer.inWeb) // InContainer.inWeb
+
+  // isInApp.value = true
+  // isInWeb.value = false
+
+  // 是否是渠道商
+  const cooperateCode = ref(false)
+  // 一切都好渠道商,是否是渠道商
+  const cookieInfo = document.cookie.split('; ')
+  cooperateCode.value = cookieInfo.some(item => item.indexOf('channelCode') > -1)
+  onMounted(() => {
+    window.addEventListener('scroll', watchScroll)
+  })
+
+  // 物业专版
+  const inBIPropertyIframe = useRoute().query.property === 'BIProperty'
+  // 营销BI嵌套
+  const inResourceBIIframe = useRoute().query.resource === 'BI'
+
+  // 是否是拓普权限
+  // const inBITopNet = useRoute().query.crm_type
+  const inBITopNet = false
+
+  // 是否山川应用嵌入环境 添加操作按钮 (个人年终报告嵌套)
+  // const inInjectBI = useRoute().query.report === 'bi' || (location.href.indexOf('/jylab/bi/index') !== -1)
+  const inInjectBI = false
+
+  // 是否是BI嵌套页面
+  const isInBI = computed(() => {
+    return inResourceBIIframe || inResourceBIIframe || inInjectBI
+  })
+
+   // 是否是领域化页面(医械通)
+  const isBidField =  location.href.includes('/jylab/medical/index')
+
+  const { goback, keywords: urlKeywords, selectType: urlSelectType } = useRoute().query
+
+  // 缓存存储配置
+  const storageConfig = {
+    listTab: {
+      key: 'pc_search_sun_listTabActive',
+      _storage: localStorage
+    },
+    // 筛选条件上次搜索筛选项缓存key
+    filter: {
+      key: 'pc_search_sun_lastFilters',
+      _storage: localStorage
+    },
+    // 页面tab(筛选项的searchGroup)
+    searchGroup: {
+      key: 'pc_search_sun_lastSearchGroup',
+      _storage: localStorage
+    }
+  }
+  // 解构基础业务
+  const APIModel = useQuickSearchModel({
+    type: 'search-sun'
+  })
+  /**
+   * 列表相关model
+   */
+  const {
+    list,
+    total,
+    loading,
+    finished,
+    selectIds,
+    listIds,
+    searchResultCount,
+    isSelectSomeCheckbox,
+    selectCheckboxCount,
+    isSelectListAllCheckbox,
+    doToggleItemSelection,
+    doToggleListSelection,
+    doClearAllSelection,
+    doQuery: doRunQuery
+  } = APIModel
+  /**
+   * 关键词搜索相关
+   */
+  const {
+    inputKeywordsState,
+    searchModelOptions,
+    getFormatAPIParams: getFormatOfInputKeywords,
+    updateInputKeywordsState,
+  } = useSearchInputKeywordsModel()
+  /**
+   * 筛选v-model数据
+   */
+  const {
+    filterState,
+    filterBase,
+    getFormatAPIParams: getFormatOfFilter,
+    updateFilterBase
+  } = useSearchFilterModel({ inBIPropertyIframe, isFree, isInApp, isBidField })
+  /**
+   * 列表头操作
+   */
+  const {
+    limitActions,
+    headerActions,
+    listItemStyleType,
+    activeItemStyleType,
+    activeHeaderActions,
+    disabledHeaderActions
+  } = useSearchListHeaderActionsModel()
+
+  $bus.$on('bidding:updateInputKeywords', function (obj) {
+    updateInputKeywordsState(obj)
+  })
+
+  /**
+   * 搜索前校验
+   */
+  const {
+    getWhiteList,
+    onTheWhiteList,
+    checkAndClearTextIncludesCommonWords
+  } = beforeSearchModel({ inputKeywordsState, urlKeywords })
+
+  onBeforeMount( async() => {
+    try{
+     await getWhiteList()
+    } catch (e){
+      console.warn('e')
+    }
+  })
+
+  /**
+   * 页面tab切换Model
+   */
+  const SearchTabsModel = useSearchTabsModel({ showTabs2: !isInApp.value, inInjectBI })
+  const { activeTab, doChangeTab } = SearchTabsModel
+  // tab切换处理
+  function onChangeTab (item) {
+    filterState.value = Object.assign(filterState.value, {
+      subtype: []
+    })
+    if(isInApp.value) {
+      doChangeTab(item)
+      doQuery({}, 'tab')
+    } else {
+      if(item.link) {
+        location.replace(item.link)
+      }
+    }
+  }
+
+  function goToBidSearchPage () {
+    const value = inputKeywordsState.value.input
+    const url = `/jylab/supsearch/index.html?keywords=${value}`
+    if (isInApp.value) {
+      // 工作桌面内跳转
+      openLinkInWorkspace(true, {
+        url: url,
+        newTab: true
+      })
+    } else {
+      window.open(url)
+    }
+  }
+
+  /**
+   * 筛选条件动态配置处理
+   */
+  const {
+    createSearchBidBaseSchema,
+    createSearchBidMoreSchema
+  } = getCreateSearchSchema({ bi: inBIPropertyIframe })
+  // 处理当前信息类型--展示数据类型
+  const  infoTypeDataType = computed(() =>  {
+    let result = 'all'
+    switch (activeTab.value) {
+      case 0:
+        result = 'all'
+        break
+      case 1:
+        result = 'base'
+        break
+      case 2:
+        result = 'advance'
+        break
+    }
+    return result
+  })
+
+
+  // 筛选条件动态配置处理
+  function disposeFilterSchema() {
+    const conf = computed(() => {
+      return {
+        isInApp: isInApp.value,
+        isLogin: isLogin.value,
+        vipUser: isVip.value && isInApp.value,
+        oldUser: isOld.value && isInApp.value,
+        showVip: isLogin.value && isInApp.value,
+        infoType: infoTypeDataType.value,
+        isBidField,
+        inInjectBI,
+        isVip: isVip.value
+      }
+    })
+
+    // 标准筛选
+    const SearchBidBaseSchema = ref([])
+    const SearchBidMoreSchema = ref([])
+    // 更多筛选
+    const searchBidMoreFreeSchema = ref([])
+    const searchBidMoreVipSchema = ref([])
+
+    SearchBidBaseSchema.value = createSearchBidBaseSchema(conf.value)
+    SearchBidMoreSchema.value =  createSearchBidMoreSchema(null, conf.value)
+
+    searchBidMoreFreeSchema.value = SearchBidMoreSchema.value?.filter(
+      (s) => !s.vipMark
+    )
+    searchBidMoreVipSchema.value = SearchBidMoreSchema.value?.filter(
+      (s) => s.vipMark
+    )
+
+    function doUpdateData(schema, type) {
+      const payload = schema || conf.value
+      if(type === 'more') {
+        SearchBidMoreSchema.value = createSearchBidMoreSchema(schema, conf.value)
+      } else {
+        SearchBidBaseSchema.value = createSearchBidBaseSchema(payload)
+      }
+    }
+
+    return {
+      SearchBidBaseSchema,
+      SearchBidMoreSchema,
+      searchBidMoreFreeSchema,
+      searchBidMoreVipSchema,
+      doUpdateData
+    }
+  }
+
+  // 列表状态
+  const listState = reactive({
+    finished,
+    loading,
+    pageNum: 1,
+    pageSize: 50,
+    total,
+    pageTotal: 0
+  })
+  // 当前展示的列表
+  const activeList = computed(() => {
+    let arr = []
+    if(list.value && list.value.length > 0) {
+      arr = list.value.map((v) => {
+        v._id = v.id
+        v.checked = selectIds.value.includes(v.id)
+        // 中标单位联系方式处理
+        if(!v.winnerTel &&  v.winnerInfo && v.winnerInfo.length > 0) {
+          v.winnerTel = v.winnerInfo[0].winnerTel || ''
+          v.winnerPerson = v.winnerInfo[0].winnerPerson || ''
+        }
+        const region = []
+        if(v.area && (v.city && v.city.indexOf(v.area) === -1)) {
+          region.push(v.area)
+        }
+        if(v.city) {
+          region.push(v.city)
+        }
+        if(v.district) {
+          region.push(v.district)
+        }
+        v.region = region.join('-')
+        return v
+      })
+    }
+    return arr
+  })
+  // 表格展示的数据
+  const tableList = ref([])
+
+  // 搜索关键词限制
+  const interceptKeywords = reactive({
+    interceptOtherWords: '',
+    interceptLimit: 0
+  })
+
+  // 根据权限处理列表头部的操作按钮展示
+  const limitFilterHeaderActions =  computed(() => {
+    // 数据导出-非营销都展示
+    if(!inResourceBIIframe) {
+      limitActions.value['data-export'] = true
+    }
+
+    // BI营销以及个人分析报告不展示以下操作
+    if(!inResourceBIIframe && !inInjectBI) {
+      limitActions.value['refined-list'] = true
+      limitActions.value['table'] = true
+      limitActions.value['collect-bid'] = true
+      // 登录后才展示详细列表
+      if(isLogin.value) {
+        limitActions.value['detailed-list'] = true
+      }
+    }
+
+    // 展示收录
+    if(canBatchEmploy.value && inResourceBIIframe) {
+      limitActions.value['employ-bid'] = true
+    }
+    // 展示分发
+    if((isEntAdmin.value || isDepartmentAdmin.value) && !inBIPropertyIframe && !inResourceBIIframe) {
+      limitActions.value['distribute-bid'] = true
+    }
+    return headerActions.value
+  })
+
+  // search-list 组件所需参数
+  const searchListProps = computed(() => {
+    return {
+      isSelectAllCheckbox: isSelectListAllCheckbox.value,
+      isSelectSomeCheckbox: isSelectSomeCheckbox.value,
+      selectCheckboxCount: selectCheckboxCount.value,
+      searchResultCount: searchResultCount.value,
+      headerActions: limitFilterHeaderActions.value,
+      list: activeList.value,
+      listState
+    }
+  })
+
+  /**
+   * 切换列表展示风格
+   * @param type - 可选风格 ['refined-list', 'detailed-list', 'table']
+   */
+  function doChangeItemStyleType(type) {
+    const styleTypes = ['refined-list', 'detailed-list', 'table']
+    if (!styleTypes.includes(type)) {
+      return console.warn('Not find style type!')
+    }
+    listItemStyleType.value = type
+    activeHeaderActions.value = without(
+      activeHeaderActions.value,
+      ...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 - 按钮原型
+   * @param item.key - 按钮标识
+   */
+  function doListHeaderAction(item, $event) {
+    if (!isLogin.value) {
+      return goLogin(false, 'reload')
+    }
+
+    const { key } = item
+    switch (key) {
+      case 'refined-list': {
+        doChangeItemStyleType(key)
+        break
+      }
+      case 'detailed-list': {
+        detailListClick(key)
+        break
+      }
+      case 'table': {
+        doChangeItemStyleType(key)
+        break
+      }
+      case 'data-export': {
+        onClickDataExport()
+        break
+      }
+      case 'collect-bid' : {
+        onClickDataCollect($event)
+        break
+      }
+      case 'distribute-bid': {
+        onClickDataDistribute()
+        break
+      }
+      case 'employ-bid' : {
+        onClickDataEmploy()
+        break
+      }
+    }
+  }
+  function detailListClick(key) {
+    if (isFree.value) {
+      openListVipDialog()
+      return
+    }
+    doChangeItemStyleType(key)
+  }
+  // 全选复选框事件
+  function doChangeAllSelect(type) {
+    doToggleListSelection(type)
+  }
+
+  // 单个复选框事件
+  function doChangeSelect(item) {
+    doToggleItemSelection(item.id)
+  }
+
+  function scrollToTopInWork(offset = 0) {
+    if (isInApp.value) {
+      try {
+        const workContainer = document.querySelector('.micro-app--layout')
+        if (workContainer) {
+          const scrollWrapper = workContainer.parentElement
+          if (scrollWrapper) {
+            scrollWrapper.scrollTop = offset || 0
+          }
+        }
+      } catch (error) {
+        console.log(error)
+      }
+    } else if (isInWeb.value) {
+      const wrapper = document.documentElement
+      if (wrapper) {
+        wrapper.scrollTop = offset || 0
+      }
+    }
+  }
+
+  // 分页事件
+  function doChangePageNum(page) {
+    listState.pageNum = page
+    scrollToTopInWork(0)
+    doQuery({}, 'pageNumChange', page)
+  }
+  // 分页大小事件
+  function doChangePageSize(size) {
+    listState.pageSize = size
+    listState.pageNum = 1
+    doQuery({}, 'pageNumChange', 1)
+  }
+  // 登录
+  $bus.$on('bidding:goLogin', (e) => {
+    // console.log(e)
+    goLogin()
+  })
+  function goLogin(type, redirectUrl) {
+    that.$showLoginDialog(type, redirectUrl)
+  }
+
+  function createClickInfoItem(item) {
+    if (!item) {
+      return {}
+    }
+    return {
+      id: item.id,
+      title: item.originalTitle || item.title || ''
+    }
+  }
+
+  const listArticleInfoStorageKey = 'sun_list_info_clicked'
+  // 处理拆条信息:详情页中需要判断purchasingList长度>1,就使用列表页标题+‘等’。
+  function saveClickItemInfo(item) {
+    const key = listArticleInfoStorageKey
+    const saved = sessionStorage.getItem(key)
+    const info = createClickInfoItem(item)
+    let list = []
+    if (saved) {
+      list = JSON.parse(saved)
+      if (Array.isArray(list)) {
+        list.unshift(info)
+      } else {
+        list = [info]
+      }
+    } else {
+      list = [info]
+    }
+    // 去重
+    list = uniqBy(list, 'id')
+    sessionStorage.setItem(key, JSON.stringify(list))
+  }
+
+
+  // 跳转详情页
+  function toDetail(item) {
+    if (!isLogin.value) {
+      return goLogin(false, 'reload')
+    }
+    let aHref = ".html"
+    if (inputKeywordsState.value.matchKeys && inputKeywordsState.value.matchKeys.length > 0){
+      const kds = inputKeywordsState.value.matchKeys.join(' ')
+      aHref+= '?kds=' + encodeURIComponent(kds)
+    }
+
+    if (aHref.includes('?')) {
+      aHref += '&from=sun'
+    } else {
+      aHref += '?from=sun'
+    }
+
+    const id = item.id
+    try {
+      that.$visited.push({
+        type: 'articleContent',
+        id:  id
+      })
+      item.visited = true
+    } catch(e) {
+      console.log(e)
+    }
+
+    try {
+      saveClickItemInfo(item)
+    } catch (error) {
+      console.log(error) 
+    }
+
+
+    if(isLogin.value) {
+      const prefix = '/article/content/'
+      const targetLink = `${prefix}${id}${aHref}`
+
+      // 在iframe里,往工作桌面跳转。不在iframe里,正常跳转
+      openLinkInWorkspace(isInApp.value, {
+        url: targetLink,
+        newTab: true,
+      })
+    } else{//没有登录跳转新的详情
+      // 一切都好渠道合作页,未登录跳转需要弹出登录,重置到已登录后的详情页
+      if(cooperateCode.value) {
+        window.location.href = '/notin/page'
+        // openLoginDig(null, "/article/content/"+thisId+aHref)
+      } else {
+        const targetLink = `/article/content/${id}${aHref}`
+        window.open(targetLink)
+      }
+    }
+  }
+  // 列表tag跳转处理
+  function tagToDetail (item, label) {
+    if(label === 'subtype' && (item['subtype'] === '拟建' || item['subtype'] === '采购意向')) {
+      return
+    }
+    let link = ''
+    if(label === 'subtype') {
+      link = item['subtypeUrl']
+    } else if(label === 'area'){
+      link = item['areaUrl']
+    }
+    if(link) {
+      window.open(link)
+    }
+  }
+
+  // 获取 store getters
+  const userType = computed(() => {
+    return useStore().getters['user/userType']
+  })
+
+  /**
+   * 变更搜索模式
+   * @type {boolean}
+   */
+  // P260需求精准搜索无结果时 自动切换到模糊搜索(查询) 模糊搜索也无结果时再切回精准(不查询)
+  // 条件:1.精准搜索模式 2.返回无数据 3.有主关键词或附加词
+  let autoSwitchModel = false
+  function changeSearchMode (searchType) {
+    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({}, searchType)
+    }
+    if(mhModel && autoSwitchModel) {
+      if( total > 0) {
+        if(!goback) {
+          that.$toast('精准搜索无结果,已为您自动切换到模糊搜索')
+        }
+        autoSwitchModel = false
+      } else {
+        // 模糊搜索无结果时 再切回精准
+        inputKeywordsState.value.searchMode = '0'
+        autoSwitchModel = false
+      }
+    }
+  }
+
+  /**
+   * 格式化请求参数
+   * @param [params] - 可选值,部分情况会提供,默认会和该函数返回值进行合并
+   */
+
+  function getParams(params = {}) {
+    // 合并所有模型的搜索筛选项
+    const result = Object.assign(
+      {
+        searchGroup: activeTab.value,
+        reqType: 'lastNews', // cache:空搜索缓存数据;lastNews:最新数据
+        pageNum: listState.pageNum,
+        pageSize: listState.pageSize,
+        // 该接口与用户身份有关
+        _expand: {
+          type: userType.value
+        }
+      },
+      getFormatOfInputKeywords(),
+      getFormatOfFilter(),
+      params
+    )
+    return result
+  }
+
+  function beforeSearchCheck (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
+      // 关键词去两边空格后为空
+      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
+  }
+  // 搜索前 处理一些事情
+  function beforeSearchSomething (pageNum) {
+    // 列表清空
+    list.value = []
+    listState.total = 0
+    // 列表重新获取时
+    if(!pageNum) {
+      listState.pageNum = 1
+      // 清空已选中数据
+      doClearAllSelection()
+      // 清空表格数据
+      tableList.value = []
+    }
+    // 如果列表展示状态为table,将列表展现形式换成详细列表或者精简列表
+    if(activeItemStyleType.value === 'T') {
+      restoreListTabActive()
+    }
+
+    //P271需求--潜客圈进引流需求
+    if(!advancedInfo.show && !getShowChart.value && inputKeywordsState.value.input) {
+      getCustomReportData({ keywords:  inputKeywordsState.value.input })
+    }
+
+  }
+  /**
+   * 统一查询入口
+   * - 拦截 doQuery 进行一些返回值处理
+   * @param [params] - 可选值,默认会和 getParams(params) 返回值进行合并
+   */
+  function doQuery(params = {}, searchType, pageNum) {
+    const bSearch = beforeSearchCheck(searchType)
+    if (!bSearch) {
+      return
+    }
+    beforeSearchSomething (pageNum)
+    // Ajax请求
+    return doRunQuery(getParams(params)).then((res) => {
+      // 搜索重新获取搜索历史
+      getHistoryQuery({ type: 1 })
+      afterQueryAjax(res, searchType)
+    })
+  }
+  // 处理查询请求后的数据以及其他业务
+  function afterQueryAjax (res, searchType) {
+    // 存储筛选条件
+    if(isInApp.value && isLogin.value && listState.pageNum === 1 && !inBIPropertyIframe) {
+      saveSearchGroupToLocal()
+
+      if(inputKeywordsState.value.input) {
+        saveFilterToLocal()
+      }
+    }
+    // 变更搜索模式
+    changeSearchMode(searchType)
+
+    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
+
+    //表格列表数据--只取第一页的前20条展示
+    if(listState.pageNum === 1) {
+      if(listState.total > 0) {
+        tableList.value = list.value.slice(0, 20)
+      } else {
+        tableList.value = []
+      }
+    }
+
+    // 列表无数据,禁用数据导出按钮
+    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 = that.$visited.check({
+        type: 'articleContent',
+        id: item.id
+      })
+      that.$set(item, 'visited', visited)
+
+      // 收藏字段显示
+      that.$set(item, 'collection', item.isCollected ? 1 : 0)
+
+      // 收录字段
+      that.$set(item, 'isEmploy', false)
+
+      // that.$set(item, 'publicType', '用户发布')
+      // 计算截止时间
+      if (item.signEndTime) {
+        const now = Date.now()
+        const signupEnd = item.signEndTime * 1000 < now
+        const signEndTimeText = dateFormatter(item.signEndTime * 1000, 'yyyy-MM-dd  HH:mm')
+        that.$set(item, 'signupEnd', signupEnd)
+        that.$set(item, 'signEndTimeText', signEndTimeText)
+      }
+
+      return item
+    })
+
+    // 列表底部-是否展示需要切换模糊搜索的tip
+    checkToggleSearchMode({
+      pageNum: 1,
+      count: origin?.total || 0,
+      blurCount: origin?.bCount || 0,
+      searchMode: Number(inputKeywordsState.value.searchMode)
+    })
+  }
+
+  // 是否展示筛选条件
+  const showFilter = ref(true)
+  // 展开收起筛选条件
+  function toggleFilter() {
+    showFilter.value = !showFilter.value
+  }
+
+  // 组合好的组件格式的筛选条件
+  function packageFilter () {
+    const originParams = Object.assign(
+      {
+        searchGroup: activeTab.value,
+      },
+      filterState.value,
+      inputKeywordsState.value
+    )
+
+    return originParams
+  }
+
+  // 格式化当前已选筛选条件
+  // P611需求,展示当前已选条件文案
+  const showCurrentFilterText = computed(() => {
+    if(!(isLogin.value && isInApp.value && !inBIPropertyIframe)) {
+      return {}
+    }
+    const originParams = packageFilter()
+    const text = FilterModel2ViewModel.formatAll(originParams)
+    // 融创
+    if (originParams.mobileTag) {
+      text.mobileTagText = originParams.mobileTag.includes('all') ? '' : originParams.mobileTag.join(',')
+    }
+    return text
+  })
+
+// P271需求,潜客圈进,展示超前项目or分析报告引流
+  const recommendCardCircleModel = recommendCardModel({ that })
+  const {
+    getShowChart,
+    advancedInfo,
+    getCustomReportData,
+  } = recommendCardCircleModel
+
+  /******开通超级订阅弹窗start**********/
+
+  // 开通超级订阅弹窗配置
+  const vipDialogConfig = reactive({
+    type: 'filter',
+    show: false,
+    text: '立享更多搜索权限,寻找商机更精准'
+  })
+
+  // 筛选条件打开超级订阅弹窗
+  function openFilterVipDialog () {
+    vipDialogConfig.type = 'filter'
+    vipDialogConfig.show =  true
+    vipDialogConfig.text = '立享更多搜索权限,寻找商机更精准'
+  }
+  // 关闭超级订阅弹窗
+  function closeVipDialog () {
+    vipDialogConfig.show = false
+  }
+  // 数据列表--开通超级订阅弹窗
+  function openListVipDialog () {
+    vipDialogConfig.type = 'list'
+    vipDialogConfig.show = true
+    vipDialogConfig.text = '立享列表展示更多公告关键信息,例如:采购单位、中标单位、招标代理机构等,提高公告查看效率'
+  }
+
+  const vipDialogConf = toRefs(vipDialogConfig)
+
+  // 筛选条件无权限提示开通超级订阅弹窗
+  $bus.$on('search:filter:no-power', openFilterVipDialog)
+
+  /******开通超级订阅弹窗end**********/
+
+  /** 保存、重置筛选条件部分 start ****/
+
+  const disposeFilterActionModel = saveFilterActionsModel({ isBidField })
+  const {
+    historyFilterDialogVisible, //已存筛选条件弹窗
+    getFilterHistoryList, // 已存筛选条件列表
+    checkFilterPass, // 检测是否可以保存筛选条件
+  } = disposeFilterActionModel
+
+  /**
+   * 保存筛选条件
+   */
+  async function onSaveFilter() {
+    const originParams = packageFilter()
+    const config = { filter: originParams }
+    await checkFilterPass(config)
+  }
+
+  /**
+   * 恢复筛选条件
+   * @param item
+   */
+  function onSelectedFilter (item) {
+    // 动态插入的存储的筛选条件
+    const _expand = getExpandSearchParams()
+    const { isPay } = item
+    // 恢复选中状态
+    if (isFree?.value && isPay) {
+      MessageBox.confirm('已存筛选条件包含仅限会员用户可用的筛选条件,如需使用请开通超级订阅', '',{
+        confirmButtonText: '开通超级订阅',
+        cancelButtonText: '使用免费条件',
+        center: true,
+        showClose: false,
+        customClass: 'filter-delete-messagebox',
+        confirmButtonClass: 'btn-group confirm-btn',
+        cancelButtonClass: 'btn-group cancel-btn',
+      })
+        .then(() => {
+          window.open('/swordfish/page_big_pc/free/svip/buy', '_blank')
+        })
+        .catch(() => {
+          // 之前是付费用户,现在变成普通用户了,需要重置一些vip选项!!!!
+          const params = Object.assign(item,{
+            bidField: isBidField ? 'medical' : '',
+            publishTime: 'thisyear',
+            selectType: ['title', 'content'],
+            subtype: item.subtype,
+            regionMap: {},
+            industry: item.industry,
+            fileExists: item.fileExists,
+            price: item.price,
+            buyerclass: {},
+            buyertel: '',
+            winnertel: '',
+            notkey: '',
+            buyer: '',
+            winner: '',
+            agency: '',
+          })
+          restoreFilter(params, 'saveBack', {_expand})
+          historyFilterDialogVisible.value = false
+        })
+    } else {
+      restoreFilter(item, 'saveBack', {_expand})
+    }
+  }
+  function restoreFilter (viewFilter, type, expandFilter) {
+    let resultFilter = {}
+    if(type === 'saveBack') {
+      resultFilter = FilterHistoryAjaxModelRestore.formatAll(viewFilter)
+    } else {
+      resultFilter = viewFilter
+    }
+    // 额外需要回显的拓展数据
+    if(expandFilter && typeof expandFilter === 'object') {
+      Object.assign(resultFilter, expandFilter)
+    }
+    doChangeTab({ key: resultFilter.searchGroup })
+    let  _expand = resultFilter._expand || {}
+
+    filterState.value = {
+      bidField: isBidField ? 'medical' : '',
+      publishTime: resultFilter.publishTime,
+      selectType: resultFilter.selectType,
+      subtype: resultFilter.subtype,
+      regionMap: resultFilter.regionMap,
+      industry: resultFilter.industry,
+      fileExists: resultFilter.fileExists,
+      price: resultFilter.price,
+      buyerclass: resultFilter.buyerclass,
+      buyertel: resultFilter.buyertel,
+      winnertel: resultFilter.winnertel,
+      notkey: resultFilter.notkey,
+      buyer: resultFilter.buyer,
+      winner: resultFilter.winner,
+      agency: resultFilter.agency,
+      ..._expand,
+      _expand: _expand
+    }
+    inputKeywordsState.value = {
+      input: resultFilter.input,
+      // 关键词筛选模式
+      searchMode: resultFilter.searchMode,
+      // 接口返回的关键词,如果模糊搜索,服务端会根据一定规则切割多个词
+      matchKeys: resultFilter.matchKeys,
+      // 附加关键词筛选模式
+      wordsMode: resultFilter.wordsMode,
+      // 附件关键词组
+      additionalWords: resultFilter.additionalWords,
+      selectMoreKey: resultFilter.additionalWords?.length > 0
+    }
+    if(type === 'saveBack') {
+      historyFilterDialogVisible.value = false
+    }
+    doQuery({}, 'not-filter')
+  }
+
+  // 重置筛选条件
+  function onResetFilter () {
+    updateFilterBase()
+    doQuery({}, 'not-filter')
+  }
+ /**保存、重置、查看筛选条件end******/
+
+ /*** 筛选条件头部、列表头部滚动start *****/
+
+  // 页面滚动方法 用于悬浮吸顶,将【筛选条件】置顶
+  const fixedTop = ref(false)
+  // 表格顶部操作方法滚动吸顶
+  const tableFixedTop = ref(false)
+
+  function watchScroll () {
+    const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
+    const watchFilter = document.querySelector('.searchTender').offsetTop - 25// 25为margin-top值
+    const watchTable = document.querySelector('.search-bidding-list-container').offsetTop - 15 // 15为margin-top值
+    if (scrollTop >= watchFilter) {
+      fixedTop.value = true
+      tableFixedTop.value = false
+    } else {
+      fixedTop.value = false
+    }
+    if (scrollTop >= watchTable) {
+      tableFixedTop.value = true
+      fixedTop.value = false
+    } else {
+      tableFixedTop.value = false
+    }
+  }
+
+  /*** 筛选条件头部、列表头部滚动end *****/
+
+  /*******数据导出 start **********/
+  const {
+    dataExport,
+    setExport,
+    exportDialogChange,
+    showDataExportDialog,
+    toPayDataExport
+  } = dataExportActionsModel ()
+
+  // 数据导出操作
+  function onClickDataExport (table) {
+    if(!isLogin.value) {
+      goLogin()
+      return
+    }
+
+    const originParams = packageFilter()
+    const params = {
+      listState: listState,
+      selectCheckboxCount: selectCheckboxCount.value,
+      selectIds: selectIds.value,
+      filter: originParams
+    }
+    if(table) {
+      toPayDataExport(params)
+    } else {
+      dataExport(params)
+    }
+
+  }
+
+  /*******数据导出 end ***********/
+
+
+  /********* 标讯收藏部分start ********/
+  const {
+    onCollect
+  } = dataCollectActionModel({ that })
+
+  // 批量标讯收藏
+  function onClickDataCollect (e) {
+    const config = {
+      total: listState.total,
+      isSelectSomeCheckbox: isSelectSomeCheckbox.value,
+      event: e,
+      selectIds: selectIds.value,
+    }
+    onCollect(config, 'batch')
+  }
+
+  // 单个标讯收藏
+  function onClickSingleCollect (data) {
+    const config = {
+      item: data.item,
+      total: listState.total,
+      event: data.event
+    }
+    onCollect(config, 'single')
+  }
+
+  // 挂载搜索事件,供模块js回调
+  /**
+   * params: {
+   *   type:  C存储, R取消
+   *   ids: 要操作的id数组
+   * }
+   */
+  $bus.$on('bidding:updateListCollectStatus', function (obj) {
+    const { type, ids } = obj
+    list.value = list.value.map(temp => {
+      if (type === 'R' && ids.includes(temp.id)) {
+        that.$set(temp, 'collection', 0)
+      } else if(type === 'C' && ids.includes(temp.id)){
+        that.$set(temp, 'collection', 1)
+      }
+      return temp
+    })
+  })
+  /********* 标讯收藏部分end ********/
+
+  /********* 分发部分start ********/
+  const {
+    usePowerRef,
+    openDistribute,
+    doSubmitDistribute
+  } = dataDistributeActionsModel()
+
+  function onClickDataDistribute () {
+    openDistribute(selectIds.value)
+  }
+  /*********分发部分end ********/
+
+  /********参标start *********/
+  // 参标只有莱茵有权限
+  // const {
+  //   BidrenewalDialogRef,
+  //   getJoinBidInfo,
+  //   onJoinBid
+  // } = joinBidActionsModel ()
+
+  // 窗口切换刷新参标数据
+  document.addEventListener('visibilitychange', function () {
+    if (document.visibilityState === 'visible') {
+      that.$visited.refreshVisited()
+      // if(isLogin.value) {
+      //   getJoinBidInfo(listIds.value)
+      // }
+    }
+  })
+
+  // 变更列表数据的参标状态
+  $bus.$on('bidding:updateListJoinStatus', function (obj) {
+    const {item, joinData } = obj
+    if(joinData) {
+      for (const temp of joinData) {
+        list.value = list.value.map(v => {
+          if (temp.id === v.id) {
+            that.$set(v, 'joinBid', Boolean(temp.value))
+          }
+          return v
+        })
+      }
+    } else {
+      list.value = list.value.map((temp) => {
+        if (temp.id === item?.id) {
+          that.$set(temp, 'joinBid', true)
+        }
+        return temp
+      })
+    }
+  })
+  /*******参标end ***********/
+
+  /*******收录start *********/
+  const {
+    showPropertyDialog,
+    propertyIframeSrc,
+    canBatchEmploy,
+    getEmployData,
+    onSingleEmploy,
+    onBatchEmploy
+  } = dataEmployActionsModel({ inBIPropertyIframe })
+
+  // 变更列表收录状态
+  $bus.$on('bidding:updateDateEmployStatus', function (obj) {
+    const { type, employIds } = obj
+    list.value = list.value.map(function (item) {
+      // 添加收录
+      if(type === 'add' && employIds.includes(item.id)) {
+        item.isEmploy = true
+      }
+      // 取消收录
+      if(type === 'cancel' && employIds.includes(item.id)) {
+        item.isEmploy = false
+      }
+      return item
+    })
+  })
+  // 批量收录
+  function onClickDataEmploy() {
+    onBatchEmploy(selectIds.value)
+  }
+  /*******收录end ***********/
+
+  /*****BI添加操作start*********/
+  const {
+    getBidAddInfos,
+    doAddInfoOfBI
+  } = dataAddActionsModel()
+
+  $bus.$on('bidding:updateDataAddStatus', function(params) {
+    const { type, ids } = params
+    list.value = list.value.map(function (item) {
+      // 添加收录
+      if(type === 'add' && ids.includes(item.id)) {
+        item.isAdd = true
+      }
+      // 取消收录
+      if(type === 'cancel' && ids.includes(item.id)) {
+        item.isAdd = false
+      }
+      return item
+    })
+  })
+  // 批量添加
+  function onAddInfoOfBI () {
+    doAddInfoOfBI({ ids: selectIds.value })
+  }
+  // 单个添加
+  function onSingleAddInfo (item) {
+    doAddInfoOfBI({ item })
+  }
+  /*****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', () => {
+      })
+    }
+  }
+
+  /*******工作台跳转start***********/
+  function guideGoWorkSpace () {
+    if (!isLogin.value) {
+      goLogin(false, 'reload')
+      return
+    }
+    goWorkSpace()
+  }
+  // 跳转到工作台
+  function goWorkSpace () {
+    const { key, _storage } = storageConfig.filter
+    const goHref_ = location.origin + '/swordfish/page_big_pc/search/sun'
+    // 组件筛选条件
+    const originParams = packageFilter()
+    _storage.setItem(key, JSON.stringify(originParams))
+
+    window.location.replace('/page_workDesktop/work-bench/app/big/search/sun')
+  }
+
+  // 监听路由事件
+  function backRouteParams () {
+    const { searchGroup, subtype } = useRoute().query
+    if(searchGroup) {
+      // 进入页面,默认回显tab
+      const key = Number(searchGroup)
+      doChangeTab({ key})
+    } else if(subtype && subtype ==='采购意向' && inBIPropertyIframe) {
+      // 物业专版,采购意向回显
+      filterState.value.subtype = {采购意向: ['采购意向']}
+    }
+  }
+  /*******工作台跳转end***********/
+
+  /***页面缓存回显相关*******/
+  /**
+   * 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
+    }
+  }
+
+  /**
+   * 从缓存中恢复搜索范围
+   */
+  function restoreSelectTypeFromLocal () {
+  }
+
+  /**
+   *  恢复和保存筛选条件到本地业务说明:
+   *  工作台内 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) {
+      const expandFilter = {}
+      if(urlSelectType) {
+        expandFilter.selectType = urlSelectType.split(',')
+      }
+      if(urlKeywords) {
+        expandFilter.input = urlKeywords
+      }
+      // 身份切换时候,本地缓存的上次筛选条件有融创等动态拓展筛选条件,但是切换到的身份没有时候,需要移除这些动态筛选条件
+      const jParams = JSON.parse(params)
+      restoreFilter(jParams, 'localBack', expandFilter)
+    }
+  }
+
+  // 恢复筛选条件
+  // 恢复优先级、外边跳转进工作台的最高、其次是本地缓存、最后是已存筛选条件第一条
+  function getLastFilter () {
+    if(isLogin.value) {
+      const { key, _storage } = storageConfig.filter
+      const params =_storage.getItem(key)
+      const jParams = params ? JSON.parse(params) : null
+      // 工作台内
+      if(isInApp.value) {
+        // 免费用户恢复tab,搜索范围默认标题和正文
+        if(isFree.value) {
+          restoreSearchGroupFromLocal()
+          let defaultSelectType = ['title', 'content']
+          // 如果路由传参带入搜索范围参数
+          if(urlSelectType) {
+            const arr = urlSelectType.split(',')
+            defaultSelectType = arr
+          }
+          filterState.value.selectType = defaultSelectType
+          if(!goback) {
+            // 非工作台外跳转进入则默认搜索
+            firstSearch()
+          }
+        }
+
+        // vip 或者 桌面跳转回显筛选条件
+        if(goback || (isVip.value && jParams)) {
+          restoreFilterFromLocal()
+        }
+
+        // 获取已存筛选条件的列表
+        getFilterHistoryList(null,function (arr) {
+          if(isVip.value && !jParams) {
+            // 付费用户,优先恢复本地缓存,本地无缓存,则恢复已存筛选第一条数据
+            if(Array.isArray(arr) && arr.length > 0) {
+              const _expand = getExpandSearchParams()
+              const expandFilter = {
+                _expand: _expand,
+              }
+              if(urlSelectType) {
+                expandFilter.selectType = urlSelectType.split(',')
+              }
+              if(urlKeywords) {
+                expandFilter.input = urlKeywords
+              }
+              restoreFilter(arr[0], 'saveBack', expandFilter)
+            } else {
+              firstSearch()
+            }
+          }
+        })
+      } else {
+        filterState.value.selectType = ['title', 'content']
+        firstSearch()
+      }
+    } else {
+      filterState.value.selectType = ['title', 'content']
+      firstSearch()
+    }
+  }
+  // 获取本地缓存的动态插入的筛选条件
+  function getExpandSearchParams () {
+    // 动态获取存储的筛选条件
+    const expandSearchParams = localStorage.getItem('search_bidding_expandSearchParams')
+    let _expand = {}
+    if(expandSearchParams) {
+      _expand = JSON.parse(expandSearchParams)
+    }
+    return  _expand
+  }
+
+  function firstSearch () {
+    return doQuery({}, 'firstSearch')
+  }
+  /**
+   * 检查是否需要切换模糊搜索、是否展示提示
+   * 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
+  })
+  /**********切换模糊搜索end*********/
+
+  // 页面初始化
+  function initPage () {
+    const { searchGroup } = useRoute().query
+    // 路由所带参数回显
+    backRouteParams()
+    // 恢复以前选过的tab
+    if(!searchGroup) {
+      restoreListTabActive()
+    }
+  }
+  initPage()
+
+  // // 恢复上次筛选条件
+  onMounted(() => {
+    if (isInApp.value) {
+      window.top.document.title = '剑鱼标讯'
+    }
+    if(!inBIPropertyIframe) {
+      // getLastFilter()
+      firstSearch()
+    } else {
+      firstSearch()
+    }
+    //  P271潜客圈进--客户引流请求
+    if(inputKeywordsState.value.input) {
+      getCustomReportData({ keywords: inputKeywordsState.value.input})
+    }
+  })
+
+
+  return {
+    isLogin,
+    isInApp,
+    isInWeb,
+    inResourceBIIframe,
+    inBIPropertyIframe,
+    inBITopNet,
+    inInjectBI,
+    isInBI,
+    isFree,
+    isVip,
+    isBidField,
+    goLogin,
+    guideGoWorkSpace,
+    cooperateCode,
+    list,
+    tableList,
+    searchModelOptions,
+    searchListProps,
+    SearchTabsModel,
+    inputKeywordsState,
+    filterState,
+    updateFilterBase,
+    listState,
+    interceptKeywords,
+    activeItemStyleType,
+    disposeFilterSchema,
+    doQuery,
+    doListHeaderAction,
+    doChangeAllSelect,
+    doChangeSelect,
+    doChangePageNum,
+    doChangePageSize,
+    onChangeTab,
+    activeTab,
+    vipDialogConf,
+    closeVipDialog,
+    onSaveFilter, // 以下存筛选条件相关
+    disposeFilterActionModel,
+    historyFilterDialogVisible,
+    onSelectedFilter,
+    onResetFilter,
+    toggleFilter,
+    showFilter,
+    fixedTop,
+    tableFixedTop,
+    toDetail,
+    tagToDetail,
+    onClickDataExport, // 以下为导出相关
+    setExport,
+    exportDialogChange,
+    showDataExportDialog,
+    onClickSingleCollect, // 收藏
+    usePowerRef, // 以下分发相关
+    doSubmitDistribute,
+    // onJoinBid,//以下 参标
+    // BidrenewalDialogRef,
+    showPropertyDialog,// 以下收录
+    propertyIframeSrc,
+    onSingleEmploy,
+    onAddInfoOfBI,
+    onSingleAddInfo,
+    goWorkSpace,
+    timeSelectorText,
+    collectElementRef,
+    onFreeTaste,
+    getLastFilter,
+    toggleBlurModeTip,
+    doToggleSearchBlurMode,
+    recommendCardCircleModel,
+    goToBidSearchPage,
+    getWhiteList,
+    storageConfig,
+    clearHistoryQuery,
+    searchHistoryList,
+    showCurrentFilterText
+  }
+}

+ 9 - 0
apps/bigmember_pc/src/views/search/sun/model/index.js

@@ -0,0 +1,9 @@
+import useModel from './base'
+
+let SearchBidModel = {}
+function useSearchBidModel() {
+  SearchBidModel = useModel()
+  return SearchBidModel
+}
+
+export { useSearchBidModel, SearchBidModel }

+ 99 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/before-search.js

@@ -0,0 +1,99 @@
+// 获取是否允许空搜索白名单
+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, urlKeywords } = paramsObj
+  // 是否是白名单用户
+  let onTheWhiteList = ref(false)
+  // 关键词搜索的校验规则
+  const commonSearchWordsRegExp = ref([])
+
+  async function getWhiteList () {
+    const keywords = urlKeywords || 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.value) {
+        const timer = setTimeout(function() {
+          clearTimeout(timer)
+          const hasCommonWords = checkAndClearTextIncludesCommonWords(keywords)
+          if (hasCommonWords) {
+            showToast('请输入项目名称等关键词')
+          }
+        })
+      }
+    }
+  }
+
+  // 判断文字中是否有通用词(返回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 {
+    getWhiteList,
+    onTheWhiteList,
+    checkAndClearTextIncludesCommonWords
+  }
+
+}
+
+

+ 57 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/data-add-actions.js

@@ -0,0 +1,57 @@
+import { ref } from 'vue'
+import { ajaxGetInfoIds, ajaxSetInfoId } from '@/api/modules/bi'
+import $bus from '@/utils/bus'
+import { difference } from 'lodash'
+import { showToast } from '@/components/toast'
+
+export function dataAddActionsModel () {
+  let hadAddList = []
+
+  // 获取已添加的信息id
+  async function getBidAddInfos () {
+    const { error_code: code, data } = await ajaxGetInfoIds()
+    if(code === 0) {
+      const ids = data || []
+      $bus.$emit('bidding:updateDataAddStatus', { type: 'add', ids })
+      hadAddList = hadAddList.concat(ids)
+    }
+  }
+  async function doAddInfoOfBI(objParams) {
+    const { item, ids } = objParams
+    // 已添加过不处理
+    if(item?.isAdd){
+      return
+    }
+    let infoIds = []
+    if(item) {
+      infoIds = [item.id]
+    }
+    if(ids) {
+      if (ids.length > 0) {
+        infoIds = difference(ids, hadAddList )
+      } else {
+        return showToast('尚未选择数据,请选择')
+      }
+
+    }
+    if(!infoIds.length) {
+      return showToast('所选数据均已添加过,请重新选择')
+    }
+    const params = {
+      info_id: infoIds.join(','),
+      source: 2
+    }
+    const { data, error_code: code } = await ajaxSetInfoId(params)
+    if (code === 0 && data?.status === 1) {
+      showToast('添加成功')
+      getBidAddInfos()
+    } else {
+      showToast('添加操作失败')
+    }
+  }
+
+  return {
+    getBidAddInfos,
+    doAddInfoOfBI
+  }
+}

+ 414 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/data-collect-actions.js

@@ -0,0 +1,414 @@
+import { computed, onMounted, ref } from 'vue'
+import { useStore } from '@/store'
+import { getEventTarget } from '@/utils/jq-help'
+import $bus from '@/utils/bus'
+import {
+  bidCollAction,
+  getBidCollTagList,
+  saveBidCollAddTag,
+  createBidTag,
+  checkBidsIsColl,
+} from '@/api/modules/'
+
+// 是否登录
+const isLogin = computed(() => {
+  return useStore().getters['user/loginFlag']
+})
+
+// 是否是免费用户
+const isFree = computed(() => {
+  return useStore().getters['user/isFree']
+})
+
+export function dataCollectActionModel (gParams) {
+  const { that } = gParams
+
+  onMounted(() => {
+    initCollectEvent()
+  })
+
+  function onCollect (config, type) {
+    // 未登录跳转登录
+    if (!isLogin.value) {
+      $bus.$emit('bidding:goLogin')
+      return
+    }
+
+    const { total, isSelectSomeCheckbox, event, item, selectIds } = config
+
+    // 列表无数据
+    if (!total) return
+
+    let binfo = []
+    if(type === 'batch') {
+      // 无选择标讯
+      if (!isSelectSomeCheckbox) {
+        return that.$toast('尚未选择标讯,请选择')
+      }
+      binfo = selectIds.map(id => {
+        return {
+          bid: id
+        }
+      })
+    } else {
+      binfo = [{
+        bid: item.id
+      }]
+    }
+    // 单条取消收藏
+    if(item?.collection) {
+      const reParams = {
+        baction: 'R',
+        binfo,
+        type,
+        event
+      }
+      ajaxForCollectChange(reParams, function (res) {
+        if (res.data) {
+          // 更新列表当前收藏状态
+          const ids = [item.id]
+          $bus.$emit('bidding:updateListCollectStatus', { type: 'R', ids })
+
+          that.$toast('已取消收藏', 800)
+        } else {
+          that.$toast(res.error_msg, 1000)
+        }
+      })
+    } else {
+      // 将本次收藏的标讯id缓存起来 用于绑定标签时使用
+      sessionStorage.setItem('$save-tags-binfo', JSON.stringify(binfo))
+      const reParams = {
+        baction: 'C',
+        binfo,
+        type,
+        event
+      }
+      if (isFree.value) {
+        // 打开留资弹窗
+        const collectElementRef = that.$refs.collectElementRef
+        collectElementRef?.isNeedSubmit('article_collection', () => {
+          ajaxForCollectChange(reParams)
+        })
+      } else {
+        ajaxForCollectChange(reParams)
+      }
+    }
+  }
+
+  async function ajaxForCollectChange(reParams, callback) {
+    const $ = that.$querySelector.bind(that)
+    const { baction, binfo, type, event } = reParams
+    const { top, left } = calcCardTopLeft(event, type === 'batch')
+    /*
+      收藏或取消收藏ajax
+      params: {
+        baction: String, //用户行为:R:移除收藏;C:收藏(默认)非必填
+        binfo: Array, // 招标信息数组 必填
+        bid: String, // 招标信息加密后id 必填
+      }
+      参数示例 (baction=R binfo数组只需要bid即可)onSizeChange
+    */
+    const params = {
+      baction: baction,
+      binfo: binfo
+    }
+    const res = await bidCollAction(params)
+    if(callback) {
+      callback(res)
+      return
+    }
+    const {error_code: code, data, error_msg } = res
+    if(code === 0 && data) {
+      that.$toast('收藏成功', 1500)
+      $('.tags-box')
+        .show(function () {
+          window.pushListActiveTags = []
+          $('.tag-labels').empty()
+          $('.clear-input').val('')
+          $('.tags-list').find('.tags-item').removeClass('tags-active')
+          $('.tag-placeholder').show()
+        })
+        .css({
+          top: top,
+          right: 'unset',
+          left: left
+        })
+      window.getUserTags()
+      // 更新列表当前收藏状态
+      const ids = binfo.map(temp => {
+        return temp.bid
+      })
+      $bus.$emit('bidding:updateListCollectStatus', { type: 'C', ids })
+    } else {
+      if (error_msg.indexOf('付费') > -1) {
+        that.$toast('您的标讯收藏上限为5000条,请联系客服人员。', 1500)
+      }
+    }
+  }
+
+
+  // 处理收藏弹出的标签框
+  function initCollectEvent () {
+    const $ = that.$querySelector.bind(that)
+
+    function toastFn(text, duration) {
+      that.$toast(text, duration)
+    }
+
+    // 自定义标签
+    // 标签输入框事件
+    $('.tags-box').click(function (e) {
+      e.stopPropagation()
+    })
+    $('.tag-input').click(function (e) {
+      e.stopPropagation()
+      $(this).children('.tag-placeholder').hide()
+      $(this).children('input').focus()
+    })
+    // 标签输入框回车事件
+    $('.tag-input .clear-input').keydown(function (event) {
+      event.stopPropagation()
+      if (event.keyCode == 13) {
+        if (!$('.tags-box').is(':hidden')) {
+          $('.tags-inputs .add-tag-button').trigger('click')
+        }
+      }
+    })
+    // 标签输入框失去焦点事件
+    $('.tag-input .clear-input').blur(function () {
+      if ($('.tag-labels').children().length == 0 && $(this).val() == '') {
+        $('.tag-placeholder').show()
+      }
+    })
+    // 添加标签按钮事件
+    $('.tags-inputs .add-tag-button').on('click', function () {
+      var input = $('.tag-input .clear-input')
+      if (input.val().length >= 2 && input.val().length < 11) {
+        // ajax提交自定义标签
+        addTagsAjax(input.val())
+      }
+    })
+    // 点击确定按钮,绑定标签
+    $('.tags-footer .button-confirm').on('click', function () {
+      if (!$('.tags-box').is(':hidden')) {
+        var lids = ''
+        var lname = ''
+        $('.tags-item.tags-active').each(function () {
+          if ($(this).attr('data-id')) {
+            if (lids != '') {
+              lids += ','
+            }
+            if (lname != '') {
+              lname += ','
+            }
+            lids += $(this).attr('data-id')
+            lname += $(this).text()
+          }
+        })
+        var params = {
+          lids: lids,
+          laction: 'S',
+          binfo: JSON.parse(sessionStorage.getItem('$save-tags-binfo'))
+        }
+
+        // 执行保存绑定标签操作
+        if (params.lids !== '') {
+          saveChooseTags(params, function () {
+            $('.tags-footer .button-cancel').trigger('click')
+          })
+        }
+      }
+    })
+
+    $('.tags-footer .button-cancel').on('click', function () {
+      $('.tags-box').hide(function () {
+        // 标签弹框消失时 清除上次选择的标签分类
+        pushListActiveTags = []
+        $('.tag-labels').empty()
+        $('.clear-input').val('')
+        $('.tags-list').find('.tags-item').removeClass('tags-active')
+        $('.tag-placeholder').show()
+      })
+    })
+
+    window.pushListActiveTags = [] // 选中的自定义标签 作为全局变量使用
+    // 解绑自定义标签
+    function deleteInputTag(item) {
+      var index = $(item).parent().attr('data-index')
+      var id = $(item).parent().attr('data-id')
+      pushListActiveTags.splice(index, 1)
+      inputTagList()
+      $('.tags-item[data-id="' + id + '"]').removeClass('tags-active')
+    }
+
+    function inputTagList() {
+      var ht = ''
+      $('.tag-labels').html(ht)
+      pushListActiveTags.forEach(function (v, i) {
+        ht +=
+          '<span class="tag-label" data-index=' +
+          i +
+          ' data-id="' +
+          v.lid +
+          '">'
+        ht += '<em>' + v.lname + '</em>'
+        ht += '<i class="tag-close"></i>'
+        ht += '</span>'
+      })
+      $('.tag-labels')
+        .html(ht)
+        .off('click')
+        .on('click', '.tag-close', function (e) {
+          const target = getEventTarget(e)
+          deleteInputTag(target)
+        })
+      if ($('.tag-labels').children('.tag-label').length > 0) {
+        $('.tag-placeholder').hide()
+      }
+      checkTagDisabled()
+    }
+
+    // 渲染标签列表数据
+    function renderTagsList(data) {
+      if (data && data.length > 0) {
+        var ht = ''
+        data.forEach(function (v, i) {
+          ht +=
+            '<span class="tags-item" data-count=' +
+            v.count +
+            ' data-id=' +
+            v.lid +
+            '>' +
+            v.lanme +
+            '</span>'
+        })
+        $('.tags-list').html(ht)
+        pushListActiveTags.forEach(function (s, j) {
+          $('.tags-list .tags-item[data-id="' + s.lid + '"]').addClass(
+            'tags-active'
+          )
+        })
+        $('.tags-item').click(function (e) {
+          e.stopPropagation()
+          if ($(this).hasClass('disabled')) return
+          var id = $(this).attr('data-id')
+          var name = $(this).text()
+          $(this).toggleClass('tags-active')
+          if ($(this).hasClass('tags-active')) {
+            pushListActiveTags.push({
+              lid: id,
+              lname: name
+            })
+            inputTagList()
+          } else {
+            var newArr = pushListActiveTags.filter(function (item) {
+              return item.lid != id
+            })
+            pushListActiveTags = newArr
+            inputTagList()
+          }
+        })
+      }
+      inputTagList()
+    }
+
+    // 获取用户自定义标签
+    function getUserTags() {
+      getBidCollTagList().then((r) => {
+        if (r.error_code == 0 && Array.isArray(r.data)) {
+          renderTagsList(r.data.reverse())
+        }
+      })
+    }
+
+    window.getUserTags = getUserTags
+
+    /*
+      保存或清除标签 ajax
+      params: {
+        lids: String 标签id(加密后),  非必传
+        lname: String 标签名称,  非必传
+        laction: String  用户行为:S添加或绑定标签;D删除标签  非必传
+        binfo: Array 招标信息数组(已收藏的招标信息) 非必传
+        bid: String 招标信息加密后id  必传
+      }
+      1:lids为空;lname不为空;laction=”S”;binfo数组不为空->新增标签并且绑定收藏信息
+      2:lids不为空;laction=”S”;binfo数组不为空->收藏信息绑定标签
+      3:lids不为空;laction=”D”;->删除标签 并解绑收藏的信息
+    */
+    function saveChooseTags(params, callback) {
+      saveBidCollAddTag(params).then((r) => {
+        if (r.data) {
+          toastFn('标签绑定成功', 1000)
+          callback && callback()
+        }
+      })
+    }
+
+    // 新增标签
+    function addTagsAjax(name) {
+      createBidTag({ name }).then((r) => {
+        if (r.data) {
+          $('.tag-input .clear-input').val('')
+          // 添加标签成功后 绑定标签
+          if (pushListActiveTags.length < 3) {
+            pushListActiveTags.push({
+              lid: r.data,
+              lname: name
+            })
+          }
+          getUserTags()
+        } else {
+          // toastFn(r.error_msg, 1000)
+          toastFn('标签已经存在,无需添加', 1000)
+        }
+      })
+    }
+
+    function checkTagDisabled() {
+      if (pushListActiveTags.length >= 3) {
+        // 禁用标签
+        $('.tags-list')
+          .find('.tags-item:not(.tags-active)')
+          .addClass('disabled')
+      } else {
+        // 解除禁用
+        $('.tags-list').find('.disabled').removeClass('disabled')
+      }
+    }
+
+    getUserTags()
+  }
+
+  //计算收藏标签框的位置数据
+  function calcCardTopLeft (e, batch) {
+    const $ = that.$querySelector.bind(that)
+    const containerWidth = that.$el.clientWidth
+    const containerHeight = that.$el.clientHeight
+    const cardWidth = 332
+    const cardHeight = 362
+    var top = parseInt($(getEventTarget(e)).position().top) + 40
+    if (!batch) {
+      top -= 10
+    }
+    var left = parseInt($(getEventTarget(e)).offset().left) - 300
+
+    if (top >= containerHeight - cardHeight) {
+      top = containerHeight - cardHeight
+    }
+    if (left >= containerWidth - cardWidth) {
+      left = containerWidth - cardWidth
+    }
+
+    left += 'px'
+    top += 'px'
+    return {
+      top,
+      left
+    }
+  }
+
+  return {
+    onCollect
+  }
+}

+ 46 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/data-distribute-actions.js

@@ -0,0 +1,46 @@
+// 分发业务
+import { ref } from 'vue'
+import { ajaxSetDidDistributor } from '@/api/modules'
+import { Message } from 'element-ui'
+import { showToast } from '@/components/toast'
+
+export function dataDistributeActionsModel() {
+
+  const usePowerRef = ref(null)
+  let sIds = []
+  function openDistribute(selectIds) {
+    if(!selectIds.length) {
+     return showToast('尚未选择公告,请选择')
+    }
+    sIds = selectIds
+    usePowerRef.value.titleMsg = '选择接收人员'
+    usePowerRef.value.searchVal = ''
+    usePowerRef.value.centerDialogVisible = true
+    usePowerRef.value.selectDataIds = selectIds
+    usePowerRef.value.getData('yes')
+  }
+
+  // 提交分发
+  function doSubmitDistribute(data) {
+    ajaxSetDidDistributor({
+      infoids: sIds,
+      staffs: data
+    }).then((res) => {
+      if (res.error_code === 0) {
+        if (res.data === 1) {
+          Message({ message: '分发成功', type: 'success' })
+        } else {
+          Message({ message: res.error_msg, type: 'warning' })
+        }
+      } else {
+        Message({ message: res.error_msg, type: 'warning' })
+      }
+    })
+  }
+
+  return {
+    usePowerRef,
+    openDistribute,
+    doSubmitDistribute
+  }
+}

+ 155 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/data-employ-actions.js

@@ -0,0 +1,155 @@
+// 收录业务
+import { computed, ref } from 'vue'
+import { useStore } from '@/store'
+import $bus from '@/utils/bus'
+import { ajaxEmployInfo, ajaxEmployOperate, ajaxOptEmployOperate, ajaxOptOperateExist } from '@/api/modules/'
+import { showToast } from '@/components/toast'
+
+// 是否登录
+const isLogin = computed(() => {
+  return useStore().getters['user/loginFlag']
+})
+
+
+export function dataEmployActionsModel (gParams) {
+  const { inBIPropertyIframe } = gParams
+  // 是否可以批量收录
+  const canBatchEmploy = ref(false)
+
+  // 物业专版收录-创建情报iframe弹窗是否展示
+  const showPropertyDialog = ref(false)
+  // 创建情报iframe地址
+  const propertyIframeSrc = ref('')
+
+  // 查询收录情况
+  async function getEmployData (listIds) {
+    const params = {
+      employType: 1,
+      idArr: listIds.join()
+    }
+    const { data, batchEmploy, error_code: code } = await ajaxEmployInfo(params)
+    if(code === 0) {
+      // 批量收录操作权限
+      if(batchEmploy) {
+        canBatchEmploy.value = !!batchEmploy
+      }
+      if (Array.from(data) && data.length > 0) {
+        const employIds = []
+        data.forEach(item => {
+          if(item.isEmploy) {
+            employIds.push(item.id)
+          }
+        })
+        // 添加收录
+        $bus.$emit('bidding:updateDateEmployStatus', { type:'add', employIds: employIds })
+      }
+    }
+  }
+  // 批量收录
+  function onBatchEmploy (selectIds) {
+    // 未登录跳转登录
+    if (!isLogin.value) {
+      location.href = '/notin/page?close_goBack=1'
+      return
+    }
+    if(!selectIds.length) {
+     return showToast('尚未选择标讯,请选择')
+    }
+
+    // 物业专版收录
+    if(inBIPropertyIframe) {
+      const params = {
+        ids: selectIds.join(',')
+      }
+      ajaxOptEmployOperate(params).then((res) => {
+        const { code, data, msg } = res
+        // 物业专版
+        if(inBIPropertyIframe) {
+          if (code === 1) {
+            // 全部收录成功
+            $bus.$emit('bidding:updateDateEmployStatus',  { type:'add', employIds: selectIds })
+          } else if(code === 2) {
+            const ids = data?.split(',')
+            // code 2 部分收录成功
+            $bus.$emit('bidding:updateDateEmployStatus', { type:'add', employIds: ids })
+          }
+          showToast(msg)
+        }
+      })
+    } else {
+      // 营销收录
+      let params = {
+        idArr: selectIds.join(','),
+        isEmploy: true,
+        employType: 1
+      }
+      ajaxEmployOperate(params).then((res) => {
+        const { error_code: code, data, msg } = res
+        if (code === 0 && data.status) {
+          $bus.$emit('bidding:updateDateEmployStatus', { type:'add', employIds: selectIds })
+        }
+      })
+    }
+
+  }
+
+  // 收录操作
+  function onSingleEmploy(item) {
+    let params = {}
+    params = {
+      idArr: item.id,
+      isEmploy: !item.isEmploy,
+      employType: 1,
+    }
+    // 取消收录添加参数
+    if(!item.isEmploy) {
+      params.from = 'jhfp'
+    }
+    // 物业专版,添加收录
+    if(inBIPropertyIframe && !item.isEmploy) {
+      params = {
+        id: item.id
+      }
+      ajaxOptOperateExist(params).then(res => {
+        const { code, msg } = res
+        if (code === 2) {
+          // 已经创建情报信息,直接收录
+          $bus.$emit('bidding:updateDateEmployStatus', { type:'add', employIds: [item.id] })
+        } else if (code === 1) {
+          // 未创建情报信息,需要手动创建
+          propertyIframeSrc.value = `${location.origin}/succbi/crm_system/app/crm.app/%E9%80%9A%E7%94%A8%E5%88%9B%E5%BB%BA/create_intelligence.spg?t=${new Date().getTime()}`
+          showPropertyDialog.value = true
+        } else {
+          showToast(msg)
+        }
+      })
+    } else {
+      // 物业专版添加收录、营销添加或者取消收录
+      ajaxEmployOperate(params).then(res => {
+        const {error_code: code, data } = res
+        if(code === 0) {
+          if(data.status) {
+            // 取消收录操作
+            if(item.isEmploy) {
+              $bus.$emit('bidding:updateDateEmployStatus', { type:'cancel', employIds: [item.id] })
+            } else {
+              $bus.$emit('bidding:updateDateEmployStatus', { type:'add', employIds: [item.id] })
+            }
+          } else {
+            showToast(data.msg)
+          }
+        }
+      })
+    }
+  }
+
+  return {
+    showPropertyDialog,
+    propertyIframeSrc,
+    canBatchEmploy,
+    getEmployData,
+    onSingleEmploy,
+    onBatchEmploy
+  }
+
+}

+ 121 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/data-export-actions.js

@@ -0,0 +1,121 @@
+import { computed, ref } from 'vue'
+import $bus from '@/utils/bus'
+import {
+  ajaxGetDontPromptAgain,
+  ajaxSetDontPromptAgain,
+  getPushListExport,
+  defaultSelectEnt,
+  searchIndexDataExport
+} from '@/api/modules/'
+
+import { useStore } from '@/store'
+import  { FilterHistoryViewModel2AjaxModel, openOuterLink } from '@/utils'
+
+// 是否登录
+const isLogin = computed(() => {
+  return useStore().getters['user/loginFlag']
+})
+
+export function dataExportActionsModel () {
+  const exportLoading = ref(false)
+  const showDataExportDialog = ref(false)
+
+  // 数据导出获取的筛选条件
+  const filterFormatParams = ref({})
+
+  async function dataExport (config) {
+    const { listState, selectCheckboxCount, selectIds, filter } = config
+    // 未登录跳转登录
+    if (!isLogin.value) {
+      $bus.$emit('bidding:goLogin')
+      return
+    }
+    if (!listState.total) return
+
+    filterFormatParams.value = FilterHistoryViewModel2AjaxModel.formatAll(filter)
+    const _expand = filter._expand || {}
+    Object.assign(filterFormatParams.value, _expand)
+    exportLoading.value = true
+    // 查询是否需要弹窗
+    const { error_code: code, isPrompt } = await ajaxGetDontPromptAgain()
+    if(code === 0) {
+      exportLoading.value = false
+
+      let countBool = false
+      // 限制2000条提示弹窗
+      if(selectCheckboxCount > 0) {
+        countBool = selectCheckboxCount >= 20000
+      } else {
+        countBool = listState.total >= 20000
+      }
+      if (isPrompt && countBool) {
+        showDataExportDialog.value = isPrompt
+      } else {
+        toDataExportEvent(selectIds)
+      }
+    } else {
+      toDataExportEvent(selectIds)
+    }
+  }
+  // 根据条件获取到导出id, 并跳转到导出付费页
+  async function toDataExportEvent (selectIds) {
+    try {
+      // 判断是否选择过企业。未选择过调用 selectEnt 选择一个默认的企业
+      await defaultSelectEnt()
+      const { area, city, district } = FilterHistoryViewModel2AjaxModel.formatAreaCity(filterFormatParams.value.regionMap)
+      let searchGroup = filterFormatParams.value.searchGroup === 3 ? 1 : filterFormatParams.value.searchGroup
+      const dParams = {
+        scope: area, // 地区省份(数据导出接口用到)
+        area, // 地区省份(保存筛选接口用到)
+        city,
+        district,
+        ...filterFormatParams.value,
+        searchGroup
+      }
+      const params = Object.assign(dParams, {
+        selectIds: selectIds ? selectIds.join(',') : null
+      })
+      const { _id } = await searchIndexDataExport(params)
+      if (!_id) return
+      const link = `/front/dataExport/toCreateOrderPage/${_id}`
+      openOuterLink(link, true)
+    } catch (error) {
+    } finally {
+      exportLoading.value = false
+    }
+  }
+  // 超过2000条确认导出
+  function setExport () {
+    toDataExportEvent()
+  }
+  // 超过2000条是否在提示
+  async function  exportDialogChange(data) {
+    const params = {
+      status: data ? 1 : 0
+    }
+    const { error_code: code } = await ajaxSetDontPromptAgain(params)
+  }
+
+  //携带当前检索跳转数据导出支付页面
+  function toPayDataExport (config) {
+   const { listState, selectCheckboxCount, selectIds, filter } = config
+   filterFormatParams.value = FilterHistoryViewModel2AjaxModel.formatAll(filter)
+    const _expand = filter._expand || {}
+    filterFormatParams.value = Object.assign(filterFormatParams.value, _expand)
+    // 未登录跳转登录
+    if (!isLogin.value) {
+      $bus.$emit('bidding:goLogin')
+      return
+    } else {
+      toDataExportEvent()
+    }
+  }
+
+  return {
+    dataExport,
+    setExport,
+    exportDialogChange,
+    showDataExportDialog,
+    toPayDataExport
+  }
+}

+ 52 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/filter-keywords.js

@@ -0,0 +1,52 @@
+import { ref } from 'vue'
+
+const searchModelOptions = [
+  {
+    label: '精准搜索',
+    value: '0'
+  },
+  {
+    label: '模糊搜索',
+    value: '1'
+  }
+]
+export function useSearchInputKeywordsModel() {
+  const inputKeywordsState = ref({
+    input: '',
+    // 关键词筛选模式
+    searchMode: '0',
+    matchKeys: [],
+    // 附加关键词筛选模式
+    wordsMode: 0,
+    // 附件关键词组
+    additionalWords: [],
+    selectMoreKey: false
+  })
+
+  function getFormatAPIParams() {
+    const params = {
+      keyWords: inputKeywordsState.value.input,
+      searchMode: Number(inputKeywordsState.value.searchMode),
+    }
+
+    if (inputKeywordsState.value.selectMoreKey) {
+      params.additionalWords =
+        inputKeywordsState.value.additionalWords.join(',')
+      params.wordsMode = inputKeywordsState.value.wordsMode
+    }
+
+    return params
+  }
+
+  // 动态更新筛选条件
+  function updateInputKeywordsState (keyObj = {}) {
+    inputKeywordsState.value = Object.assign(inputKeywordsState.value, keyObj)
+  }
+
+  return {
+    inputKeywordsState,
+    searchModelOptions,
+    getFormatAPIParams,
+    updateInputKeywordsState
+  }
+}

+ 215 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/filter.js

@@ -0,0 +1,215 @@
+import { ref, readonly } from 'vue'
+import  {
+  FilterHistoryViewModel2AjaxModel,
+  areaObjTwoToSingle,
+  infoTypeMapFormat
+} from '@/utils'
+
+export function useSearchFilterModel(conf) {
+  const { inBIPropertyIframe, isFree,isInApp, isBidField } = conf
+  const initBase = {
+    // 发布时间
+    publishTime: 'thisyear',
+    // 搜索范围
+    selectType: ['title', 'purchasing'],
+    // 报名截止状态
+    bmjzzt: '0',
+    // 报名截止时间
+    signUpEndTime: '',
+    // 交付地点
+    jfArea: {},
+    // 项目地区
+    projectArea: {},
+    // 领域
+    lingyu: {},
+    // 行业
+    industry: {},
+    // 附件
+    fileExists: '0',
+    // 发布方
+    publisher: '0',
+  }
+
+  // 筛选组件状态
+  const filterBase = ref({
+    // 发布时间
+    publishTime: 'thisyear',
+    // 搜索范围
+    selectType: ['title', 'purchasing'],
+    // 报名截止状态
+    bmjzzt: '0',
+    // 报名截止时间
+    signUpEndTime: '',
+    // 交付地点
+    jfArea: {},
+    // 项目地区
+    projectArea: {},
+    // 领域
+    lingyu: {},
+    // 行业
+    industry: {},
+    // 附件
+    fileExists: '0',
+    // 发布方
+    publisher: '0',
+  })
+  const filterState = ref({})
+  filterState.value = filterBase.value
+ // 搜索接口需要的格式化后的数据
+ function getFormatAPIParams (){
+    return getFormatApiBaseParams()
+ }
+  // 格式化招标采购基础筛选条件
+  function getFormatApiBaseParams () {
+    const { publishTime, bmjzzt, signUpEndTime, industry, publisher, jfArea, projectArea: pArea, lingyu } = filterState.value
+    const { area: deliveryArea, city: deliveryCity, district: deliveryDistrict } = FilterHistoryViewModel2AjaxModel.formatAreaCity(jfArea)
+    const { area: projectArea, city: projectCity, district: projectDistrict } = FilterHistoryViewModel2AjaxModel.formatAreaCity(pArea)
+    const { first: domainFirstType, second: domainSecondType, third: domainThirdType } = FilterHistoryViewModel2AjaxModel.formatLingyu(lingyu)
+    const rPublishTime = publishTime?.indexOf('_') > -1 ? FilterHistoryViewModel2AjaxModel.formatExactTime(publishTime, '-') :  FilterHistoryViewModel2AjaxModel.formatTime(publishTime, true, '-')
+    const rSignUpEndTime = signUpEndTime?.indexOf('_') > -1 ? FilterHistoryViewModel2AjaxModel.formatExactTime(signUpEndTime, '-') :  FilterHistoryViewModel2AjaxModel.formatTime(signUpEndTime, true, '-')
+    const rIndustry = FilterHistoryViewModel2AjaxModel.formatIndustry(industry)
+
+    const params = {
+      publishTime: rPublishTime,
+      selectType: filterState.value.selectType.join(','),
+      deadlineStatus: bmjzzt - 0,
+      deadlineTime: rSignUpEndTime, // 投标截止时间
+      domainFirstType,
+      domainSecondType,
+      domainThirdType,
+      deliveryArea,
+      deliveryCity,
+      deliveryDistrict,
+      projectArea,
+      projectCity,
+      projectDistrict,
+      industry: rIndustry,
+      publisher: publisher - 0,
+      fileExists: filterState.value.fileExists - 0,
+    }
+    return params
+  }
+  // 格式化物业专版的筛选条件
+  function getFormatAPIPropertyParams() {
+    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('-')
+      if(isNaN(arr[0])) {
+        rPublishTime = FilterHistoryViewModel2AjaxModel.formatTime(publishTime, true, '-')
+      } else {
+        rPublishTime = arr[0]/ 1000  + '-' +  arr[1] / 1000
+      }
+    } else {
+      const pTime  = publishTime || 'fiveyear'
+      rPublishTime = FilterHistoryViewModel2AjaxModel.formatTime(pTime, 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 {
+      rExpireTime = (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 ,
+      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 = {}, type) {
+    const {key, value } = keyObj
+    const initParams = JSON.parse(JSON.stringify(initBase))
+    if(keyObj && Object.keys(keyObj).length > 0) {
+      // 重置单个筛选条件
+      if(type === 'resetItem') {
+        // 动态组件重置
+        if(filterState.value._expand[key]) {
+          const expandSearchParams = localStorage.getItem('search_bidding_expandSearchParams')
+          let _expand = {}
+          if(expandSearchParams) {
+            _expand = JSON.parse(expandSearchParams)
+          }
+          filterBase.value[key] = _expand[key]
+          filterBase.value._expand[key] = _expand[key]
+          filterState.value[key] = _expand[key]
+          filterState.value._expand[key] = _expand[key]
+        } else {
+          // 普通组件重置
+          filterBase.value[key] = initParams[key]
+          filterState.value[key] = initParams[key]
+        }
+
+      } else if(type === 'expand') {
+        // 动态拓展筛选条件
+        const _expand = filterState.value._expand || {}
+        let _expandResult = {
+          _expand: {}
+        }
+        if(Object.keys(_expand).length  > 0 && filterState.value[key]) {
+          _expandResult._expand =  Object.assign(_expand, { [key]: value })
+        } else {
+          _expandResult._expand =  {
+            [key]: value
+          }
+        }
+        if(!inBIPropertyIframe) {
+          filterState.value = Object.assign(filterBase.value, filterState.value, _expandResult)
+        }
+      } else {
+        filterBase.value[key] = value
+        filterState.value[key] = value
+      }
+    } else {
+      // 动态获取存储的筛选条件
+      const expandSearchParams = localStorage.getItem('search_bidding_expandSearchParams')
+      let _expand = {}
+      if(expandSearchParams) {
+        _expand = JSON.parse(expandSearchParams)
+      }
+      // 重置所有筛选项
+      filterBase.value = {
+        ...initParams,
+        ..._expand,
+        expand: _expand
+      }
+      //
+      if(!inBIPropertyIframe) {
+        filterState.value =  filterBase.value
+      }
+    }
+
+  }
+
+  return {
+    filterState,
+    filterBase,
+    getFormatAPIParams,
+    getFormatAPIPropertyParams,
+    updateFilterBase
+  }
+}

+ 56 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/join-bid-actions.js

@@ -0,0 +1,56 @@
+import { ref } from 'vue'
+import { getBidIsJoin, joinBidAction } from '@/api/modules/'
+import { showToast } from '@/components/toast'
+import $bus from '@/utils/bus'
+
+export function joinBidActionsModel () {
+  const  BidrenewalDialogRef = ref(null)
+
+  async function getJoinBidInfo(listIds) {
+      const ids = listIds.join()
+      const { error_code: code, data } = await getBidIsJoin({ ids })
+      if (code === 0 && data) {
+        $bus.$emit('bidding:updateListJoinStatus', {joinData: data})
+      }
+  }
+  // 参标
+  async function onJoinBid(item) {
+    // 终止参标
+    if (item.joinBid) {
+      showToast('如需终止参标,需要在详情页进行操作。')
+      return
+    }
+    // 参标
+    const params = {
+      bidIds: item.id
+    }
+    try {
+      const {
+        error_code: code,
+        error_msg: msg,
+        data
+      } = await joinBidAction('in', params)
+      if (code === 0 && data) {
+        // showToast.$toast('已参标,请前往我的参标项目列表查看。')
+        // 拉起参标更新弹窗
+        if(BidrenewalDialogRef.value) {
+          BidrenewalDialogRef.value.passVisible = true
+          BidrenewalDialogRef.value.setid(item.id)
+          BidrenewalDialogRef.value.refreshData()
+        }
+        $bus.$emit('bidding:updateListJoinStatus', { item })
+      } else if (code === -1) {
+        showToast.$toast(msg || '操作错误,请稍后重试')
+      }
+    } catch (e) {
+      console.warn(e)
+      showToast.$toast('操作错误,请稍后重试')
+    }
+  }
+
+  return {
+    BidrenewalDialogRef,
+    getJoinBidInfo,
+    onJoinBid
+  }
+}

+ 101 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/list-header-actions.js

@@ -0,0 +1,101 @@
+import { computed, ref } from 'vue'
+import { ListHeaderActions } from '../../composables/list-header-actions'
+
+// 列表顶部操作按钮
+const actions = [
+  [
+    'refined-list',
+    'detailed-list',
+    // 'table'
+  ],
+  // ['data-export'],
+  // ['collect-bid'],
+  // ['distribute-bid'],
+  // ['employ-bid'],
+  // ['edit-tags'],
+  // ['cancel-collect']
+]
+
+// 列表 Item 样式风格切换,用于兼容旧参数
+const ItemStyleTypes = {
+  'refined-list': 'S',
+  'detailed-list': 'D',
+  table: 'T'
+}
+
+// 权限列表,过滤操作按钮权限
+const limitActions = ref({
+  'refined-list': true,
+  'detailed-list': true,
+  'table': false,
+  'data-export': false,
+  'collect-bid': false,
+  'distribute-bid': false,
+  'employ-bid': false,
+  'edit-tags': false,
+  'cancel-collect': false
+})
+
+// 默认的风格样式
+const defaultItemStyleType = 'refined-list'
+
+export function useSearchListHeaderActionsModel() {
+  // 默认的风格样式
+  const listItemStyleType = ref(defaultItemStyleType)
+
+  // 当前激活的风格样式
+  const activeItemStyleType = computed(() => {
+    return ItemStyleTypes[listItemStyleType.value]
+  })
+
+  // 需要高亮 active 的顶部操作按钮
+  const activeHeaderActions = ref([defaultItemStyleType])
+
+  // 需要禁用的 disable 的顶部操作按钮
+  const disabledHeaderActions = ref([])
+
+  // 顶部操作按钮图标等配置填充
+  const headerActions = computed(() => {
+    if (actions.length > 0) {
+      // 过滤权限
+      const authActions = actions.map(acItem => {
+        if (Array.isArray(acItem)) {
+          const arr = acItem.filter(flagKey => {
+            return flagKey && limitActions.value[flagKey]
+          })
+          return  arr
+        }
+        return []
+      }).filter(item => {
+        return item.length > 0
+      })
+
+      // 变更actions状态字段
+      const updateAction =  authActions.map((actions) => {
+        if (Array.isArray(actions)) {
+          return actions.map((actionKey) => {
+            const result = ListHeaderActions[actionKey]
+            // 对应高亮上方 activeHeaderActions 中的按钮
+            result.isActive = activeHeaderActions.value.includes(actionKey)
+            // 禁用某指定操作
+            result.disabled = disabledHeaderActions.value.includes(actionKey)
+            return result
+          })
+        }
+        return []
+      })
+      return updateAction
+    } else {
+      return []
+    }
+  })
+
+  return {
+    headerActions,
+    listItemStyleType,
+    activeItemStyleType,
+    activeHeaderActions,
+    disabledHeaderActions,
+    limitActions
+  }
+}

+ 306 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/recommend-card.js

@@ -0,0 +1,306 @@
+import { computed, reactive, ref } from 'vue'
+import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
+import { difference } from 'lodash'
+import { ajaxSetLeadGetDateRecord, leadGetDate } from '@/api/modules/'
+import {
+  formatPrice,
+  replaceKeyword
+} from '@/utils/'
+
+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-orange-bd">',
+      '</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
+  }
+}

+ 178 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/save-filter-actions.js

@@ -0,0 +1,178 @@
+import { computed, ref } from 'vue'
+import { checkBiddingFilterPass, addBiddingFilter, getBiddingFilterList, deleteBiddingFilter } from '@/api/modules'
+import  { FilterHistoryViewModel2AjaxModel, FilterHistoryAjaxModel2ViewModel} from '@/utils'
+import { showToast } from '@/components/toast'
+import { MessageBox } from 'element-ui'
+
+
+const saveConfig = {
+  savedFilterListMaxCount: 10 //筛选条件保存条数最大值
+}
+
+// P611医械通:要处理参数 bidField: isBidField ? 'medical' : '',
+
+export function saveFilterActionsModel (conf) {
+   const { savedFilterListMaxCount: maxCount } = saveConfig
+  // 是否是医械通
+  const { isBidField }  = conf
+   // 已存筛选列表
+   let filterHistoryList = ref([])
+   // 保存筛选条件框展示与否
+   const saveFilterDialogVisible = ref(false)
+  // 查看已保存的历史筛选条件
+   const historyFilterDialogVisible = ref(false)
+   // 当前保存的筛选条件多返回的ID
+   const currentFilterParamsKey = ref('')
+  // 当前保存的筛选条件
+   let filterSaveParams = ref({})
+  // 视图可视筛选条件
+   let viewFilterParams = ref({})
+  // 已存搜索条件个数
+   const historyFilterCount = computed(() => {
+     return filterHistoryList.value.length
+   })
+
+  // 检测筛选条件是否可保存
+   async function checkFilterPass (config) {
+     const  { filter } = config
+     filterSaveParams.value = FilterHistoryViewModel2AjaxModel.formatAll(filter)
+
+     const { buyer, winner, agency, searchvalue, additionalWords, industry } = filterSaveParams.value
+     const hasOneKey = buyer || winner || agency
+     if (!searchvalue && !additionalWords && !industry && !hasOneKey) {
+       return showToast('请先输入关键词')
+     }
+     if(historyFilterCount.value > maxCount) {
+       return showToast(`对不起,最多可保存${maxCount}个筛选条件。`)
+     }
+     const { data: id, error_code: code = 0, error_msg: msg } = await checkBiddingFilterPass(filterSaveParams.value)
+     if(code === 0) {
+       currentFilterParamsKey.value = id
+       viewFilterParams.value = FilterHistoryAjaxModel2ViewModel.formatAll(filterSaveParams.value)
+       saveFilterDialogVisible.value = true
+     } else {
+      if (msg) {
+        if (msg.includes('无用户身份')) {
+          showToast('请登录')
+        } else {
+          showToast(msg)
+        }
+      }
+    }
+  }
+
+  // 取消保存筛选条件操作
+  function saveFilterCancel () {
+    saveFilterDialogVisible.value = false
+  }
+
+  // 确定保存筛选条件操作
+  async function saveFilterConfirm () {
+    try {
+      const params = {
+        ...filterSaveParams.value,
+        inkey: currentFilterParamsKey.value
+      }
+      const { error_code: code = 0, error_msg: msg } = await addBiddingFilter(params)
+      if (code === 0) {
+        saveFilterDialogVisible.value = false
+        showToast('保存成功')
+        getFilterHistoryList()
+      } else {
+        if (msg) {
+          showToast(msg)
+        }
+      }
+    } catch (error) {
+    }
+  }
+  // 获取已存筛选条件
+  async function  getFilterHistoryList(type, callback) {
+    try {
+        const par = {
+          bidField: isBidField ? 'medical' : ''
+        }
+        const { data, error_code: code, error_msg: msg } = await getBiddingFilterList(par)
+      if (code === 0) {
+        const resData = data || []
+        const arr = resData.map((item, index) => {
+          return {
+            ...FilterHistoryAjaxModel2ViewModel.formatAll(item),
+            id: item.id,
+            inKey: item.inKey,
+            isPay: item.isPay,
+            open: index === 0
+          }
+        })
+        filterHistoryList.value = arr
+        if(type === 'delete' && arr.length === 0) {
+          historyFilterDialogVisible.value = false
+        }
+        callback && callback(arr)
+      } else {
+        if (msg) {
+          showToast(msg)
+        }
+      }
+    } catch (error) {
+    }
+  }
+
+  /**
+   * 获取已保存的筛选条件
+   * @returns {Promise<void>}
+   */
+  async function onHasFilter () {
+    if(!historyFilterCount.value) {
+      showToast('请先保存筛选条件')
+      return
+    }
+    historyFilterDialogVisible.value = true
+  }
+  // 已存筛选条件弹窗,单条展开收起
+  function onHasToggle (item) {
+    filterHistoryList.value.forEach(function(v) {
+      v.open = false
+    })
+    item.open = !item.open
+  }
+
+  function onDeleteFilter (item) {
+    MessageBox.confirm('确定删除该筛选条件吗?', '删除', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      center: true,
+      showClose: false,
+      customClass: 'filter-delete-messagebox',
+      confirmButtonClass: 'btn-group confirm-btn',
+      cancelButtonClass: 'btn-group cancel-btn'
+    }).then(function() {
+      const params = {
+        id: item.id
+      }
+      deleteBiddingFilter(params).then(res => {
+        const { error_code: code} = res
+        if(code === 0) {
+          showToast('删除成功')
+          getFilterHistoryList('delete')
+        }
+      })
+    }).catch(function() {})
+  }
+
+  return {
+    saveFilterDialogVisible,
+    historyFilterDialogVisible,
+    filterSaveParams,
+    viewFilterParams,
+    historyFilterCount,
+    checkFilterPass,
+    saveFilterCancel,
+    saveFilterConfirm,
+    filterHistoryList,
+    getFilterHistoryList,
+    onHasFilter,
+    onHasToggle,
+    onDeleteFilter
+  }
+}

+ 80 - 0
apps/bigmember_pc/src/views/search/sun/model/modules/tabs.js

@@ -0,0 +1,80 @@
+import { computed, ref } from 'vue'
+
+export function useSearchTabsModel(conf) {
+  const { showTabs2, inInjectBI } = conf
+  const defaultTab = showTabs2 ? 7 : 0
+  const activeTab = ref(defaultTab)
+  const Tabs = [
+    {
+      label: '采购信息',
+      key: 0
+    }
+  ]
+  const Tabs2 = [
+    {
+      label: '招标采购公告',
+      key: 1,
+      link: '/jylab/supsearch/index.html?searchGroup=1'
+    },
+    {
+      label: '企业搜索',
+      key: 3,
+      link: '/jylab/entSearch/index.html'
+    },
+    {
+      label: '采购单位搜索',
+      key: 4,
+      link: '/jylab/purSearch/index.html'
+    },
+    {
+      label: '阳光直采',
+      key: 7,
+      link: '/swordfish/page_big_pc/search/sun'
+    },
+    {
+      label: '供应搜索',
+      key: 5,
+      link: '/swordfish/page_big_pc/search/supply'
+    },
+    {
+      label: '超前项目查询',
+      key: 2,
+      link: '/jylab/supsearch/index.html?searchGroup=2'
+    },
+    {
+      label: '拟在建项目查询',
+      key: 6,
+      link: '/swordfish/page_big_pc/search/nzj'
+    }
+  ]
+  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
+      return v
+    })
+  })
+
+  function doChangeTab(tab) {
+    activeTab.value = tab.key
+  }
+  return {
+    searchTabs,
+    activeTab,
+    doChangeTab
+  }
+}

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

@@ -62,6 +62,8 @@ import MyEquityList from './components/MyEquityList.vue'
 const BusinessProfile = () => import('./components/BusinessProfile.vue')
 // const MyCustomer = () => import('./components/MyCustomer.vue')
 // const CustomerWatcher = () => import('./components/CustomerWatcher.vue')
+import { showFunctionGuide } from '@/api/modules/'
+import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
 export default {
   name: 'WorkspaceDashboard',
   components: {
@@ -94,7 +96,8 @@ export default {
     return {
       dialog: {
         checkUserShow: false
-      }
+      },
+      showSunshineGuide: false
     }
   },
   computed: {
@@ -128,11 +131,51 @@ export default {
       'customerWatcherShow',
       'hasMemberNJPower',
       'dataReportShow'
-    ]),
+    ])
+  },
+  mounted() {
+    tryCallHooks({
+      fn: () => {
+        this.$BRACE.$on('close-guide', (data) => {
+          // 关闭引导
+          console.log(data, 'data')
+          this.handleFunctionGuide('know')
+        })
+      },
+      spareFn: () => {
+        console.log('err')
+      }
+    })
   },
   methods: {
     initNextDialog(key) {
       this.dialog[key] = true
+      // 获取引导
+      this.handleFunctionGuide()
+    },
+    async handleFunctionGuide(know) {
+      const params = {
+        tipName: 'sunshine_guide',
+        doType: know || ''
+      }
+      const { error_code: code, data } = await showFunctionGuide(params)
+      if (code === 0) {
+        const tipTimes = data || 0
+        this.showSunshineGuide = tipTimes < 2
+        if (know && data === 1) {
+          this.showSunshineGuide = false
+        }
+        try {
+          window.$BRACE.$emit('menu-guide', {
+            name: '直采',
+            show: this.showSunshineGuide,
+            title: '阳光直采上线了!',
+            content: '海量企业直发采购需求,供应商可直接对接采购部门'
+          })
+        } catch (error) {
+          console.log(error)
+        }
+      }
     }
   }
 }

+ 1 - 1
apps/jy-pc/index.html

@@ -29,7 +29,7 @@
   <% } %>
 
   <!-- 使用CDN的CSS文件 -->
-      <link rel="stylesheet" href="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/24.9.5/iconfont.css">
+      <link rel="stylesheet" href="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/24.11.7/iconfont.css">
 
       <!-- 使用CDN的CSS文件 -->
       <% for (var i in cdn && cdn.css) { %>

BIN
apps/jy-pc/src/assets/image/purchase/vip.png


+ 24 - 24
apps/jy-pc/src/assets/js/china_area.js

@@ -535,7 +535,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 5,
-    name: '内蒙古自治区',
+    name: '内蒙古',
     Initials: 'N',
     ProSort: 32,
     ProRemark: '自治区',
@@ -3531,7 +3531,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 21,
-    name: '广西壮族自治区',
+    name: '广西',
     Initials: 'G',
     ProSort: 28,
     ProRemark: '自治区',
@@ -4103,7 +4103,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 24,
-    name: '新疆维吾尔自治区',
+    name: '新疆',
     Initials: 'X',
     ProSort: 31,
     ProRemark: '自治区',
@@ -4453,7 +4453,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 26,
-    name: '宁夏回族自治区',
+    name: '宁夏',
     Initials: 'N',
     ProSort: 30,
     ProRemark: '自治区',
@@ -4745,7 +4745,7 @@ const chinaMapJSON = [
           '木里藏族自治县',
           '盐源县',
           '德昌县',
-          '会理',
+          '会理',
           '会东县',
           '宁南县',
           '普格县',
@@ -5259,7 +5259,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 31,
-    name: '西藏自治区',
+    name: '西藏',
     Initials: 'X',
     ProSort: 29,
     ProRemark: '自治区',
@@ -5438,7 +5438,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 33,
-    name: '澳门特别行政区',
+    name: '澳门',
     Initials: 'A',
     ProSort: 33,
     ProRemark: '特别行政区',
@@ -5464,7 +5464,7 @@ const chinaMapJSON = [
   },
   {
     ProID: 34,
-    name: '香港特别行政区',
+    name: '香港',
     Initials: 'X',
     ProSort: 34,
     ProRemark: '特别行政区',
@@ -5498,22 +5498,22 @@ const chinaMapJSON = [
       }
     ]
   },
-  {
-    ProID: 35,
-    name: '钓鱼岛',
-    Initials: 'D',
-    ProSort: 35,
-    ProRemark: '特别行政区',
-    city: [
-      {
-        CityID: 372,
-        name: '钓鱼岛',
-        ProID: 35,
-        CitySort: 372,
-        area: ['钓鱼岛']
-      }
-    ]
-  }
+  // {
+  //   ProID: 35,
+  //   name: '钓鱼岛',
+  //   Initials: 'D',
+  //   ProSort: 35,
+  //   ProRemark: '特别行政区',
+  //   city: [
+  //     {
+  //       CityID: 372,
+  //       name: '钓鱼岛',
+  //       ProID: 35,
+  //       CitySort: 372,
+  //       area: ['钓鱼岛']
+  //     }
+  //   ]
+  // }
   // , {
   //   ProID: 36,
   //   name: ' 海外',

+ 37 - 0
apps/jy-pc/src/router/modules/purchase.js

@@ -0,0 +1,37 @@
+export default [
+  {
+    path: '/',
+    name: 'index',
+    component: () => import('@/views/purchase/Index.vue'),
+    children: [
+      {
+        path: '',
+        redirect: 'info'
+      },
+      {
+        path: 'info',
+        name: 'info',
+        component: () => import('@/views/purchase/info/InfoPurchase.vue'),
+        meta: {
+          title: '采购发布'
+        }
+      },
+      {
+        path: 'my',
+        name: 'my',
+        component: () => import('@/views/purchase/my/MyPurchase.vue'),
+        meta: {
+          title: '发布管理'
+        }
+      }
+    ]
+  },
+  {
+    path: '/detail/:id',
+    name: 'purchase-detail',
+    component: () => import('@/views/purchase/Detail.vue'),
+    meta: {
+      title: '发布详情'
+    }
+  }
+]

+ 64 - 1
apps/jy-pc/src/views/issued/components/IssuedForm.vue

@@ -342,7 +342,22 @@
         </div>
       </div>
     </el-form>
-
+    <el-dialog
+      @close="purchaseCancel"
+      title="阳光直采功能升级"
+      :visible.sync="showPurchaseDialog"
+      width="380px"
+      class="puschase-dialog"
+      center
+    >
+      <div class="dialog">
+        采购信息发布升级到“采购发布”内,采购发布成功<br />后,可收到至少5家供应商参与报价
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="goJumpRelease">跳转发布</el-button>
+        <el-button @click="purchaseCancel">我再想想</el-button>
+      </span>
+    </el-dialog>
     <el-dialog
       title="提交失败"
       :visible.sync="showSubfailDialog"
@@ -450,6 +465,7 @@ export default {
       noticeData: [],
       showNoticeContent: false,
       showSubfailDialog: false,
+      showPurchaseDialog: false,
       isPublic: true,
       selectNotice: {},
       ruleForm: {
@@ -599,6 +615,8 @@ export default {
       if (val === '3' || val === 3) {
         this.showSupply = true
         this.isPublic = true
+      } else if (val === '2' || val === 2) {
+        this.showPurchaseDialog = true
       } else {
         this.showSupply = false
         this.getIndustryShowMore()
@@ -865,6 +883,22 @@ export default {
         }
       }
     },
+    // 跳转发布
+    goJumpRelease() {
+      // const routeData = this.$router.resolve({
+      //   path: '/purchase/info'
+      // })
+      // openSelfLink(routeData)
+      const gohref = location.origin + '/swordfish/page_web_pc/purchase/info'
+      window.open(
+        '/page_workDesktop/work-bench/page?link=' + encodeURIComponent(gohref)
+      )
+    },
+    // 我再想想
+    purchaseCancel() {
+      this.showPurchaseDialog = false
+      this.ruleForm.msgType = '1'
+    },
     // 复制操作
     copyText() {
       const text = '项目发布媒体:剑鱼标讯(https://www.jianyu360.cn/)'
@@ -1318,6 +1352,35 @@ body {
     font-size: 14px;
     line-height: 22px;
   }
+  .puschase-dialog {
+    ::v-deep {
+      .el-dialog {
+        border-radius: 8px;
+      }
+      .el-dialog__header {
+        padding: 32px 32px 0;
+      }
+      .el-dialog__body {
+        padding: 20px 32px 32px;
+        text-align: center;
+      }
+      .el-dialog__footer {
+        padding: 0 32px 32px;
+        .dialog-footer {
+          display: flex;
+          justify-content: space-between;
+          button {
+            width: 132px;
+          }
+        }
+      }
+    }
+    .el-form-item.is-required:not(.is-no-asterisk)
+      .el-form-item__label-wrap
+      > .el-form-item__label:before {
+      margin-right: 0;
+    }
+  }
 }
 </style>
 <style scoped>

+ 487 - 0
apps/jy-pc/src/views/purchase/Detail.vue

@@ -0,0 +1,487 @@
+<template>
+  <div class="issued-detail v-w1200" v-loading="loading">
+    <div class="detail-breadcrumb">
+      <el-breadcrumb separator-class="el-icon-arrow-right">
+        <el-breadcrumb-item @click="goMyIssuedPage"
+          >发布管理</el-breadcrumb-item
+        >
+        <el-breadcrumb-item>发布详情</el-breadcrumb-item>
+      </el-breadcrumb>
+    </div>
+    <div class="detail-container">
+      <div class="d-main">
+        <div class="info-title">
+          <span class="title-status" :class="getClass(info.reviewStatus)">{{
+            getStatus(info.reviewStatus)
+          }}</span>
+          <h3 class="title-h">{{ info.title }}</h3>
+        </div>
+        <div class="info-content">
+          <div class="c-item">
+            <div class="item-label">信息类型:</div>
+            <div class="item-value">{{ getType(info.msgType) }}</div>
+          </div>
+          <div class="c-item" v-if="info.msgType != 5 && info.msgType != 3">
+            <div class="item-label">项目编号:</div>
+            <div class="item-value">{{ info.code || '--' }}</div>
+          </div>
+          <div class="c-item" v-if="info.msgType != 5 && info.msgType != 3">
+            <div class="item-label">交付地点:</div>
+            <div class="item-value">{{ info.deliveryAddress || '--' }}</div>
+          </div>
+          <div class="c-item" v-if="info.msgType != 3">
+            <div class="item-label">项目地区:</div>
+            <div class="item-value">{{ info.province }} {{ info.city }}</div>
+          </div>
+          <!-- <div class="c-item" v-if="info.industry && info.industry.length > 0">
+            <div class="item-label">项目行业:</div>
+            <div class="item-value" v-if="info.industry">{{info.industry.join(',')}}</div>
+            <div class="item-value" v-else>--</div>
+          </div> -->
+          <div class="c-item" v-if="info.msgType != 3">
+            <div class="item-label">采购单位:</div>
+            <div class="item-value">{{ info.buyer || '--' }}</div>
+          </div>
+          <div class="c-item" v-if="info.msgType != 5 && info.msgType != 3">
+            <div class="item-label">预算金额:</div>
+            <div class="item-value" v-if="info.budget === '0' || info.budget">
+              {{ (info.budget / 10000).fixed(2) }}万元
+            </div>
+            <div class="item-value" v-else>--</div>
+          </div>
+          <div class="c-item">
+            <div class="item-label">信息正文:</div>
+            <div class="item-value" v-html="info.detail"></div>
+          </div>
+          <div class="c-item" v-if="info.msgType == 3">
+            <div class="item-label">有效期至:</div>
+            <div class="item-value">{{ info.validityTime || '--' }}</div>
+          </div>
+          <div class="c-item">
+            <div class="item-label">附件:</div>
+            <div
+              class="item-value"
+              v-if="info.attach && Object.keys(info.attach).length > 0"
+            >
+              <div v-for="(at, i) in info.attach" :key="i">
+                <a
+                  class="href"
+                  :href="
+                    at.ossurl +
+                    '/' +
+                    at.fid +
+                    '?response-content-type=application/octet-stream'
+                  "
+                  download
+                  target="_blank"
+                  >{{ at.filename }}</a
+                >
+              </div>
+            </div>
+            <div v-else class="item-value">--</div>
+          </div>
+        </div>
+        <div class="info-concat">
+          <!-- <h3 class="concat-title" v-if="info.msgType == 3">联系人信息</h3> -->
+          <div class="concat-title">
+            <span>联系人信息</span>
+            <div class="group-title-tip contact-tip">
+              <span>将作为“采购单位联系方式”进行公开</span>
+            </div>
+          </div>
+          <!-- <h3 class="concat-title" v-else>采购联系人信息</h3> -->
+          <div>
+            <div class="c-item">
+              <div class="item-label">联系人:</div>
+              <div class="item-value">{{ info.contactName || '--' }}</div>
+            </div>
+            <div class="c-item">
+              <div class="item-label">联系电话:</div>
+              <div class="item-value">{{ info.contactPhone || '--' }}</div>
+            </div>
+            <!-- <p
+              class="concat-tips"
+              v-if="info.contactOvert && info.contactOvert != 0"
+            >
+              我<span v-if="info.contactOvert == 1">同意</span
+              ><span v-else>不同意</span>公开联系方式
+            </p> -->
+            <div
+              class="c-item agency-item"
+              v-show="getMsgType && info.recommendedService"
+            >
+              <div class="item-label item-tips">供应商推荐服务:</div>
+              <div class="item-value item-tips">
+                {{ info.recommendedService === 1 ? '我需要' : '我不需要' }}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="d-footer">
+        <div class="c-item">
+          <div class="item-label">申请日期:</div>
+          <div class="item-value">{{ info.createTime || '--' }}</div>
+        </div>
+        <div class="c-item">
+          <div class="item-label">申请状态:</div>
+          <div
+            class="item-value"
+            :class="[
+              info.reviewStatus == 1 ? 'color-yellow' : '',
+              info.reviewStatus == 3 ? 'color-red' : ''
+            ]"
+          >
+            {{ getStatus(info.reviewStatus) }}
+          </div>
+        </div>
+        <div class="c-item" v-if="info.reviewStatus == 3">
+          <div class="item-label">原因:</div>
+          <div class="item-value">{{ info.reviewDetail || '--' }}</div>
+        </div>
+        <div class="c-item" style="margin: 0" v-if="info.reviewTime">
+          <div class="item-label">审核时间:</div>
+          <div class="item-value">{{ info.reviewTime }}</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { myPublishDetail } from '@/api/modules/jyinfo'
+import { moneyUnit, dateFormatter, openSelfLink } from '@/utils/'
+export default {
+  name: 'issued-detail',
+  data() {
+    return {
+      loading: true,
+      info: {}
+    }
+  },
+  created() {
+    this.getDetail()
+  },
+  computed: {
+    getMsgType() {
+      // 采购意向、招标预告、招标公告、采购信息显示供应商推荐
+      const msg = Number(this.$route.query.msgType)
+      return msg === 8
+    }
+  },
+  methods: {
+    moneyUnit,
+    dateFormatter,
+    goMyIssuedPage() {
+      openSelfLink(
+        {
+          link: '/issued/my'
+        },
+        true
+      )
+    },
+    getClass(id) {
+      if (!id) return
+      let oClass = ''
+      id = Number(id)
+      switch (id) {
+        case 1:
+          oClass = 'warn'
+          break
+        case 2:
+          oClass = 'success'
+          break
+        case 3:
+          oClass = 'error'
+          break
+      }
+      return oClass
+    },
+    getStatus(id) {
+      let oName = ''
+      id = Number(id)
+      switch (id) {
+        case 1:
+          oName = '审核中'
+          break
+        case 2:
+          oName = '审核通过'
+          break
+        case 3:
+          oName = '审核不通过'
+          break
+      }
+      return oName
+    },
+    getType(type) {
+      if (!type) return
+      let name = ''
+      switch (type) {
+        case 1:
+          name = '招标信息'
+          break
+        case 2:
+          name = '采购信息'
+          break
+        case 3:
+          name = '供应信息'
+          break
+        case 4:
+          name = '招标公告'
+          break
+        case 5:
+          name = '采购意向'
+          break
+        case 6:
+          name = '招标预告'
+          break
+        case 7:
+          name = '招标结果'
+          break
+        case 8:
+          name = '采购信息'
+          break
+      }
+      return name
+    },
+    async getDetail() {
+      const params = {
+        msgId: this.$route.params.id,
+        type: 0,
+        msgType: Number(this.$route.query.msgType)
+      }
+      const {
+        error_msg: msg,
+        data,
+        err_msg: errMsg
+      } = await myPublishDetail(params)
+      if (
+        (errMsg && errMsg.indexOf('未认证') > -1) ||
+        (msg && msg.indexOf('未认证') > -1)
+      ) {
+        this.$router.replace('/issued/info')
+        return
+      }
+      if (data) {
+        this.loading = false
+        const {
+          title,
+          msgType,
+          province,
+          city,
+          industry,
+          detail,
+          code,
+          buyer,
+          budget,
+          InfoDetailReview,
+          infoDetailContact,
+          attach,
+          publishTime,
+          validityTime,
+          createTime,
+          deliveryAddress,
+          recommendedService
+        } = data
+        if (industry) {
+          industry.forEach((v, i) => {
+            if (!v) {
+              data.industry.splice(i, 1)
+            }
+          })
+        }
+        if (attach) {
+          this.info.attach = JSON.parse(attach)
+        }
+        this.info.title = title
+        this.info.msgType = msgType
+        this.info.province = province
+        this.info.city = city
+        this.info.detail = detail.replaceAll('<br/>', '\n')
+        this.info.code = code
+        this.info.buyer = buyer
+        this.info.budget = budget
+        this.info.deliveryAddress = deliveryAddress
+        this.info.recommendedService = recommendedService
+        this.info.publishTime = this.dateFormatter(
+          new Date(publishTime?.replaceAll('-', '/')).getTime(),
+          'yyyy-MM-dd'
+        )
+        this.info.createTime = this.dateFormatter(
+          new Date(createTime?.replaceAll('-', '/')).getTime(),
+          'yyyy-MM-dd'
+        )
+        this.info.validityTime = this.dateFormatter(
+          new Date(validityTime?.replaceAll('-', '/')).getTime(),
+          'yyyy-MM-dd'
+        )
+        this.info.reviewStatus = InfoDetailReview?.status
+        this.info.reviewDetail = InfoDetailReview?.detail
+        this.info.reviewTime = this.dateFormatter(
+          new Date(InfoDetailReview?.time?.replaceAll('-', '/')).getTime(),
+          'yyyy-MM-dd'
+        )
+        this.info.contactName = infoDetailContact?.name
+        this.info.contactPhone = infoDetailContact?.phone
+        this.info.contactOvert = infoDetailContact?.overt
+      } else {
+        this.$toast(msg || errMsg)
+      }
+    },
+    openRouter(id) {
+      const routeData = this.$router.resolve({
+        path: '/issued/detail/' + id,
+        query: {
+          msgType: this.info.msgType
+        }
+      })
+      openSelfLink(routeData)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.in-app {
+  .detail-container {
+    @extend .sub-card-container;
+    background-color: transparent;
+    overflow: hidden;
+  }
+}
+.issued-detail {
+  padding: 36px 0 47px;
+  .detail-container {
+    margin-top: 20px;
+    .d-main {
+      padding: 33px 40px;
+      background: #fff;
+    }
+    .d-footer {
+      margin-top: 20px;
+      padding: 40px;
+      background: #fff;
+    }
+    .info-title {
+      display: flex;
+      align-items: center;
+      padding-bottom: 27px;
+      border-bottom: 1px solid #e0e0e0;
+      .title-status {
+        min-width: 58px;
+        margin-right: 10px;
+        padding: 3px 8px;
+        font-size: 14px;
+        line-height: 22px;
+        border-radius: 4px;
+      }
+      .title-h {
+        font-size: 18px;
+        color: #1d1d1d;
+        font-weight: bold;
+      }
+    }
+    .info-content {
+      margin: 20px 0;
+    }
+    .info-concat {
+      .concat-title {
+        display: flex;
+        align-items: center;
+        margin-bottom: 10px;
+        font-size: 14px;
+        color: #1d1d1d;
+        line-height: 40px;
+        font-weight: bold;
+        .contact-tip {
+          margin-left: 20px;
+          padding: 2px 8px;
+          border-radius: 4px;
+          background: #f7f9fc;
+          line-height: 24px;
+          & > span {
+            color: #2abed1;
+            font-weight: normal;
+          }
+        }
+      }
+      .concat-tips {
+        margin-top: 3px;
+        font-size: 14px;
+        color: #888888;
+        line-height: 22px;
+      }
+    }
+    .c-item {
+      display: flex;
+      margin-bottom: 10px;
+      font-size: 14px;
+      &.agency-item {
+        margin-top: 10px;
+      }
+      .item-label {
+        min-width: 80px;
+        color: #686868;
+        line-height: 22px;
+        &.item-tips {
+          color: #888888;
+        }
+      }
+      .item-value {
+        line-height: 22px;
+        flex: 1;
+        color: #1d1d1d;
+        &.item-tips {
+          color: #888888;
+        }
+      }
+      .href {
+        color: $color_main;
+        text-decoration: underline;
+        cursor: pointer;
+        font-size: 14px;
+      }
+      &.c-item-agreetext {
+        font-size: 14px;
+        font-weight: 400;
+        color: #2cb7ca;
+      }
+    }
+    .color-yellow {
+      color: $color_yellow !important;
+    }
+    .color-red {
+      color: $color_red !important;
+    }
+    .success {
+      background: rgba(11, 217, 145, 0.1);
+      color: #0bd991;
+    }
+    .error {
+      background: rgba(255, 58, 32, 0.1);
+      color: #ff3a20;
+    }
+    .warn {
+      background: rgba(255, 159, 64, 0.1);
+      color: #ff9f40;
+    }
+  }
+  ::v-deep {
+    .el-breadcrumb__inner.is-link {
+      color: #686868;
+      font-weight: normal;
+      &:hover {
+        color: $color_main;
+      }
+    }
+    .el-breadcrumb__item:last-child {
+      .el-breadcrumb__inner {
+        color: $color_main;
+      }
+    }
+    .el-breadcrumb__separator[class*='icon'] {
+      margin: 0;
+    }
+  }
+}
+</style>

+ 396 - 0
apps/jy-pc/src/views/purchase/Index.vue

@@ -0,0 +1,396 @@
+<template>
+  <div class="issued-index v-w1200" v-loading="loading">
+    <div>
+      <div class="issued-nav">
+        <div class="tab-link">
+          <router-link
+            class="link-to"
+            active-class="active-class"
+            to="/purchase/info"
+            replace
+            >采购发布</router-link
+          >
+          <router-link
+            class="link-to"
+            active-class="active-class"
+            to="/purchase/my"
+            replace
+            >发布管理</router-link
+          >
+        </div>
+      </div>
+      <router-view v-if="!loading" :entInfo="entInfo"></router-view>
+    </div>
+    <div>
+      <IssuedDialog
+        ref="dialogRef"
+        title="信息发布"
+        top="30vh"
+        :showDialog="dialog.showDialog"
+        :showCancelBtn="dialog.showCancelBtn"
+        :confirmBtnText="dialog.confirmBtnText"
+        cancelBtnText="取消"
+        showDialogLabel
+        @onConfirm="onConfirmDialogBtn"
+        @onCancel="onCancelDialogBtn"
+      >
+        <div slot="content">
+          <div name="content" v-html="dialog.content"></div>
+        </div>
+        <div slot="label">
+          <slot name="label"
+            >如有问题,可拨打客服电话 400-108-6670 或在线
+            <em class="light-text open-customer" @click="openCustomer"
+              >联系客服</em
+            ></slot
+          >
+        </div>
+      </IssuedDialog>
+    </div>
+  </div>
+</template>
+
+<script>
+// import SwitchEnt from '@/views/issued/components/SwitchEnt' // p278移除企业切换
+import {
+  getMyEntInfo,
+  getEntInfo,
+  getEntList,
+  selectEnt,
+  getBoundPhone,
+  selectedDefaultEnt
+} from '@/api/modules/entbase'
+import { getEntCheckStatus } from '@/api/modules/authen'
+import { identityList } from '@/api/modules/publicapply'
+import IssuedDialog from '@/components/dialog/Dialog'
+import { mapState } from 'vuex'
+import { openSelfLink } from '@/utils'
+export default {
+  name: 'issued-index',
+  components: {
+    // SwitchEnt,
+    IssuedDialog
+  },
+  data() {
+    return {
+      // 是否限免(前期由前端控制是否限免,后期改成接口控制)
+      isLimited: true,
+      // 是否加载信息发布页面
+      isLoad: false,
+      loading: true,
+      entInfo: {
+        ent_name: '',
+        ent_id: '',
+        ent_auth_status: false,
+        user_id: '',
+        authStatus: 0, // 企业是否认证 -1 未通过,0 未认证,1 已认证. -2 已到期  3待审核
+        freezeStatus: 0, // 冻结状态 0:正常 1:冻结
+        boundPhone: '',
+        province: '',
+        city: ''
+      },
+      entList: [],
+      dialog: {
+        showDialog: false,
+        content: '',
+        showCancelBtn: false,
+        confirmBtnText: '',
+        cancelBtnText: '',
+        label: ''
+      }
+    }
+  },
+  inject: ['reload'],
+  computed: {
+    ...mapState({
+      userInfo: (state) => state.user.info
+    })
+  },
+  created() {
+    this.getBoundPhoneFn()
+  },
+  methods: {
+    // 判断有无上一页
+    actionHistory() {
+      openSelfLink(
+        {
+          link: '/page_workDesktop/',
+          prefix: ''
+        },
+        true
+      )
+      // @date 2022-08-30 工作桌面版本调整为全部前往工作台首页
+      // const len = history.length
+      // const referrer = document.referrer
+      // // 判断有无上一页,再判断上一页是否是登录页
+      // if (len > 1) {
+      //   if (!referrer || referrer.indexOf('/notin/page') > -1) {
+      //     location.href = '/'
+      //   } else {
+      //     window.history.back()
+      //   }
+      // } else {
+      //   // window.open('about:blank', '_top').close()
+      //   location.href = '/'
+      // }
+    },
+    // dialog确定按钮触发事件
+    onConfirmDialogBtn(data) {
+      this.dialog.showDialog = false
+      if (data === '我知道了') {
+        this.actionHistory()
+      } else if (data.indexOf('认证') > -1) {
+        openSelfLink(
+          {
+            link: '/authen/index'
+          },
+          true
+        )
+      } else if (data.indexOf('去开通') > -1) {
+        openSelfLink(
+          {
+            link: '/page_big_pc/free/svip/buy',
+            prefix: ''
+          },
+          true
+        )
+      }
+    },
+    // dialog取消按钮触发事件
+    onCancelDialogBtn() {
+      this.dialog.showDialog = false
+      this.actionHistory()
+      // this.dialog.showDialog = false
+    },
+    // dialog组件参数
+    setDialogProps(status) {
+      this.dialog.showDialog = true
+      switch (status) {
+        case 0:
+          this.dialog.confirmBtnText = '前往认证'
+          this.dialog.showCancelBtn = true
+          this.dialog.content =
+            '按照国家相关法律规定,在互联网平台发布信息需要经过实名认证,请您前往完成认证后,再进行信息发布。'
+          break
+        case -1:
+          this.dialog.confirmBtnText = '重新认证'
+          this.dialog.showCancelBtn = true
+          this.dialog.content =
+            '您发起的机构认证“审核不通过”,无法进行信息发布。可修改机构认证信息,重新发起认证。'
+          break
+        case -2:
+          this.dialog.confirmBtnText = '重新认证'
+          this.dialog.showCancelBtn = true
+          this.dialog.content =
+            '您发起的机构认证“已过期”,请重新发起机构认证后,再进行信息发布。'
+          break
+        case 3:
+          this.dialog.confirmBtnText = '我知道了'
+          this.dialog.showCancelBtn = false
+          this.dialog.content =
+            '您发起的机构认证正在“审核中”,请耐心等待,<br>审核通过后即可进行信息发布。'
+          break
+        case 4:
+          this.dialog.confirmBtnText = '我知道了'
+          this.dialog.showCancelBtn = false
+          this.dialog.content =
+            '当前身份为“个人版”,不支持信息发布,请将身份切换至<br>企业/组织,并完成机构认证后,再进行信息发布。'
+          break
+        case 100: // 100为自定义状态
+          this.dialog.confirmBtnText = '去开通'
+          this.dialog.showCancelBtn = true
+          this.dialog.content = `<div class="service-info">
+              <p class="black-text service-title">-  服务介绍  -</p>
+              <p>为您提供 <em class="black-text">招标信息</em> 、 <em class="black-text">供应信息</em> 、 <em class="black-text">需求信息</em> 发布平台<br>帮助您快速找到供应商、加强产品推介!</p>
+            </div>
+            <p>信息发布功能仅针对超级订阅及以上级别会员开放使用</p>`
+      }
+    },
+    // 获取是否绑定手机号
+    async getBoundPhoneFn() {
+      const { data } = await getBoundPhone()
+      // 绑定过手机号才执行查询企业相关信息
+      if (data) {
+        this.selectedDefaultFn()
+        this.getEntInfoFn()
+        // this.getEntListFn() // p278移除企业切换
+        // 存绑定手机号 提交表单时需要
+        this.entInfo.boundPhone = data?.phone
+        if (data.phone === '') {
+          location.href = '/swordfish/frontPage/userMerge/sess/bind'
+        }
+      }
+    },
+    // 默认选择一个企业
+    async selectedDefaultFn() {
+      await selectedDefaultEnt()
+    },
+    // 查询企业认证状态
+    async getEntCheckStatusFn() {
+      const { vipStatus, memberStatus } = this.userInfo
+      if (!this.isLimited && vipStatus <= 0 && memberStatus <= 0) {
+        this.setDialogProps(100)
+        return
+      }
+      const { data } = await getEntCheckStatus()
+      if (data) {
+        this.entInfo.authStatus = data.authStatus
+        this.entInfo.freezeStatus = data.frozenStatus
+        if (data.authStatus === 1) {
+          this.isLoad = true
+        } else {
+          this.isLoad = false
+          this.setDialogProps(data.authStatus)
+        }
+      } else {
+        // 无企业但有企业id 没有返回认证状态时也需提示创建企业
+        this.setDialogProps(0)
+      }
+    },
+    // 切换企业
+    switchEntFn(data) {
+      selectEnt({
+        id: data.id
+      }).then((res) => {
+        if (res.data && res.data.status > 0) {
+          // window.location.reload()
+          this.reload()
+        }
+      })
+    },
+    async getEntInfoFn() {
+      this.loading = true
+      const res = await identityList()
+      if (res.data) {
+        this.loading = false
+        const list = res.data
+        let personage = false
+        let hasOrganization = false
+        list.forEach((e) => {
+          if (e.positionType === 0 && e.checked === 1) {
+            // 当前为个人版
+            personage = true
+          }
+          if (e.positionType === 1) {
+            hasOrganization = true // 存在机构身份
+          }
+        })
+        if (personage) {
+          // 当前选中为个人版
+          if (hasOrganization) {
+            // 存在机构身份
+            this.setDialogProps(4)
+          } else {
+            // 不存在
+            this.setDialogProps(0)
+          }
+          return
+        }
+      }
+      this.loading = true
+      const { data } = await getEntInfo()
+      if (data) {
+        this.loading = false
+        this.entInfo.ent_name = data.ent_name
+        this.entInfo.ent_id = data.ent_id
+        this.entInfo.ent_auth_status = data.ent_auth_status
+        this.entInfo.user_id = data.user_id
+        // 有企业id去查询当前企业认证状态
+        if (data.ent_id) {
+          this.getEntCheckStatusFn(data.ent_id)
+          this.getEntRegion(data.ent_id)
+        } else {
+          // 无企业id即没有创建企业 提示去认证(认证即创建)
+          this.dialog.showDialog = true
+          this.setDialogProps(0)
+        }
+      }
+    },
+    async getEntListFn() {
+      // p278移除企业切换
+      const { data } = await getEntList()
+      if (data) {
+        this.entList = data.ents.filter((item) => {
+          return item.auth_status === 1
+        })
+      }
+    },
+    // 查询当前企业所在地区
+    async getEntRegion(entId) {
+      const { data } = await getMyEntInfo({ entId: entId })
+      if (data) {
+        this.entInfo.province = data?.province
+        this.entInfo.city = data?.city
+      }
+    },
+    openCustomer() {
+      this.contactCustomer(this)
+      /* eslint-disable */
+      // this.$nextTick(() => {
+      //   if ($('#zc__sdk__container').height() <= 0) {
+      //     zc('frame_status', function(data) {
+      //       openCustomDig = data === 'expand'
+      //     })
+      //   }
+      // })
+      /* eslint-enable */
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.in-app {
+  .issued-index {
+    padding-top: 0;
+  }
+  .my-issued,
+  .info-issued {
+    @extend .sub-card-container;
+  }
+}
+.issued-index {
+  padding: 48px 0 108px;
+  .light-text {
+    text-decoration: underline;
+    cursor: pointer;
+  }
+  .issued-nav {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 20px;
+  }
+  .link-to {
+    display: inline-block;
+    width: 105px;
+    font-size: 16px;
+    color: $color_main;
+    text-align: center;
+    line-height: 35px;
+    font-weight: bold;
+  }
+  .active-class {
+    width: 108px;
+    height: 36px;
+    background: url('~@/assets/image/public/tooltip.png') no-repeat center;
+    background-size: contain;
+    color: #fff;
+  }
+}
+</style>
+<style lang="scss">
+.service-info {
+  margin-bottom: 20px;
+  padding: 14px 0;
+  background: #f7f9fc;
+  border-radius: 8px;
+  font-size: 13px;
+  .service-title {
+    margin-bottom: 12px;
+  }
+}
+.black-text {
+  color: #1d1d1d;
+}
+</style>

+ 1165 - 0
apps/jy-pc/src/views/purchase/components/PurchaseForm.vue

@@ -0,0 +1,1165 @@
+<template>
+  <div class="purchase-form">
+    <el-form :model="ruleForm" label-width="104px" :rules="rules">
+      <div class="group-info">
+        <div class="group-title">
+          <span>发布采购信息</span>
+          <div class="group-title-tip">
+            <img src="@/assets/image/purchase/vip.png" alt="采购信息" />
+            <span>限时免费!不限量!</span>
+          </div>
+        </div>
+        <el-form-item
+          label="信息标题:"
+          prop="infoTitle"
+          :inline-message="true"
+        >
+          <el-input
+            type="textarea"
+            class="custom-input"
+            v-model.trim="ruleForm.infoTitle"
+            placeholder="请输入信息标题"
+            autosize
+            maxlength="50"
+            show-word-limit
+          >
+          </el-input>
+        </el-form-item>
+        <div v-show="!showSupply">
+          <el-form-item
+            v-show="ruleForm.msgType != 4"
+            label="项目编号:"
+            prop="infoCode"
+          >
+            <el-input
+              class="custom-input"
+              v-model.trim="ruleForm.infoCode"
+              placeholder="请输入项目编号"
+              maxlength="50"
+            ></el-input>
+          </el-form-item>
+          <el-form-item
+            class="delivery-form"
+            label="交付地点:"
+            prop="deliveryAddress"
+            :inline-message="true"
+          >
+            <el-cascader
+              v-model="ruleForm.deliveryAddress"
+              placeholder="请选择省/市/区"
+              :options="deliveryOptions"
+              :props="{ value: 'code', label: 'name', children: 'children' }"
+            >
+            </el-cascader>
+            <el-input
+              class="address-input"
+              v-model.trim="ruleForm.detailAddress"
+              maxlength="100"
+              placeholder="请输入详细地址信息,如道路、门牌号等信息"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="项目地区:" prop="province">
+            <el-select
+              v-model="ruleForm.province"
+              placeholder="选择省"
+              class="region-select"
+              @change="provinceChange"
+            >
+              <el-option
+                v-for="p in regionData"
+                :key="p.code"
+                :label="p.name"
+                :value="p.name"
+              ></el-option>
+            </el-select>
+            <el-select
+              v-model="ruleForm.city"
+              placeholder="选择市"
+              class="region-select"
+              clearable
+              v-if="ruleForm.province && cityData.length > 1"
+            >
+              <el-option
+                v-for="c in cityData"
+                :key="c.code"
+                :label="c.name"
+                :value="c.name"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+          <!-- <IndustrySelector @onChange="getIndustry" :showAllButton="false" selectorType="line" dataType="industry" ref="industryRef">
+            <span class="el-form-item__label industry-label" slot="header">项目行业:</span>
+          </IndustrySelector> -->
+          <el-form-item label="采购单位:" prop="buyer" :inline-message="true">
+            <el-input
+              class="custom-input"
+              v-model.trim="ruleForm.buyer"
+              placeholder="请输入采购单位全称"
+            >
+            </el-input>
+          </el-form-item>
+          <el-form-item
+            v-show="ruleForm.msgType != 4"
+            label="预算金额:"
+            prop="budget"
+          >
+            <el-input
+              class="small-input"
+              v-model="ruleForm.budget"
+              placeholder="请输入数字,支持小数点后两位"
+              oninput="value=value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1')"
+              maxlength="12"
+            >
+            </el-input>
+            <span class="normal-money">万元</span>
+          </el-form-item>
+          <el-form-item
+            v-show="ruleForm.msgType == 6"
+            label="中标单位:"
+            prop="winner"
+            :inline-message="true"
+          >
+            <el-autocomplete
+              class="custom-input"
+              :popper-class="cname"
+              v-model.trim="ruleForm.winner"
+              placeholder="请输入中标单位全称"
+              :debounce="500"
+              :fetch-suggestions="querySearchWinner"
+              @select="selectWinner"
+              @focus="getWinnerFocus('cname')"
+              clearable
+            >
+              <template slot-scope="{ item }">
+                <div class="name" v-html="item"></div>
+              </template>
+            </el-autocomplete>
+          </el-form-item>
+          <el-form-item
+            v-show="ruleForm.msgType == 6"
+            label="中标金额:"
+            prop="amount"
+          >
+            <el-input
+              class="small-input"
+              v-model="ruleForm.amount"
+              placeholder="请输入数字,支持小数点后两位"
+              maxlength="12"
+              oninput="value=value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1')"
+            >
+            </el-input>
+            <span class="normal-money">万元</span>
+          </el-form-item>
+        </div>
+        <el-form-item label="信息正文:" prop="content" required>
+          <Editor
+            v-model="ruleForm.content"
+            ref="editorRef"
+            :menubar="editorInfo.menubar"
+            :toolbar="editorInfo.toolbar"
+            :maxLength="10000"
+          >
+          </Editor>
+          <p
+            v-show="ruleForm.msgType == '1' || ruleForm.msgType == '5'"
+            class="editor-tips"
+          >
+            公告内容按照法规要求应当尽量载明:招标人名称、地址和联系方式、招标项目的性质、规模、资金来源、实施地点和时间、投标截止日以及获取招标文件或者资格预审文件的时间和方法等事项。
+          </p>
+        </el-form-item>
+        <el-form-item
+          label="有效期至:"
+          prop="endTime"
+          v-if="showSupply"
+          :inline-message="true"
+        >
+          <el-date-picker
+            v-model="ruleForm.endTime"
+            type="datetime"
+            format="yyyy-MM-dd HH:mm"
+            default-time="23:59:59"
+            placeholder="请选择信息有效截止日期"
+            prefix-icon="j-icon icon-date-gray"
+            value-format="yyyy-MM-dd HH:mm"
+            clearable
+            :editable="false"
+            :picker-options="pickerOptions"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item label="上传附件:" prop="attach">
+          <el-upload
+            class="upload-demo"
+            action="string"
+            :http-request="uploadFileHandle"
+            :limit="5"
+            :file-list="fileList"
+            :on-remove="handleRemove"
+            :on-exceed="uploadExceed"
+            :before-upload="beforeUpload"
+          >
+            <div class="upload-container">
+              <el-button
+                icon="el-icon-plus"
+                class="upload-btn"
+                size="small"
+                type="primary"
+                >选择上传文件</el-button
+              >
+              <div slot="tip" class="el-upload__tip">
+                提示:支持jpg、jpeg、png、doc(.docx)、.xls(.xlsx)、.pdf、.txt、zip、rar格式,大小≤2M
+              </div>
+            </div>
+          </el-upload>
+        </el-form-item>
+        <!-- <el-progress :percentage="progress" :show-text="false"></el-progress> -->
+      </div>
+      <div class="group-contact">
+        <div class="group-title">
+          <span>联系人信息</span>
+          <div class="group-title-tip contact-tip">
+            <span>将作为“采购单位联系方式”进行公开</span>
+          </div>
+        </div>
+        <el-form-item label="联系人:" prop="name" :inline-message="true">
+          <el-input
+            class="custom-input"
+            v-model.trim="ruleForm.name"
+            placeholder="请输入姓名"
+            maxlength="20"
+          >
+          </el-input>
+        </el-form-item>
+        <el-form-item label="联系电话:" prop="phone" :inline-message="true">
+          <el-input
+            class="custom-input"
+            v-model.trim="ruleForm.phone"
+            placeholder="请输入联系电话"
+            maxlength="11"
+            oninput="value=value.replace(/\D/g,'')"
+          >
+          </el-input>
+        </el-form-item>
+        <div :class="{ 'checkbox-parent': !isPublic }">
+          <el-checkbox v-model="ruleForm.read">我已阅读,理解并接受</el-checkbox
+          ><a class="service" @click="goService" href="javascript:;"
+            >《剑鱼标讯信息发布免责声明》</a
+          >
+        </div>
+        <div class="supply-agent">
+          <div class="top">
+            <div class="left-info">供应商推荐服务</div>
+            <div class="right-info">
+              信息审核通过后,剑鱼标讯将根据您的招标采购需求,为您匹配并推荐供应商
+            </div>
+          </div>
+          <el-radio class="radios" v-model="ruleForm.supplyRecommend" label="1"
+            >我需要</el-radio
+          >
+          <el-radio class="radios" v-model="ruleForm.supplyRecommend" label="-1"
+            >我不需要</el-radio
+          >
+        </div>
+        <div class="checkbox-parent">
+          <el-button
+            :disabled="submitDisabled"
+            class="submit-btn"
+            type="primary"
+            @click="onSubmit"
+            >提交</el-button
+          >
+        </div>
+      </div>
+    </el-form>
+
+    <el-dialog
+      title="提交失败"
+      :visible.sync="showSubfailDialog"
+      width="464px"
+      class="edialog"
+      center
+    >
+      <div class="dialog">
+        <span>您已同意将剑鱼标讯作为项目发布媒体,</span>
+        <span class="center-content">还请在信息正文处增加以下内容:</span>
+        <span>项目发布媒体:剑鱼标讯(https://www.jianyu360.cn/)</span>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="copyToput">复制并修改</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import Editor from '@/components/editor/Editor'
+import ChinaJson from '@/assets/js/china_area.js'
+// import IndustrySelector from '@/components/selector/IndustrySelector'
+import {
+  getArea,
+  pushedRelated,
+  publishBiddingInfo,
+  publishSupplyInfo,
+  myPublishDetail,
+  infoFileUpload
+} from '@/api/modules/jyinfo'
+import { getEntAssociation } from '@/api/modules/pay'
+import { brightKeyword, debounce } from '@/utils/utils'
+import { openSelfLink } from '@/utils'
+export default {
+  name: 'purchase-form',
+  props: ['entInfo', 'surplus'],
+  components: {
+    Editor
+    // IndustrySelector
+  },
+  computed: {
+    submitDisabled() {
+      const title = this.ruleForm.infoTitle
+      const deliveryAddress = this.ruleForm.deliveryAddress
+      const detailAddress = this.ruleForm.detailAddress
+      const content = this.ruleForm.content
+      const phone = this.ruleForm.phone
+      const name = this.ruleForm.name
+      const province = this.ruleForm.province
+      const buyer = this.ruleForm.buyer
+      const isRead = this.ruleForm.read
+      const endTime = this.ruleForm.endTime
+      const winner = this.ruleForm.winner
+      if (this.ruleForm.msgType === '3') {
+        return !(title && content && phone && name && isRead && endTime)
+      } else if (this.ruleForm.msgType === '6') {
+        return !(
+          title &&
+          content &&
+          phone &&
+          name &&
+          province &&
+          buyer &&
+          isRead &&
+          winner
+        )
+      } else if (this.ruleForm.msgType === '8') {
+        return !(
+          title &&
+          deliveryAddress &&
+          detailAddress &&
+          content &&
+          phone &&
+          name &&
+          province &&
+          buyer &&
+          isRead
+        )
+      } else {
+        return !(
+          title &&
+          content &&
+          phone &&
+          name &&
+          province &&
+          buyer &&
+          isRead
+        )
+      }
+    },
+    deliveryOptions() {
+      return this.convertDataStructure(ChinaJson)
+    }
+  },
+  data() {
+    const validatorPhone = (rule, value, callback) => {
+      const status = /^1[3456789]\d{9}$/.test(value)
+      if (value === '') {
+        return callback(new Error('请输入联系电话'))
+      } else {
+        if (!status) {
+          return callback(new Error('手机号格式不正确'))
+        } else {
+          callback()
+        }
+      }
+    }
+    const validatorTime = (rule, value, callback) => {
+      if (!value) {
+        return callback(new Error('请选择有效期'))
+      } else {
+        callback()
+      }
+    }
+    return {
+      regionData: [],
+      cityData: [],
+      showSubfailDialog: false,
+      isPublic: true,
+      selectNotice: {},
+      ruleForm: {
+        msgType: '8',
+        infoTitle: '',
+        infoCode: '',
+        deliveryAddress: '', // 省市区街道
+        detailAddress: '', // 详细地址
+        province: '',
+        city: '',
+        industry: [],
+        buyer: '',
+        budget: '',
+        winner: '',
+        amount: '',
+        endTime: '',
+        attach: [],
+        content: '',
+        name: '',
+        phone: '',
+        read: false,
+        supplyRecommend: '1'
+      },
+      fileList: [],
+      rules: {
+        infoTitle: [
+          { required: true, message: '请输入信息标题', trigger: 'change' }
+        ],
+        deliveryAddress: [
+          {
+            required: true,
+            validator: this.validatorAddress,
+            trigger: 'change'
+          }
+        ],
+        buyer: [
+          { required: true, message: '请输入采购单位', trigger: 'change' }
+        ],
+        winner: [
+          { required: true, message: '请输入中标单位', trigger: 'change' }
+        ],
+        name: [{ required: true, message: '请输入联系人', trigger: 'change' }],
+        phone: [
+          { required: true, validator: validatorPhone, trigger: 'change' }
+        ],
+        province: [{ required: true, message: '请选择省', trigger: 'change' }],
+        endTime: [
+          { required: true, validator: validatorTime, trigger: 'change' }
+        ]
+      },
+      showSupply: false,
+      cname: '', // 中标单位输入框dom-class
+      // 富文本编辑器配置
+      editorInfo: {
+        menubar: '',
+        toolbar:
+          'code undo redo restoredraft | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | table preview fullscreen'
+      },
+      pickerOptions: {
+        disabledDate(time) {
+          return time.getTime() < Date.now()
+        }
+      }
+    }
+  },
+  created() {
+    this.msgTypeinit()
+  },
+  mounted() {
+    this.getAreaFn()
+    this.getIndustryShowMore()
+    this.echoUserInfo()
+    this.setContentVal()
+  },
+  watch: {
+    'ruleForm.buyer': {
+      handler(newValue, oldValue) {
+        if (newValue !== this.entInfo.ent_name) {
+          this.isPublic = false
+        } else {
+          this.isPublic = true
+        }
+      }
+    }
+  },
+  methods: {
+    brightKeyword,
+    validatorAddress(rule, value, callback) {
+      if (!value.length || !this.ruleForm.detailAddress) {
+        return callback(new Error('请填写交付地点'))
+      } else {
+        callback()
+      }
+    },
+    setContentVal() {
+      if (this.ruleForm.content === '') {
+        this.ruleForm.content =
+          '项目发布媒体:剑鱼标讯(https://www.jianyu360.cn/)'
+      } else {
+        this.ruleForm.content =
+          '项目发布媒体:剑鱼标讯(https://www.jianyu360.cn/)' +
+          this.ruleForm.content
+      }
+    },
+    convertDataStructure(data) {
+      // 假设data是一个包含多个省份对象的数组
+      return data.map((province) => {
+        const zxs = ['北京市', '天津市', '重庆市', '上海市']
+        // 将zxs的市去掉
+        zxs.forEach((item) => {
+          if (item === province.name) {
+            province.name = province.name.replace(/市$/, '')
+          }
+        })
+        // 将数据中的钓鱼岛数据删除
+        province.name = province.name.replace(/省$/, '')
+        // 构造省份对象
+        const provinceObj = {
+          code: province.name, // 假设ProID作为省份的code
+          name: province.name,
+          children: []
+        }
+
+        // 处理城市数据
+        if (province.city) {
+          provinceObj.children = province.city.map((city) => {
+            // 构造城市对象
+            return {
+              code: city.name, // 假设CityID作为城市的code
+              name: city.name,
+              children: city.area
+                ? city.area.map((areaName, index) => ({
+                    code: areaName, // 区域没有提供具体的code,所以留空或者可以自定义编码规则
+                    name: areaName
+                  }))
+                : []
+            }
+          })
+        }
+
+        return provinceObj
+      })
+    },
+    arrayToObject(arr) {
+      const formatObj = arr.reduce((obj, cur, index) => {
+        if (cur.uid) {
+          delete cur.uid
+        }
+        obj[`${index}`] = cur
+        return obj
+      }, {})
+      return formatObj
+    },
+    getIndustryShowMore() {
+      if (this.$refs.industryRef) {
+        if (this.$refs.industryRef.$refs.content) {
+          this.$refs.industryRef.$refs.content.showMore = true
+        }
+      }
+    },
+    msgTypeinit() {
+      const type = this.$route.query.informationType
+      if (type && type === 'cgxx') {
+        this.ruleForm.msgType = '2'
+      } else if (type === 'gyxx') {
+        this.ruleForm.msgType = '3'
+      } else if (type === 'zbgg') {
+        this.ruleForm.msgType = '1'
+      } else if (type === 'cgyx') {
+        this.ruleForm.msgType = '4'
+      } else if (type === 'zbyg') {
+        this.ruleForm.msgType = '5'
+      }
+      if (this.ruleForm.msgType === '3' || this.ruleForm.msgType === 3) {
+        this.showSupply = true
+        this.isPublic = true
+      } else {
+        this.showSupply = false
+      }
+    },
+    // 关联公告联想Api
+    async getNoticeList(val, type) {
+      const params = {
+        match: val,
+        msgType: Number(this.ruleForm.msgType),
+        type: Number(type)
+      }
+      const { data } = await pushedRelated(params)
+      return data
+    },
+    // 回显联系人和注册手机号
+    echoUserInfo() {
+      // this.ruleForm.name = this.entInfo?
+      this.getNoticeList('', 1).then((res) => {
+        if (res && res.length > 0) {
+          this.ruleForm.name = res[0].person
+          this.ruleForm.supplyRecommend = res[0].recommendedService
+            ? String(res[0].recommendedService)
+            : '1'
+        }
+      })
+      this.ruleForm.buyer = this.entInfo?.ent_name
+      this.ruleForm.phone = this.entInfo?.boundPhone
+      // this.ruleForm.province = this.entInfo?.province
+      // this.ruleForm.city = this.entInfo?.city
+    },
+    // 根据选择的关联公告查询对应的信息用于回显
+    async getNoticeDetail() {
+      const p = {
+        msgId: this.selectNotice.id,
+        type: 0,
+        msgType: Number(this.ruleForm.msgType)
+      }
+      const { data } = await myPublishDetail(p)
+      if (data) {
+        this.ruleForm.infoCode = data.code ? data.code : ''
+        this.ruleForm.province = data.province ? data.province : ''
+        this.provinceChange(data.province)
+        this.ruleForm.city = data.city ? data.city : ''
+        this.ruleForm.industry = data.industry ? data.industry : []
+        this.ruleForm.buyer = data.buyer ? data.buyer : ''
+        this.ruleForm.winner = data.winner ? data.winner : ''
+        this.ruleForm.supplyRecommend = data.recommendedService
+          ? String(data.recommendedService)
+          : '1'
+        this.ruleForm.budget = data.budget
+          ? Math.floor((data.budget / 10000) * 100) / 100
+          : ''
+        this.ruleForm.amount = data.amount
+          ? Math.floor((data.amount / 10000) * 100) / 100
+          : ''
+        if (data.infoDetailContact) {
+          this.ruleForm.phone = data.infoDetailContact.phone
+            ? data.infoDetailContact.phone
+            : ''
+          this.ruleForm.name = data.infoDetailContact.name
+            ? data.infoDetailContact.name
+            : ''
+        }
+      }
+    },
+    // 地区省份切换城市
+    provinceChange(val) {
+      this.ruleForm.city = ''
+      const provinces = this.regionData
+      const arr = []
+      provinces.forEach((v) => {
+        if (val === v.name) {
+          v.citys.forEach((s) => {
+            arr.push(s)
+          })
+        }
+      })
+      this.cityData = arr
+    },
+    // 获取省市
+    async getAreaFn() {
+      const { data } = await getArea()
+      if (data) {
+        const arr = data.filter((v) => {
+          return v.name !== '全国'
+        })
+        this.regionData = arr
+      }
+    },
+    // 中标单位获取焦点事件
+    getWinnerFocus(e) {
+      this.cname = e
+    },
+    // 中标单位联想
+    async querySearchWinner(query, cb) {
+      const $cname = document.querySelector('.' + this.cname)
+      if (!query || query.length < 2) {
+        $cname.style.display = 'none'
+        return
+      }
+      const params = {
+        name: query
+      }
+      const { data } = await getEntAssociation(params)
+      const arr = []
+      if (data && data.length > 0) {
+        data.forEach((item) => {
+          item = this.brightKeyword(query, item)
+          arr.push(item)
+        })
+        $cname.style.display = 'block'
+        cb(arr)
+      } else {
+        $cname.style.display = 'none'
+      }
+    },
+    // 选择中标单位
+    selectWinner(item) {
+      this.ruleForm.winner = item.replace(/<.*?>/gi, '')
+    },
+    // 附件上传前生命周期函数
+    beforeUpload(file) {
+      const size = file.size / 1024 / 1024
+      const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1)
+      const whiteList = [
+        'jpg',
+        'jpeg',
+        'png',
+        'doc',
+        'docx',
+        'xls',
+        'xlsx',
+        'pdf',
+        'txt',
+        'zip',
+        'rar'
+      ]
+      if (size > 2) {
+        this.$toast('上传失败,文件大小不能超过2M')
+        return false
+      }
+      if (whiteList.indexOf(fileSuffix) === -1) {
+        this.$toast('上传失败,格式错误')
+        return false
+      }
+    },
+    // 文件超出个数限制时的钩子
+    uploadExceed(file, fileList) {
+      if (fileList.length === 5) {
+        this.$toast('上传失败,最多可上传5个附件')
+      }
+    },
+    // 文件列表移除钩子
+    handleRemove(file, fileList) {
+      this.fileList = fileList
+      // 移除文件同时要删除掉当前已添加到本地附件队列里的file信息
+      const oldArr = this.ruleForm.attach
+      const newArr = oldArr.filter((v) => {
+        return v.uid !== file.uid
+      })
+      this.ruleForm.attach = newArr
+      // console.log('当前队列里的附件:', this.ruleForm.attach)
+    },
+    async uploadFileHandle(file) {
+      const params = new FormData()
+      params.append('file', file.file)
+      const { data } = await infoFileUpload(params)
+      if (data && Object.keys(data).length > 0) {
+        data.uid = file.file.uid
+        this.ruleForm.attach.push(data)
+      } else {
+        this.uploadError(file.file)
+      }
+    },
+    uploadError(file) {
+      this.$toast('上传失败')
+      console.log(file)
+      const list = this.fileList
+      const newArr = list.filter((v) => {
+        return v.uid !== file.uid
+      })
+      this.fileList = newArr
+    },
+    // 获取行业数据
+    getIndustry(item) {
+      const tempArr = []
+      Object.keys(item).forEach((v) => {
+        const tempItem = item[v]
+        if (Array.isArray(tempItem)) {
+          tempItem.forEach((vv) => {
+            tempArr.push(`${v}_${vv}`)
+          })
+        }
+      })
+      this.ruleForm.industry = tempArr
+    },
+    goService() {
+      const routeData = this.$router.resolve({
+        path: '/static/disclaimer'
+      })
+      openSelfLink(routeData)
+    },
+    copyToput() {
+      this.showSubfailDialog = false
+    },
+    // 表单提交
+    onSubmit: debounce(async function () {
+      const dev = import.meta.env.NODE_ENV !== 'production'
+      const {
+        msgType,
+        infoTitle,
+        infoCode,
+        deliveryAddress,
+        detailAddress,
+        province,
+        city,
+        buyer,
+        budget,
+        winner,
+        amount,
+        content,
+        endTime,
+        attach,
+        name,
+        phone,
+        supplyRecommend
+      } = this.ruleForm
+      // 开发环境无代理的话需传entId、entName、phone参数
+      const addParams = {
+        entId: 10000 || this.entInfo?.ent_id,
+        entName: this.entInfo?.ent_name,
+        phone: this.entInfo?.boundPhone
+      }
+      const freezeStatus = this.entInfo?.freezeStatus
+      if (freezeStatus > 0) {
+        return this.$toast('对不起,您的账号当前处于异常状态,信息发布失败')
+      }
+      if (msgType === '3') {
+        const supplyObj = {
+          title: infoTitle,
+          msgType: 3,
+          detail: content,
+          validityTime: endTime,
+          province: this.entInfo?.province,
+          city: this.entInfo?.city,
+          contact: {
+            person: name,
+            phone: phone
+          }
+        }
+        // 有附件再添加附件字段
+        if (attach && attach.length > 0) {
+          supplyObj.attach = this.arrayToObject(attach)
+        }
+        // 判断是否是开发环境
+        if (dev) {
+          Object.assign(supplyObj, addParams)
+        }
+        // eslint-disable-next-line
+        const { data, error_msg } = await publishSupplyInfo(supplyObj)
+        if (data && data.information_id) {
+          this.$emit('onSubmit', {
+            msgType: msgType,
+            msgId: data.information_id
+          })
+        } else {
+          this.$toast(error_msg)
+        }
+      } else {
+        let mtype = Number(msgType)
+        let typ = 0
+        if (mtype === 1) {
+          typ = 4
+        } else if (mtype === 4) {
+          typ = 5
+        } else if (mtype === 5) {
+          typ = 6
+        } else if (mtype === 6) {
+          typ = 7
+        } else {
+          typ = mtype
+        }
+        const deliveryAddressObj = {
+          area: deliveryAddress[0],
+          city: deliveryAddress[1],
+          districts: deliveryAddress[2],
+          detailsAddr: detailAddress
+        }
+        const bidObj = {
+          title: infoTitle,
+          msgType: typ,
+          relatedId: this.selectNotice?.id,
+          code: infoCode,
+          deliveryAddress: JSON.stringify(deliveryAddressObj),
+          province: province,
+          city: city,
+          // industry: JSON.stringify(industry),
+          buyer: buyer,
+          budget: budget === '' ? '' : String(budget * 10000),
+          winner: winner,
+          amount: amount === '' ? '' : String(amount * 10000),
+          detail: content,
+          recommendedService: Number(supplyRecommend),
+          contact: {
+            person: name,
+            phone: phone,
+            overt: 1
+          }
+        }
+        if (attach && attach.length > 0) {
+          bidObj.attach = this.arrayToObject(attach)
+        }
+        if (dev) {
+          Object.assign(bidObj, addParams)
+        }
+        // eslint-disable-next-line
+        const { data, error_msg } = await publishBiddingInfo(bidObj)
+        if (data && data.information_id) {
+          this.$emit('onSubmit', {
+            msgType: msgType,
+            msgId: data.information_id
+          })
+        } else {
+          this.$toast(error_msg)
+        }
+      }
+    }, 500)
+  }
+}
+</script>
+<style lang="scss">
+body {
+  position: inherit;
+}
+
+.el-autocomplete-suggestion {
+  display: none;
+}
+</style>
+<style lang="scss" scoped>
+.focus-border-color {
+  &:hover,
+  &:focus,
+  &:visited,
+  &:active {
+    border-color: #e0e0e0;
+  }
+}
+
+.purchase-form {
+  padding: 24px 0;
+
+  .custom-input {
+    width: 600px;
+  }
+
+  .region-select {
+    width: 290px;
+
+    &:first-child {
+      margin-right: 20px;
+    }
+  }
+
+  .small-input {
+    width: 290px;
+  }
+
+  .group-title {
+    display: flex;
+    align-items: center;
+    padding-left: 15px;
+    margin-bottom: 8px;
+    color: $color_main;
+    line-height: 40px;
+    font-weight: bold;
+    font-size: 14px;
+    .group-title-tip {
+      margin-left: 6px;
+      padding: 1px 8px;
+      background: #ff3a20;
+      border-radius: 4px;
+      line-height: 22px;
+      & > img {
+        width: 14px;
+        height: 14px;
+      }
+      & > span {
+        margin-left: 4px;
+        color: #fff5ec;
+        font-size: 14px;
+        line-height: 22px;
+        font-weight: normal;
+      }
+      &.contact-tip {
+        background: #f7f9fc;
+        & > span {
+          color: #2abed1;
+        }
+      }
+    }
+  }
+
+  .normal-money {
+    margin-left: 20px;
+  }
+
+  .supply-agent {
+    background-color: #f5f6f7;
+    border-radius: 8px;
+    /*height: 96px;*/
+    font-size: 14px;
+    margin-top: 21px;
+    padding: 22px 20px;
+
+    .top {
+      display: flex;
+      align-items: center;
+      margin-bottom: 11px;
+
+      .left-info {
+        color: #1d1d1d;
+        margin-right: 17px;
+      }
+
+      .right-info {
+        color: #2cb7ca;
+      }
+    }
+  }
+
+  .upload-container {
+    display: flex;
+    align-items: center;
+
+    .el-upload__tip {
+      margin-left: 20px;
+    }
+
+    .upload-btn {
+      width: 132px;
+      height: 36px;
+      background: #f5feff;
+      color: $color_main;
+      border-radius: 6px;
+      border: 1px solid #2cb7ca;
+      text-align: center;
+    }
+  }
+
+  .editor-tips {
+    margin-top: 8px;
+    font-size: 14px;
+    color: $color_yellow;
+    line-height: 22px;
+  }
+
+  .checkbox-parent {
+    line-height: 48px;
+  }
+
+  .service {
+    font-size: 14px;
+    color: $color_main;
+  }
+
+  .submit-btn {
+    width: 180px;
+    height: 46px;
+    margin-top: 40px;
+    font-size: 16px;
+    border-radius: 6px;
+  }
+
+  ::v-deep {
+    input[type='file'].el-upload__input {
+      display: none !important;
+    }
+
+    .el-form-item {
+      margin-bottom: 14px;
+      &.delivery-form {
+        .el-form-item__content {
+          .el-cascader {
+            width: 290px;
+          }
+          .address-input {
+            width: 600px;
+            margin-left: 20px;
+          }
+        }
+      }
+    }
+
+    .el-form-item__label {
+      padding: 0 20px 0 0;
+      color: #1d1d1d;
+    }
+
+    .el-input__inner {
+      height: 36px;
+      line-height: 36px;
+    }
+
+    .el-upload-list--text {
+      width: 40%;
+    }
+
+    .el-upload-list__item .el-icon-upload-success {
+      color: $color_main;
+    }
+
+    .icon-date-gray {
+      width: 18px;
+    }
+
+    .el-icon-plus {
+      font-weight: bold;
+      font-size: 14px;
+    }
+
+    .el-upload-list__item-name {
+      font-size: 12px;
+    }
+
+    .el-upload-list__item.is-success .el-upload-list__item-name:focus,
+    .el-upload-list__item.is-success .el-upload-list__item-name:hover {
+      color: $color_main;
+    }
+
+    .el-checkbox .el-checkbox__label,
+    .el-checkbox .el-checkbox__input.is-checked + .el-checkbox__label {
+      color: #888;
+    }
+
+    .el-textarea__inner {
+      resize: none;
+      min-height: 36px !important;
+      padding: 6px 15px;
+      overflow: hidden;
+      line-height: 1.6;
+    }
+
+    .el-input__count {
+      line-height: 22px;
+    }
+
+    .selector-card.s-line {
+      padding: 0 !important;
+      margin-bottom: 14px;
+
+      .industry-label {
+        width: 95px;
+      }
+    }
+
+    .selector-card-header.s-header {
+      min-width: 0 !important;
+    }
+
+    .notice-select {
+      .el-input__inner {
+        @extend .focus-border-color;
+      }
+    }
+
+    .el-form-item.is-required:not(.is-no-asterisk)
+      > .el-form-item__label:before,
+    .el-form-item.is-required:not(.is-no-asterisk)
+      .el-form-item__label-wrap
+      > .el-form-item__label:before {
+      margin-right: 0;
+    }
+  }
+
+  .dialog {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    color: #686868;
+    font-size: 14px;
+    line-height: 22px;
+  }
+}
+</style>
+<style scoped>
+.edialog >>> .el-dialog .el-dialog__body {
+  padding: 20px 30px;
+}
+
+.radios >>> .el-radio__label {
+  color: #1d1d1d;
+  font-weight: 400;
+}
+</style>

+ 229 - 0
apps/jy-pc/src/views/purchase/components/PurchaseTable.vue

@@ -0,0 +1,229 @@
+<template>
+  <div class="table-components">
+    <el-table :data="list" style="width: 100%">
+      <el-table-column
+        class-name="base-column"
+        align="center"
+        width="120"
+        prop="msgType"
+        label="信息类型"
+      >
+        <template slot-scope="scope">
+          <div>{{ getMsyType(scope.row.msgType) }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column prop="title" label="信息标题">
+        <template slot-scope="scope">
+          <div
+            class="ellipsis-2 finish-title"
+            v-if="scope.row.reviewStatus == 2"
+            @click="goDetail(scope.row, 'title')"
+          >
+            {{ scope.row.title }}
+          </div>
+          <div class="ellipsis-2 base-title" v-else>
+            {{ scope.row.title }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        class-name="base-column"
+        align="center"
+        width="120"
+        prop="reviewTime"
+        label="申请日期"
+      >
+        <template slot-scope="scope">
+          <div>
+            {{
+              dateFormatter(
+                new Date(scope.row.createTime).getTime(),
+                'yyyy-MM-dd'
+              )
+            }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        width="120"
+        prop="reviewStatus"
+        label="申请状态"
+      >
+        <template slot-scope="scope">
+          <div
+            :class="{
+              'finish-status': scope.row.reviewStatus == 2,
+              'warn-status': scope.row.reviewStatus == 3,
+              'wait-status': scope.row.reviewStatus == 1
+            }"
+          >
+            {{ getMsgStatus(scope.row.reviewStatus) }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column width="100" align="center" label="操作">
+        <template slot-scope="scope">
+          <div class="table-btn-cell">
+            <el-button
+              type="text"
+              size="medium"
+              @click="goDetail(scope.row, 'button')"
+              >查看</el-button
+            >
+            <el-button type="text" size="medium" @click="deleteFn(scope.row)"
+              >删除</el-button
+            >
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="tab-pagination" v-if="page.pageCount > 0">
+      <el-pagination
+        popper-class="pagination-custom-select"
+        background
+        :page-size="page.pageSize"
+        :current-page="page.pageNum"
+        layout="prev, pager, next, sizes, jumper"
+        :page-sizes="[5, 10, 50, 100]"
+        :page-count="page.pageCount"
+        :show-confirm-btn="true"
+        @current-change="handleCurrentChange"
+        @size-change="onSizeChange"
+      >
+      </el-pagination>
+    </div>
+  </div>
+</template>
+
+<script>
+import { dateFormatter } from '@/utils'
+export default {
+  name: 'table-components',
+  props: {
+    list: {
+      type: Array,
+      default() {
+        return []
+      },
+      required: true
+    },
+    page: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
+  methods: {
+    dateFormatter,
+    getMsyType(type) {
+      let str = ''
+      switch (type) {
+        case 1:
+          str = '招标信息'
+          break
+        case 2:
+          str = '采购信息'
+          break
+        case 3:
+          str = '供应信息'
+          break
+        case 4:
+          str = '招标公告'
+          break
+        case 5:
+          str = '采购意向'
+          break
+        case 6:
+          str = '招标预告'
+          break
+        case 7:
+          str = '招标结果'
+          break
+        case 8:
+          str = '采购信息'
+          break
+      }
+      return str
+    },
+    getMsgStatus(type) {
+      let str = ''
+      switch (type) {
+        case 1:
+          str = '审核中'
+          break
+        case 2:
+          str = '审核通过'
+          break
+        case 3:
+          str = '审核不通过'
+          break
+      }
+      return str
+    },
+    handleCurrentChange(currentPage) {
+      this.$emit('changePage', currentPage)
+    },
+    onSizeChange(size) {
+      this.$emit('sizePage', size)
+    },
+    goDetail(item, type) {
+      this.$emit('link', {
+        item,
+        type
+      })
+    },
+    deleteFn(data) {
+      this.$emit('delete', data)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.table-components {
+  .base-title {
+    color: #1d1d1d;
+  }
+  .finish-title {
+    color: $color_main;
+    cursor: pointer;
+  }
+  .wait-status {
+    color: $color_yellow;
+  }
+  .warn-status {
+    color: $color_red;
+  }
+  .finish-status {
+    color: #686868;
+  }
+  .tab-pagination {
+    padding-top: 20px;
+    text-align: right;
+  }
+  ::v-deep {
+    .el-table {
+      thead {
+        tr,
+        th.el-table__cell {
+          background: #f7f9fc;
+          font-size: 14px;
+          color: #1d1d1d;
+        }
+      }
+      tbody {
+        .base-column {
+          .cell {
+            color: #999;
+          }
+        }
+      }
+      .table-btn-cell {
+        white-space: nowrap;
+      }
+    }
+  }
+}
+</style>

+ 261 - 0
apps/jy-pc/src/views/purchase/info/InfoPurchase.vue

@@ -0,0 +1,261 @@
+<template>
+  <div class="info-purchase">
+    <div>
+      <div class="step-nav">
+        <div class="step-nav-content">
+          <StepItem
+            v-for="(item, index) in computedStepList"
+            :key="index"
+            :title="item.title"
+            :status="item.status"
+          >
+          </StepItem>
+        </div>
+        <div class="call-phone">
+          <span>如有问题,请拨</span>
+          <span><i class="j-icon icon-call-phone"></i>400-108-6670</span>
+        </div>
+      </div>
+      <div class="step-container">
+        <div class="step-first" v-if="activeStep == 1">
+          <!-- <div class="message">
+            信息发布不定期限时免费!供应信息每个认证机构每月可发布
+            <em class="light-count">{{ publish.total }}</em> 条(当月余额:
+            <em class="light-count">{{ publish.surplus }}</em>
+            条),每月1号上月余额清零重新计算;其余信息不限量!
+          </div> -->
+          <div class="issued-form">
+            <PurchaseForm
+              :entInfo="entInfo"
+              :surplus="publish.surplus"
+              @onSubmit="onSubmitCallback"
+            ></PurchaseForm>
+          </div>
+        </div>
+        <div class="step-second" v-else-if="activeStep == 2">
+          <Result
+            icon="icon-process"
+            title="审核中"
+            subTitle="您的采购信息发布申请已提交成功,平台方将在3个工作日内完成审核<br>如有问题,可拨打客服电话<span class='light-text'>400-108-6670</span>或在线<span class='light-text' onClick='openCustomer()'>联系客服</span> "
+            btnText="查看发布管理"
+            @onClick="checkIssuedList"
+          >
+          </Result>
+        </div>
+        <div class="step-third" v-else-if="activeStep == 3">
+          <Result
+            v-if="publish.status == 2"
+            icon="checkbox checked"
+            title="审核通过"
+            subTitle="您的采购信息发布申请已审核通过,该信息已自动发布到平台<br>可前往招标或供需搜索查看"
+            btnText="查看信息详情"
+            @onClick="checkIssuedDetail"
+          >
+          </Result>
+          <Result
+            v-if="publish.status == 3"
+            icon="icon-red-close"
+            title="审核不通过"
+            subTitle="您的采购信息发布申请未审核通过,请前往信息详情查看"
+            btnText="查看信息详情"
+            @onClick="checkIssuedDetail"
+          >
+          </Result>
+        </div>
+      </div>
+    </div>
+    <CustomDialog
+      title="信息发布"
+      top="30vh"
+      :showDialog="surplusDialog"
+      :showCancelBtn="false"
+      confirmBtnText="我知道了"
+      @onConfirm="onConfirmDialogBtn"
+    >
+      <div slot="content">
+        对不起,您本月可发布信息条数已达上限,<br />如需增加条数请联系客服
+      </div>
+    </CustomDialog>
+  </div>
+</template>
+
+<script>
+import StepItem from '@/components/steps/StepItem'
+import Result from '@/components/result/Result'
+import PurchaseForm from '@/views/purchase/components/PurchaseForm'
+import { reviewStatus } from '@/api/modules/jyinfo'
+import { getSupplySurplus } from '@/api/modules/resourceCenter'
+import CustomDialog from '@/components/dialog/Dialog'
+import { openSelfLink } from '@/utils'
+export default {
+  name: 'info-purchase',
+  props: ['entInfo'],
+  components: {
+    StepItem,
+    Result,
+    PurchaseForm,
+    CustomDialog
+  },
+  data() {
+    return {
+      stepList: [
+        {
+          index: 1,
+          title: '1.提交采购信息发布申请'
+        },
+        {
+          index: 2,
+          title: '2.平台方3个工作日内完成审核'
+        },
+        {
+          index: 3,
+          title: '3.采购信息自动发布到平台'
+        }
+      ],
+      activeStep: 1,
+      publish: {
+        total: 0,
+        surplus: 0,
+        status: 1
+      },
+      bid: {
+        msgType: '',
+        msgId: ''
+      },
+      timer: null,
+      surplusDialog: false
+    }
+  },
+  computed: {
+    computedStepList() {
+      const list = this.stepList
+      list.forEach((v, i) => {
+        i = i + 1
+        if (i === this.activeStep) {
+          v.status = 'process'
+        } else if (i < this.activeStep) {
+          v.status = 'finish'
+        } else {
+          v.status = 'wait'
+        }
+      })
+      return list
+    }
+  },
+  beforeRouteLeave(to, from, next) {
+    window.clearInterval(this.timer)
+    next()
+  },
+  created() {
+    this.getSupplySurplus()
+  },
+  mounted() {
+    window.openCustomer = this.openCustomer
+  },
+  methods: {
+    onConfirmDialogBtn() {
+      this.surplusDialog = false
+    },
+    checkIssuedList() {
+      openSelfLink(
+        {
+          link: '/purchase/my'
+        },
+        true
+      )
+    },
+    checkIssuedDetail() {
+      const routeData = this.$router.resolve({
+        path: '/purchase/detail/' + this.bid.msgId,
+        query: {
+          msgType: Number(this.bid.msgType)
+        }
+      })
+      openSelfLink(routeData)
+    },
+    async getSupplySurplus() {
+      // const params = {
+      //   functionCode: 'xxfb_gyxx_add'
+      // }
+      const { data } = await getSupplySurplus()
+      if (data) {
+        this.publish.total = data.total
+        this.publish.surplus = data.surplus
+        // if (data.surplus === 0) {
+        //   this.surplusDialog = true
+        // }
+      }
+    },
+    onSubmitCallback(data) {
+      if (data && Object.keys(data).length > 0) {
+        this.bid.msgType = data.msgType
+        this.bid.msgId = data.msgId
+        this.activeStep = 2
+        window.scrollTo(0, 0)
+        clearInterval(this.timer)
+        this.timer = setInterval(() => {
+          this.getReviewStatus(data.msgType, data.msgId)
+          if (this.activeStep !== 2) {
+            window.clearInterval(this.timer)
+          }
+        }, 10000)
+      }
+    },
+    async getReviewStatus(type, id) {
+      if (this.activeStep !== 2) return
+      const params = {
+        type: Number(type),
+        msgId: id
+      }
+      const { data } = await reviewStatus(params)
+      if (data) {
+        if (Number(data.status) === 2 || Number(data.status) === 3) {
+          this.activeStep = 3
+          this.publish.status = Number(data.status)
+        }
+      }
+    },
+    openCustomer() {
+      this.contactCustomer(this)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.info-purchase {
+  padding: 40px;
+  background: #fff;
+  .step-nav {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    .call-phone {
+      flex-shrink: 0;
+      display: flex;
+      flex-direction: column;
+      font-size: 14px;
+      color: #686868;
+      text-align: center;
+    }
+    .icon-call-phone {
+      width: 16px;
+      height: 16px;
+      margin-right: 6px;
+    }
+  }
+  .step-first {
+    .message {
+      margin-top: 24px;
+      padding: 9px 24px;
+      background: #f7f9fc;
+      border-radius: 4px;
+      font-size: $font-size-base;
+      .light-count {
+        color: $color_red;
+        font-weight: 600;
+      }
+    }
+  }
+}
+</style>

+ 285 - 0
apps/jy-pc/src/views/purchase/my/MyPurchase.vue

@@ -0,0 +1,285 @@
+<template>
+  <div class="my-issued">
+    <div class="filter-nav">
+      <div class="input-container">
+        <el-input placeholder="请输入信息标题关键词" v-model="filter.inputVal">
+          <i slot="suffix" class="el-input__icon j-icon icon-search-gray"></i>
+        </el-input>
+        <el-button type="text" size="small" @click="searchFn">搜索</el-button>
+      </div>
+    </div>
+    <div class="table-container">
+      <el-tabs v-model="activeName" @tab-click="handleClick">
+        <el-tab-pane
+          :label="'全部(' + count.amount + ')'"
+          name=" "
+        ></el-tab-pane>
+        <el-tab-pane
+          :label="'审核通过(' + count.approved + ')'"
+          name="2"
+        ></el-tab-pane>
+        <el-tab-pane
+          :label="'审核中(' + count.inReview + ')'"
+          name="1"
+        ></el-tab-pane>
+        <el-tab-pane
+          :label="'审核不通过(' + count.auditFailed + ')'"
+          name="3"
+        ></el-tab-pane>
+      </el-tabs>
+      <TableComponent
+        v-if="allData && allData.length > 0"
+        :list="allData"
+        :page="pageInfo"
+        @link="goDetailFn"
+        @delete="deleteFn"
+        @changePage="changePageFn"
+        @sizePage="changeSizeFn"
+      >
+      </TableComponent>
+      <Empty v-else>
+        <p>暂无数据</p>
+      </Empty>
+    </div>
+  </div>
+</template>
+
+<script>
+import TableComponent from '../components/PurchaseTable'
+import Empty from '@/components/empty/Empty'
+import { myPublishList, delMyPublish } from '@/api/modules/jyinfo'
+import { openSelfLink } from '@/utils'
+export default {
+  name: 'my-issued',
+  props: ['entInfo'],
+  components: {
+    TableComponent,
+    Empty
+  },
+  data() {
+    return {
+      filter: {
+        inputVal: ''
+      },
+      activeName: ' ',
+      allData: [],
+      pageInfo: {
+        pageSize: 10,
+        pageNum: 1,
+        pageCount: 0
+      },
+      count: {
+        amount: 0,
+        approved: 0,
+        auditFailed: 0,
+        inReview: 0
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    handleClick() {
+      this.pageInfo.pageNum = 1
+      this.getList()
+    },
+    searchFn() {
+      this.pageInfo.pageNum = 1
+      this.getList()
+    },
+    async getList() {
+      const params = {
+        match: this.filter.inputVal,
+        reviewStatus: this.activeName,
+        pageSize: this.pageInfo.pageSize,
+        pageIndex: this.pageInfo.pageNum,
+        classify: 1
+      }
+      const { data } = await myPublishList(params)
+      if (data) {
+        this.allData = data.list
+        if (this.pageInfo.pageNum === 1) {
+          this.pageInfo.pageCount = data.total
+          this.count.amount = data.amount || 0
+          this.count.approved = data.approved || 0
+          this.count.auditFailed = data.auditFailed || 0
+          this.count.inReview = data.inReview || 0
+        }
+      } else {
+        this.allData = []
+        this.pageInfo.pageCount = 0
+        this.count.amount = 0
+        this.count.approved = 0
+        this.count.auditFailed = 0
+        this.count.inReview = 0
+      }
+    },
+    goDetailFn(data) {
+      const { item, type } = data
+      if (type === 'title') {
+        // 招标采购信息三级页
+        const goHref = location.origin + `/article/content/${item.pId}.html`
+        window.open(
+          '/page_workDesktop/work-bench/page?link=' +
+            encodeURIComponent(goHref),
+          '_blank'
+        )
+      } else {
+        const routeData = this.$router.resolve({
+          path: '/purchase/detail/' + item.id,
+          query: {
+            msgType: item.msgType
+          }
+        })
+        openSelfLink(routeData)
+      }
+    },
+    msgBoxConfirm(id, content, msgType) {
+      this.$messageBox
+        .confirm(content, {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          center: true,
+          lockScroll: false,
+          showClose: false,
+          dangerouslyUseHTMLString: true,
+          customClass: 'custom-msg-box'
+        })
+        .then(() => {
+          const params = {
+            msgId: id,
+            type: 1,
+            msgType: Number(msgType)
+          }
+          delMyPublish(params).then((res) => {
+            if (res.error_code === 0) {
+              this.$toast('删除成功')
+              this.getList()
+            } else {
+              this.$toast('删除失败')
+            }
+          })
+        })
+        .catch(() => {})
+    },
+    deleteFn(item) {
+      const { id, reviewStatus, msgType } = item
+      if (reviewStatus === 2) {
+        this.msgBoxConfirm(
+          id,
+          '删除后其他用户将无法搜索到此条信息,<br>确定删除该条信息吗?',
+          msgType
+        )
+      } else {
+        this.msgBoxConfirm(id, '确定删除该条信息吗?', msgType)
+      }
+    },
+    changePageFn(data) {
+      this.pageInfo.pageNum = data
+      this.getList()
+    },
+    changeSizeFn(size) {
+      this.pageInfo.pageNum = 1
+      this.pageInfo.pageSize = size
+      this.getList()
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.custom-msg-box {
+  width: 464px;
+  padding: 32px;
+  border-radius: 8px;
+  box-sizing: border-box;
+  .el-message-box__header {
+    display: none;
+  }
+  .el-message-box__content {
+    padding: 0 0 20px;
+  }
+  .el-message-box__btns {
+    padding: 0;
+    display: flex;
+    justify-content: center;
+  }
+  .el-button {
+    width: 132px;
+    border-radius: 6px;
+    margin: 0;
+    font-size: 16px;
+    &.el-button--default,
+    &.el-button--default:hover,
+    &.el-button--default:focus {
+      background: transparent;
+      border: 1px solid #e0e0e0;
+      color: #686868;
+      order: 2;
+    }
+    &.el-button--primary,
+    &.el-button--primary:hover,
+    &.el-button--primary:focus {
+      margin-right: 50px;
+      background: $color_main;
+      color: #fff;
+      border: 1px solid $color_main;
+      order: 1;
+    }
+  }
+}
+</style>
+<style lang="scss" scoped>
+.my-issued {
+  padding: 40px 40px 32px;
+  background: #fff;
+  .filter-nav {
+    display: flex;
+    align-items: center;
+  }
+  .table-container {
+    margin-top: 30px;
+  }
+  ::v-deep {
+    .filter-nav {
+      .el-select,
+      .input-container {
+        .el-input__inner {
+          height: 36px;
+          line-height: 36px;
+        }
+      }
+      .filter-select {
+        width: 290px;
+      }
+    }
+    .input-container {
+      display: flex;
+      align-items: center;
+      & .el-input {
+        width: 320px;
+      }
+      .el-button--text {
+        margin-left: 10px;
+        font-size: 14px;
+      }
+      .icon-search-gray {
+        width: 18px;
+      }
+    }
+    .el-tabs__item {
+      padding: 0 20px;
+      height: 48px;
+      line-height: 48px;
+      min-width: 136px;
+      text-align: center;
+      font-size: 16px;
+      color: #686868;
+    }
+    .el-tabs__header {
+      margin: 0;
+    }
+  }
+}
+</style>

+ 11 - 0
apps/mobile/src/api/modules/search.js

@@ -22,6 +22,17 @@ export function getBiddingSearchList(data, type = 'fType') {
     data
   })
 }
+
+// 阳光采购搜索
+export function getSunSearchList(data) {
+  return request({
+    url: '/jyapi/jybx/core/purchaseSearch',
+    method: 'post',
+    noToast: true,
+    data
+  })
+}
+
 // 标讯搜索
 export function getBiddingSearchListOfNoLogin(data) {
   return request({

BIN
apps/mobile/src/assets/image/icon/sun-1.png


BIN
apps/mobile/src/assets/image/icon/sun-2.png


BIN
apps/mobile/src/assets/image/icon/sun-3.png


BIN
apps/mobile/src/assets/image/mask/bg/article-content-mask-card@2x.png


BIN
apps/mobile/src/assets/image/treasure-box/sun-guide.png


+ 1 - 1
apps/mobile/src/assets/js/china_area.js

@@ -5002,7 +5002,7 @@ const chinaMapJSON = [
           '木里藏族自治县',
           '盐源县',
           '德昌县',
-          '会理',
+          '会理',
           '会东县',
           '宁南县',
           '普格县',

+ 6 - 0
apps/mobile/src/assets/style/modules/tag.scss

@@ -18,6 +18,7 @@ $jy-tag-padding: 6px;
 
 .j-tag-item {
   $blue: #05a6f3;
+  $orange-deep: #FA6F33;
 
   display: inline-flex;
   align-items: center;
@@ -45,6 +46,11 @@ $jy-tag-padding: 6px;
     border-color: $orange;
     background-color: rgba($orange, 0.1);
   }
+  &.orange-deep {
+    color: $orange-deep;
+    border-color: $orange-deep;
+    background-color: rgba($orange-deep, 0.1);
+  }
   &.blue {
     color: $blue;
     border-color: $blue;

+ 15 - 0
apps/mobile/src/assets/style/pic-icon.scss

@@ -231,3 +231,18 @@
   background-image: url(@/assets/image/icon/icon-renmai@2x.png);
   background-size: contain;
 }
+
+.icon-sun-info {
+  background-image: url(@/assets/image/icon/sun-1.png);
+  background-size: contain;
+}
+
+.icon-sun-release {
+  background-image: url(@/assets/image/icon/sun-2.png);
+  background-size: contain;
+}
+
+.icon-sun-manage {
+  background-image: url(@/assets/image/icon/sun-3.png);
+  background-size: contain;
+}

+ 1 - 0
apps/mobile/src/components/common/ScrollNav.vue

@@ -72,6 +72,7 @@ export default {
     }
   }
   &-right {
+    flex: 1;
     padding-bottom: 48px;
     overflow-y: auto;
     background: #fff;

+ 86 - 0
apps/mobile/src/components/search/middle/HotKeyCardSun.vue

@@ -0,0 +1,86 @@
+<template>
+  <HistoryList
+    class="hot-key-card-container"
+    :list="hotKeyList"
+    @click="clickTag"
+    title="热门搜索"
+  >
+    <div slot="header-right" class="right-text" @click="updateList">
+      <span class="j-icon j-base-icon icon-update-grey clickable"></span>
+    </div>
+  </HistoryList>
+</template>
+<script>
+import { HistoryList } from '@/ui'
+import { ajaxGetSearchHotKeyList } from '@/api/modules/'
+
+export default {
+  name: 'HotKeyCard',
+  components: {
+    HistoryList
+  },
+  data() {
+    return {
+      hotKeyList: [],
+      loading: false
+    }
+  },
+  async created() {
+    this.getList()
+  },
+  methods: {
+    async getHotKeyList(payload = {}) {
+      const list = this.hotKeyList.map((r) => r.label)
+      if (payload && payload.refresh) {
+        const params = {
+          refresh: 1,
+          word: list.join(',')
+        }
+        await this.getHotKeyListReq(params)
+      } else {
+        if (list.length > 0) {
+          return list
+        } else {
+          await this.getHotKeyListReq()
+        }
+      }
+      return list
+    },
+    async getHotKeyListReq(payload = {}) {
+      const params = {
+        mold: 1,
+      }
+      if (payload) {
+        Object.assign(params, payload)
+      }
+      const { data, error_code: code } = await ajaxGetSearchHotKeyList(params)
+      if (code === 0 && Array.isArray(data)) {
+        const list = data.map((d) => {
+          return {
+            label: d.keyword
+          }
+        })
+        this.hotKeyList = list || []
+      }
+    },
+    async updateList() {
+      await this.getList(true)
+    },
+    async getList(refresh = false) {
+      if (this.loading) return
+      this.loading = true
+      if (refresh) {
+        await this.getHotKeyList({ refresh: 1 })
+      } else {
+        await this.getHotKeyList()
+      }
+      this.loading = false
+    },
+    // 跳转搜索
+    clickTag(item) {
+      this.$emit('clickTag', item)
+    }
+  }
+}
+</script>
+<style lang="scss" scoped></style>

+ 1385 - 0
apps/mobile/src/components/search/sun/filters.vue

@@ -0,0 +1,1385 @@
+<template>
+  <van-dropdown-menu
+    ref="dropdownMenu"
+    class="dropdown-menu-arrow"
+    swipe-threshold="4"
+    :close-on-click-outside="false"
+  >
+    <van-dropdown-item
+      ref="scopeDropdown"
+      :title-class="dropdownMenuHighlight('scope')"
+      title="搜索范围"
+      get-container="body"
+      @open="doOpen('scope')"
+      @opened="openedDropDown('scope')"
+    >
+      <DropdownLayout
+        maxHeight="50vh"
+        @cancel="onReset('scope')"
+        @confirm="onConfirm('scope')"
+      >
+        <ScopeSelector
+          :options="getScopeOptions"
+          :default-val="filters.scope"
+          ref="scopeSelector"
+          @open-tag="onNoPower"
+        >
+          <template #default="{ item }">
+            <van-tag class="" round type="primary" v-if="item.labelTag">{{
+              item.labelTag
+            }}</van-tag>
+          </template>
+        </ScopeSelector>
+      </DropdownLayout>
+    </van-dropdown-item>
+    <van-dropdown-item
+      ref="dateTimeDropdown"
+      :title-class="dropdownMenuHighlight('dateTime')"
+      title="发布时间"
+      get-container="body"
+      @open="doOpen('dateTime')"
+      @opened="openedDropDown('dateTime')"
+    >
+      <DropdownLayout
+        @cancel="onReset('dateTime')"
+        @confirm="onConfirm('dateTime')"
+      >
+        <JCell class="more-filter-item">
+          <template #label>
+            <DateTimeList
+              ref="dateTimeGroup"
+              @checkboxClick="checkboxGroupClick"
+              :options="getTimeOptions"
+            />
+          </template>
+        </JCell>
+      </DropdownLayout>
+    </van-dropdown-item>
+    <van-dropdown-item
+      ref="jfAreaDropdown"
+      :title-class="dropdownMenuHighlight('jfArea')"
+      title="交付地点"
+      get-container="body"
+      @open="doOpen('jfArea')"
+      @opened="openedDropDown('jfArea')"
+    >
+      <DropdownLayout @cancel="onReset('jfArea')" @confirm="onConfirm('jfArea')">
+        <div class="jf=area-container">
+          <AreaCitySidebar
+            class="area-city-sidebar"
+            :tagTextOnlySelectedCount="true"
+            :disabledCitySelect="false"
+            :useProvinceCitySplit="true"
+            v-model="cacheMoreFilters.jfArea"
+            @onDisabledCityClick="onNoPower"
+            ref="jfAreaSelector"
+          ></AreaCitySidebar>
+        </div>
+      </DropdownLayout>
+    </van-dropdown-item>
+    <!-- <van-dropdown-item
+      ref="bmjzztDropdown"
+      :title-class="dropdownMenuHighlight('bmjzzt')"
+      title="报名截止状态"
+      get-container="body"
+      @open="doOpen('bmjzzt')"
+      @opened="openedDropDown('bmjzzt')"
+    >
+      <DropdownLayout @cancel="onReset('bmjzzt')" @confirm="onConfirm('bmjzzt')">
+        <div class="bmjzzt-container">
+          <CheckboxGroup
+            :options="conf.bmjzzzOptions"
+            :config="conf.checkboxGroup"
+            :value="filters.bmjzzt"
+            @change="onBmjzztChange"
+          />
+        </div>
+      </DropdownLayout>
+    </van-dropdown-item> -->
+    <van-dropdown-item
+      ref="moreDropdown"
+      :title-class="dropdownMenuHighlight('more')"
+      title="更多筛选"
+      get-container="body"
+      @open="doOpen('more')"
+      @opened="openedDropDown('more')"
+    >
+      <DropdownLayout
+        :maxHeight="dropdownMenuMaxHeight"
+        @cancel="onReset('more')"
+        @confirm="onConfirm('more')"
+        :style="{ height: dropdownMenuMaxHeight }"
+      >
+        <ScrollNav
+          :menu-left="calcMoreLeftMenu"
+          @tab="onScrollTargetElement"
+          ref="scrollNav"
+        >
+          <div class="wrapper">
+            <JCell class="more-filter-item" title="多关键词">
+              <template #label>
+                <KeywordsInputGroup
+                  v-model="cacheMoreFilters.moreKeywordsMode"
+                  ref="moreKeywordsGroup"
+                  wordsMode
+                  class="more-keywords-component"
+                />
+              </template>
+            </JCell>
+            <JCell class="search-mode-filter more-filter-item" title="搜索模式">
+              <template #title>
+                <span class="search-mode-filter-label-text">搜索模式</span>
+                <AppIcon
+                  class="search-mode-label-icon"
+                  name="help1"
+                  @click="showSearchModeHelp"
+                />
+              </template>
+              <template #label>
+                <CheckboxGroup
+                  :options="conf.searchModeOptions"
+                  :config="conf.checkboxGroup"
+                  v-model="cacheMoreFilters.searchMode"
+                />
+              </template>
+            </JCell>
+            <!-- <van-cell
+              class="more-filter-item one-line-filter-item"
+              :isLink="true"
+              title="领域"
+              :border="false"
+              @click="showPopup('lingyu')"
+              :value="valueTextWithLingyu"
+            /> -->
+            <!-- <JCell class="more-filter-item signup-time" title="报名截止日期">
+              <template #label>
+                <DateTimeList
+                  ref="signUpEndTimeGroup"
+                  startPlaceholder="起始时间"
+                  @checkboxClick="checkboxGroupClick"
+                  :endTimeFeature="true"
+                  :options="conf.signUpEndTimeOptions"
+                />
+              </template>
+            </JCell> -->
+
+            <!-- <van-cell
+              class="more-filter-item one-line-filter-item"
+              :isLink="true"
+              title="交付地点"
+              :border="false"
+              @click="showPopup('jfArea')"
+              :value="valueTextWithJfArea"
+            ></van-cell> -->
+            <van-cell
+              class="more-filter-item one-line-filter-item"
+              :isLink="true"
+              title="项目地区"
+              :border="false"
+              @click="showPopup('projectArea')"
+              :value="valueTextWithProjectArea"
+            ></van-cell>
+
+
+            <!-- 行业 -->
+            <van-cell
+              class="more-filter-item one-line-filter-item"
+              :isLink="true"
+              title="行业"
+              :border="false"
+              @click="showPopup('industry')"
+              :value="valueTextWithIndustry"
+              ref="industryRef"
+            />
+            <JCell class="more-filter-item" title="附件">
+              <template #label>
+                <CheckboxGroup
+                  :options="conf.fileExists"
+                  :config="conf.checkboxGroup"
+                  v-model="cacheMoreFilters.fileExists"
+                />
+              </template>
+            </JCell>
+            <JCell class="more-filter-item" title="发布方">
+              <template #label>
+                <CheckboxGroup
+                  :options="conf.publisherList"
+                  :config="conf.checkboxGroup"
+                  v-model="cacheMoreFilters.publisher"
+                />
+              </template>
+            </JCell>
+          </div>
+        </ScrollNav>
+      </DropdownLayout>
+    </van-dropdown-item>
+    <van-popup
+      class="fix-pop-height"
+      v-model="popup.lingyu"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      :style="{ 'max-height': '70%' }"
+    >
+      <PopupLayout @closeIconClick="popup.lingyu = false">
+        <div slot="title">
+          <span>请选择领域</span>
+        </div>
+        <LingYuThreeSidebar ref="lingYuThreeSidebar" />
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('lingyu')">
+              重置
+            </button>
+            <button class="j-button-confirm" @click="popupConfirm('lingyu')">
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+    <!-- <van-popup
+      class="fix-pop-height"
+      v-model="popup.jfArea"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      :style="{ 'max-height': '70%' }"
+    >
+      <PopupLayout
+        title="请选择交付地点"
+        @closeIconClick="popup.jfArea = false"
+      >
+        <AreaCitySidebar
+          class="area-city-sidebar"
+          :tagTextOnlySelectedCount="true"
+          :disabledCitySelect="false"
+          :useProvinceCitySplit="true"
+          v-model="cacheMoreFilters.jfArea"
+          @onDisabledCityClick="onNoPower"
+          ref="jfAreaSelector"
+        ></AreaCitySidebar>
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('jfArea')">
+              重置
+            </button>
+            <button
+              class="j-button-confirm"
+              @click="popupConfirm('jfArea')"
+            >
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup> -->
+    <van-popup
+      class="fix-pop-height"
+      v-model="popup.projectArea"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      :style="{ 'max-height': '70%' }"
+    >
+      <PopupLayout
+        title="请选择项目地区"
+        @closeIconClick="popup.projectArea = false"
+      >
+        <AreaCitySidebar
+          class="area-city-sidebar"
+          :tagTextOnlySelectedCount="true"
+          :disabledCitySelect="false"
+          :useProvinceCitySplit="true"
+          v-model="cacheMoreFilters.projectArea"
+          @onDisabledCityClick="onNoPower"
+          ref="projectAreaSelector"
+        ></AreaCitySidebar>
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('projectArea')">
+              重置
+            </button>
+            <button
+              class="j-button-confirm"
+              @click="popupConfirm('projectArea')"
+            >
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+    <van-popup
+      class="fix-pop-height"
+      v-model="popup.industry"
+      round
+      position="bottom"
+      :safe-area-inset-bottom="true"
+      :close-on-popstate="true"
+      get-container="body"
+      :lazy-render="false"
+      style="max-height: 70%"
+    >
+      <PopupLayout @closeIconClick="popup.industry = false">
+        <div slot="title">
+          <span>请选择行业</span>
+        </div>
+        <IndustrySidebar ref="industrySelector" />
+        <div slot="footer">
+          <div class="j-button-group height40">
+            <button class="j-button-cancel" @click="popupReset('industry')">
+              重置
+            </button>
+            <button class="j-button-confirm" @click="popupConfirm('industry')">
+              确定
+            </button>
+          </div>
+        </div>
+      </PopupLayout>
+    </van-popup>
+  </van-dropdown-menu>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import { DropdownMenu, DropdownItem, Cell, Field, Popup, Tag } from 'vant'
+import { JCell, CheckboxGroup, AppIcon } from '@/ui'
+import PopupLayout from '@/components/common/PopupLayout'
+import DropdownLayout from '@/components/common/DropdownLayout'
+import ScopeSelector from '@/components/selector/scope/index'
+//旧的地区二级选择器
+// import AreaCitySidebar from '@/components/selector/area-city-sidebar/index'
+// 地区三级选择器
+import AreaCitySidebar from '@/components/selector/area-three-sidebar/index'
+import LingYuThreeSidebar from './lingyu/'
+import AreaSelectorContent from '@/components/selector/area'
+import IndustrySidebar from '@/components/selector/industry-sidebar/index'
+import InfoTypeOneClassify from '@/components/selector/info-type-one-classify/index'
+import BuyerClassSidebar from '@/components/selector/buyer-class-sidebar/index'
+import MoneyInputGroup from '@/components/selector/money-input-group'
+import DateTimeList from '@/components/selector/date-time-list'
+import KeywordsInputGroup from '@/components/selector/keyword-input-group/index'
+import ScrollNav from '@/components/common/ScrollNav.vue'
+import { deepCompare } from '@/utils'
+import { throttle } from 'lodash'
+import { calcAreaText } from '@/utils/comput/'
+import {
+  biddingSearchTime,
+  biddingSearchFileExists,
+  searchModeList
+} from '@/data'
+
+export default {
+  name: 'BiddingFilters',
+  components: {
+    [Field.name]: Field,
+    [Tag.name]: Tag,
+    [Cell.name]: Cell,
+    [Popup.name]: Popup,
+    [DropdownMenu.name]: DropdownMenu,
+    [DropdownItem.name]: DropdownItem,
+    PopupLayout,
+    DropdownLayout,
+    IndustrySidebar,
+    AreaCitySidebar,
+    AreaSelectorContent,
+    ScopeSelector,
+    LingYuThreeSidebar,
+    BuyerClassSidebar,
+    MoneyInputGroup,
+    CheckboxGroup,
+    DateTimeList,
+    InfoTypeOneClassify,
+    KeywordsInputGroup,
+    AppIcon,
+    JCell,
+    ScrollNav
+  },
+  model: {
+    prop: 'filters',
+    event: 'change'
+  },
+  props: {
+    dropdownMenuMaxHeight: [String, Number],
+    /**
+     * 默认值,用来reset恢复数据
+     */
+    defaultFilterState: {
+      type: Object,
+      default() {
+        return {
+          moreKeywordsMode: {},
+          scope: ['title', 'purchasing'],
+          bmjzzt: '0',
+          industry: {},
+          jfArea: {},
+          projectArea: {},
+          searchMode: ['0'],
+          dateTime: {
+            exact: 'thisyear'
+          },
+          lingyu: {}, // 领域
+          fileExists: ['0'],
+          publisher: ['0'],
+          signUpEndTime: {}
+        }
+      }
+    },
+    // 提交数据
+    filters: {
+      type: Object,
+      default() {
+        return {
+          moreKeywordsMode: {},
+          scope: ['title', 'purchasing'],
+          bmjzzt: '0',
+          industry: {},
+          jfArea: {},
+          projectArea: {},
+          searchMode: ['0'],
+          dateTime: {},
+          lingyu: {}, // 领域
+          fileExists: ['0'],
+          publisher: ['0'],
+          signUpEndTime: {}
+        }
+      }
+    },
+    topSearch: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    }
+  },
+  data() {
+    return {
+      conf: {
+        dropdownTypeList: [
+          'scope',
+          'dateTime',
+          'jfArea',
+          // 'bmjzzt',
+          'more'
+        ],
+        bmjzzzOptions: [
+          {
+            label: '全部',
+            key: '0'
+          },
+          {
+            label: '未截止',
+            key: '1'
+          },
+          {
+            label: '已截止',
+            key: '2'
+          }
+        ],
+        scope: [
+          {
+            label: '标题',
+            key: 'title'
+          },
+          {
+            label: '标的名称',
+            key: 'purchasing'
+          }
+        ],
+        dateTime: biddingSearchTime,
+        signUpEndTimeOptions: [
+          {
+            label: '全部',
+            key: 'all'
+          },
+          {
+            label: '今天',
+            key: 'today'
+          },
+          {
+            label: '明天',
+            key: 'tomorrow'
+          },
+          {
+            label: '未来7天',
+            key: 'next7days'
+          },
+          {
+            label: '未来30天',
+            key: 'next30days'
+          },
+          {
+            label: '自定义',
+            key: 'exact'
+          }
+        ],
+        checkboxGroup: {
+          icon: false,
+          multiple: false
+        },
+        fileExists: biddingSearchFileExists,
+        publisherList: [
+          {
+            label: '全部',
+            key: '0'
+          },
+          {
+            label: '用户发布',
+            key: '1'
+          },
+          {
+            label: '平台发布',
+            key: '2'
+          }
+        ],
+        searchModeOptions: searchModeList
+      },
+      popup: {
+        jfArea: false, // 交付地点
+        lingyu: false,
+        projectArea: false,
+        industry: false
+      },
+      // 选择器对应的数据
+      cacheMoreFilters: {
+        moreKeywordsMode: {},
+        industry: {},
+        jfArea: {},
+        projectArea: {},
+        searchMode: ['0'],
+        dateTime: {},
+        lingyu: {},
+        bmjzzt: '0',
+        fileExists: ['0'],
+        publisher: ['0'],
+        signUpEndTime: []
+      },
+      moreLeftMenu: [
+        {
+          label: '多关键词',
+          free: true
+        },
+        {
+          label: '搜索模式',
+          free: true
+        },
+        // {
+        //   label: '领域',
+        //   free: true
+        // },
+        // {
+        //   label: '报名截止日期',
+        //   free: true
+        // },
+        // {
+        //   label: '交付地点',
+        //   free: true
+        // },
+        {
+          label: '项目地区',
+          free: true
+        },
+        {
+          label: '行业',
+          free: true
+        },
+        {
+          label: '附件',
+          free: true
+        },
+        {
+          label: '发布方',
+          free: true
+        }
+      ],
+      scrollEleTop: 0,
+      markClickScroll: false
+    }
+  },
+  computed: {
+    ...mapGetters('user', ['isLogin', 'isFree', 'isWhiteList']),
+    noLoginOrFree() {
+      if (this.isLogin) {
+        return this.isFree
+      } else {
+        return true
+      }
+    },
+    getScopeOptions() {
+      const { scope: list } = this.conf
+      if (this.isLogin) {
+        return list
+      } else {
+        return list.filter((s) => !s.disabled)
+      }
+    },
+    getTimeOptions() {
+      const list = this.conf.dateTime
+      if (this.isLogin) {
+        return list
+      } else {
+        return list.filter((s) => !s.disabled)
+      }
+    },
+    valueTextWithLingyu() {
+      const { lingyu } = this.cacheMoreFilters
+      if (lingyu && Object.keys(lingyu).length > 0) {
+        let arr = []
+        for (const key in lingyu) {
+          const item = lingyu[key]
+          if (item) {
+            for (const k2 in item) {
+              if (item[k2].length > 0) {
+                arr = arr.concat(item[k2])
+              } else {
+                arr.push(k2)
+              }
+            }
+          }
+        }
+        const text = arr.join('、')
+        return `已选:${text}`
+      } else {
+        return '已选:全部'
+      }
+    },
+    valueTextWithJfArea() {
+      const text = calcAreaText(this.cacheMoreFilters.jfArea)
+      return `已选:${text}`
+    },
+    valueTextWithProjectArea() {
+      const { projectArea } = this.cacheMoreFilters
+      const text = calcAreaText(projectArea)
+      return `已选:${text}`
+    },
+    valueTextWithIndustry() {
+      const { industry } = this.cacheMoreFilters
+      if (Object.keys(industry).length) {
+        // 取所有二级分类的长度
+        const level2 = [].concat(Object.values(industry)).flat()
+        return `已选:${level2.length}个`
+      } else {
+        return '已选:全部'
+      }
+    },
+    calcMoreLeftMenu() {
+      return this.moreLeftMenu.map((v) => {
+        const { label, ref, free } = v
+        return {
+          label,
+          ref,
+          // vip: free ? false : this.noLoginOrFree
+          vip: false
+        }
+      })
+    }
+  },
+  beforeDestroy() {
+    this.$refs.dropdownMenu.$children.forEach((child) => {
+      const wrapper = child.$refs?.wrapper
+      try {
+        wrapper && wrapper.remove()
+        child.remove()
+        child.$destroy()
+      } catch (e) {
+        console.warn(e)
+      }
+    })
+    try {
+      const scrollEle = document.querySelector('.scroll-nav-right')
+      if (scrollEle) {
+        scrollEle.removeEventListener('scroll', this.handleScroll)
+      }
+    } catch (error) {
+      console.log(error)
+    }
+  },
+  created() {
+    window.t = this
+  },
+  methods: {
+    getContainer() {
+      return this.$root.$el.querySelector('.search-result-bidding')
+    },
+    closePopup(type) {
+      if (type !== -1) {
+        this.$refs[`${type}Dropdown`]?.toggle(false)
+      } else {
+        const dropdown = this.conf.dropdownTypeList
+        dropdown.forEach((type) => {
+          this.closePopup(type)
+        })
+      }
+    },
+    confirmAll(type) {
+      if (type !== -1) {
+        this.onConfirm(type)
+      } else {
+        const dropdown = this.conf.dropdownTypeList
+        dropdown.forEach((type) => {
+          this.onConfirm(type)
+        })
+      }
+    },
+    dropdownMenuHighlight(type) {
+      const className = ['highlight']
+      let needHighlight = false
+      switch (type) {
+        case 'scope': {
+          const { scope } = this.filters
+          const { scope: scopeDefault } = this.defaultFilterState
+          const same = deepCompare(scope, scopeDefault)
+          needHighlight = !same
+          break
+        }
+        case 'dateTime': {
+          const { dateTime } = this.filters
+          const { dateTime: dateTimeDefault } = this.defaultFilterState
+          const same = deepCompare(dateTime?.exact, dateTimeDefault?.exact)
+          needHighlight = !same
+          break
+        }
+        case 'bmjzzt': {
+          const { bmjzzt } = this.filters
+          const { bmjzzt: bmjzztDefault } = this.defaultFilterState
+          const same = deepCompare(bmjzzt, bmjzztDefault)
+          needHighlight = !same
+          break
+        }
+        case 'jfArea': {
+          const { jfArea } = this.filters
+          const { jfArea: jfAreaDefault } = this.defaultFilterState
+          const same = deepCompare(jfArea, jfAreaDefault)
+          needHighlight = !same
+          break
+        }
+        case 'more': {
+          const {
+            moreKeywordsMode,
+            searchMode,
+            lingyu,
+            projectArea,
+            fileExists,
+            publisher,
+            signUpEndTime,
+            jfArea,
+            industry
+          } = this.defaultFilterState
+          const sameList = []
+          // 更多关键词
+          if (this.filters.moreKeywordsMode?.wordsMode) {
+            // this.filters.moreKeywordsMode对象在未知地方没有wordsMode属性会设置一个默认值wordsMode = '0'
+            // 所以这里对比的时候moreKeywordsMode也需要加一个,否则会永远不相等
+            moreKeywordsMode.wordsMode = '0'
+          }
+          sameList.push(
+            deepCompare(this.filters.moreKeywordsMode, moreKeywordsMode)
+          )
+
+          // 搜索模式
+          sameList.push(deepCompare(this.filters.searchMode, searchMode))
+
+          // 时间
+          // sameList.push(
+          //   deepCompare(this.filters?.dateTime?.exact, dateTime?.exact)
+          // )
+
+          // 信息类型
+          sameList.push(deepCompare(this.filters.lingyu, lingyu))
+          // 采购单位类型
+          sameList.push(deepCompare(this.filters.projectArea, projectArea))
+          // 联系方式
+          sameList.push(deepCompare(this.filters.signUpEndTime, signUpEndTime))
+
+          // sameList.push(deepCompare(this.filters.jfArea, jfArea))
+          // 附件
+          sameList.push(
+            this.filters.fileExists.toString() === fileExists.toString()
+          )
+          sameList.push(
+            deepCompare(this.filters.publisher, publisher)
+          )
+          // 行业
+          sameList.push(deepCompare(this.filters.industry, industry))
+          // console.log(
+          //   sameList,
+          //   'sameList',
+          //   this.filters.moreKeywordsMode,
+          //   moreKeywordsMode
+          // )
+          needHighlight = sameList.includes(false)
+          break
+        }
+      }
+      return needHighlight ? className.join(' ') : ''
+    },
+    doOpen(type) {
+      this.$emit('open', type)
+    },
+    // 从filters中恢复选择器状态
+    openedDropDown(type) {
+      switch (type) {
+        case 'scope': {
+          const { scopeSelector } = this.$refs
+          const { scope } = this.filters
+          scopeSelector?.setState(scope)
+          break
+        }
+        case 'dateTime': {
+          const { dateTimeGroup } = this.$refs
+          const { dateTime } = this.filters
+          this.cacheMoreFilters.dateTime = dateTime
+          dateTimeGroup?.setState(dateTime)
+          break
+        }
+        case 'bmjzzt': {
+          const { bmjzzt } = this.filters
+          this.cacheMoreFilters.bmjzzt = bmjzzt
+          break
+        }
+        case 'jfArea': {
+          const { jfArea } = this.filters
+          this.cacheMoreFilters.jfArea = jfArea
+          break
+        }
+        case 'more': {
+          const { dateTimeGroup, moreKeywordsGroup, signUpEndTimeGroup } = this.$refs
+          const {
+            moreKeywordsMode,
+            searchMode,
+            dateTime,
+            lingyu,
+            projectArea,
+            fileExists,
+            publisher,
+            signUpEndTime,
+            jfArea,
+            industry
+          } = this.filters
+          this.$nextTick(() => {
+            const scrollEle = document.querySelector('.scroll-nav-right')
+            scrollEle.addEventListener('scroll', this.handleScroll)
+          })
+          // 更多关键词
+          this.cacheMoreFilters.moreKeywordsMode = moreKeywordsMode
+          try {
+            moreKeywordsGroup?.setState(moreKeywordsMode)
+          } catch (error) {}
+
+          // 匹配模式
+          this.cacheMoreFilters.searchMode = searchMode
+
+          // 时间
+          this.cacheMoreFilters.dateTime = dateTime
+          dateTimeGroup?.setState(dateTime)
+
+          // 报名截止时间
+          this.cacheMoreFilters.signUpEndTime = signUpEndTime
+          signUpEndTimeGroup?.setState(signUpEndTime)
+
+          // 信息类型
+          this.cacheMoreFilters.lingyu = lingyu
+          // 采购单位类型
+          this.cacheMoreFilters.projectArea = projectArea
+          this.cacheMoreFilters.jfArea = jfArea
+
+          this.cacheMoreFilters.fileExists = fileExists
+          this.cacheMoreFilters.publisher = publisher
+
+          // 行业
+          this.cacheMoreFilters.industry = industry
+          break
+        }
+      }
+    },
+    // 重置选择器状态
+    resetSelectors(type) {
+      const filters = {}
+      switch (type) {
+        case 'scope': {
+          const { scope } = this.defaultFilterState
+          const { scopeSelector } = this.$refs
+          if (Array.isArray(scope)) {
+            filters.scope = scope
+            scopeSelector?.setState(scope)
+          }
+          break
+        }
+        case 'dateTime': {
+          const { dateTimeGroup } = this.$refs
+          const { dateTime } = this.defaultFilterState
+          filters.dateTime = dateTime
+          // this.cacheMoreFilters.dateTime = dateTime
+          dateTimeGroup?.setState(dateTime)
+          break
+        }
+        case 'bmjzzt': {
+          const { bmjzzt } = this.defaultFilterState
+          if (bmjzzt) {
+            filters.bmjzzt = bmjzzt
+            this.cacheMoreFilters.bmjzzt = bmjzzt
+          }
+          break
+        }
+        case 'jfArea': {
+          const { jfArea } = this.defaultFilterState
+          if (jfArea) {
+            filters.jfArea = jfArea
+            this.cacheMoreFilters.jfArea = jfArea
+          }
+          break
+        }
+        case 'more': {
+          const { signUpEndTimeGroup } = this.$refs
+          const {
+            moreKeywordsMode,
+            searchMode,
+            lingyu,
+            projectArea,
+            fileExists,
+            signUpEndTime,
+            jfArea,
+            publisher,
+            industry
+          } = this.defaultFilterState
+          // 更多关键词
+          if (moreKeywordsMode) {
+            this.cacheMoreFilters.moreKeywordsMode = moreKeywordsMode
+            filters.moreKeywordsMode = moreKeywordsMode
+          }
+
+          // 匹配模式
+          if (Array.isArray(searchMode)) {
+            this.cacheMoreFilters.searchMode = searchMode
+            filters.searchMode = searchMode
+          }
+
+          // 领域
+          if (lingyu) {
+            filters.lingyu = lingyu
+            this.cacheMoreFilters.lingyu = lingyu
+          }
+          if (jfArea) {
+            filters.jfArea = jfArea
+            this.cacheMoreFilters.jfArea = jfArea
+          }
+          if (projectArea) {
+            filters.projectArea = projectArea
+            this.cacheMoreFilters.projectArea = projectArea
+          }
+
+          // 报名截止时间
+          this.cacheMoreFilters.signUpEndTime = signUpEndTime
+          signUpEndTimeGroup?.setState(signUpEndTime)
+          filters.signUpEndTime = signUpEndTime
+
+          if (Array.isArray(fileExists)) {
+            filters.fileExists = fileExists
+            this.cacheMoreFilters.fileExists = fileExists
+          }
+          if (Array.isArray(publisher)) {
+            filters.publisher = publisher
+            this.cacheMoreFilters.publisher = publisher
+          }
+          
+          // 行业
+          if (industry) {
+            filters.industry = industry
+            this.cacheMoreFilters.industry = industry
+          }
+          break
+        }
+      }
+      return filters
+    },
+    resetModelChange(type) {
+      const filters = this.resetSelectors(type)
+      this.onChange(filters)
+    },
+    onReset(type) {
+      this.resetModelChange(type)
+      this.$emit('reset')
+      this.$refs[`${type}Dropdown`]?.toggle(false)
+    },
+    confirmSelectors(type) {
+      const filters = {}
+      switch (type) {
+        case 'scope': {
+          const { scopeSelector } = this.$refs
+          const state = scopeSelector?.getState()
+          filters.scope = state
+          break
+        }
+        case 'dateTime': {
+          const { dateTimeGroup } = this.$refs
+          const state = dateTimeGroup?.getState()
+          filters.dateTime = state
+          break
+        }
+        case 'bmjzzt': {
+          const { bmjzzt } = this.cacheMoreFilters
+          filters.bmjzzt = bmjzzt
+          break
+        }
+        case 'jfArea': {
+          const { jfArea } = this.cacheMoreFilters
+          filters.jfArea = jfArea
+          break
+        }
+        case 'more': {
+          const { signUpEndTimeGroup } = this.$refs
+          const {
+            moreKeywordsMode,
+            searchMode,
+            lingyu,
+            projectArea,
+            fileExists,
+            publisher,
+            jfArea,
+            industry
+          } = this.cacheMoreFilters
+          // 更多关键词
+          filters.moreKeywordsMode = moreKeywordsMode
+
+          // 匹配模式
+          filters.searchMode = searchMode
+
+          // 报名截止时间
+          const signUpEndTime = signUpEndTimeGroup?.getState()
+          filters.signUpEndTime = signUpEndTime || {}
+
+          // 信息类型
+          filters.lingyu = lingyu
+          // 采购单位类型
+          filters.projectArea = projectArea
+          filters.fileExists = fileExists
+          filters.publisher = publisher
+          filters.industry = industry
+          filters.jfArea = jfArea
+          break
+        }
+      }
+      return filters
+    },
+    confirmModelChange(type) {
+      const filters = this.confirmSelectors(type)
+      this.onChange(filters)
+    },
+    onConfirm(type) {
+      this.confirmModelChange(type)
+      this.$emit('confirm', { type })
+      if (type === 'more') {
+        const moreKeywordsMode =
+          !this.cacheMoreFilters.moreKeywordsMode ||
+          !Object.keys(this.cacheMoreFilters.moreKeywordsMode).length
+        // 需求:如用户未输入关键词(顶部搜索框及多关键词输入框都未输入关键词)且非白名单用户,则toast提示“请输入关键词”并锚点定位至“多关键词”的位置处
+        if (moreKeywordsMode && !this.topSearch.input && !this.isWhiteList) {
+          this.navTabClick(0)
+          return this.$toast('请输入关键词')
+        }
+      }
+      this.$refs[`${type}Dropdown`]?.toggle(false)
+    },
+    onBmjzztChange(e) {
+      const r = Object.assign({}, this.cacheMoreFilters, { bmjzzt: e })
+      this.onChange(r)
+    },
+    onChange(value = {}) {
+      const filters = {}
+      Object.assign(filters, JSON.parse(JSON.stringify(this.filters)), value)
+      console.log(JSON.stringify(filters))
+      this.$emit('change', filters)
+    },
+    popupState(type, state = false) {
+      this.popup[type] = state
+    },
+    showPopup(type) {
+      this.popupState(type, true)
+      // 恢复数据
+      switch (type) {
+        case 'lingyu': {
+          const { lingYuThreeSidebar } = this.$refs
+          const { lingyu } = this.cacheMoreFilters
+          lingYuThreeSidebar?.setState(lingyu)
+          break
+        }
+        case 'jfArea': {
+          const { jfArea } = this.cacheMoreFilters
+          if (jfArea) {
+            const { jfAreaSelector } = this.$refs
+            jfAreaSelector?.setState(jfArea)
+          }
+          break
+        }
+        case 'projectArea': {
+          const { projectAreaSelector } = this.$refs
+          const { projectArea } = this.cacheMoreFilters
+          projectAreaSelector?.setState(projectArea)
+          break
+        }
+        case 'industry': {
+          const { industrySelector } = this.$refs
+          const { industry } = this.cacheMoreFilters
+          industrySelector?.setState(industry)
+          break
+        }
+      }
+    },
+    popupReset(type) {
+      switch (type) {
+        case 'lingyu': {
+          const { lingYuThreeSidebar } = this.$refs
+          const { lingyu } = this.defaultFilterState
+          this.cacheMoreFilters.lingyu = lingyu
+          lingYuThreeSidebar?.setState(lingyu)
+          break
+        }
+        case 'jfArea': {
+          const { jfAreaSelector } = this.$refs
+          const { jfArea } = this.defaultFilterState
+          jfAreaSelector?.setState(jfArea)
+          break
+        }
+        case 'projectArea': {
+          const { projectAreaSelector } = this.$refs
+          const { projectArea } = this.defaultFilterState
+          this.cacheMoreFilters.projectArea = projectArea
+          projectAreaSelector?.setState(projectArea)
+          break
+        }
+        case 'industry': {
+          const { industrySelector } = this.$refs
+          const { industry } = this.defaultFilterState
+          this.cacheMoreFilters.industry = industry
+          industrySelector?.setState(industry)
+          break
+        }
+      }
+      this.popupState(type, false)
+    },
+    popupConfirm(type) {
+      switch (type) {
+        case 'lingyu': {
+          const { lingYuThreeSidebar } = this.$refs
+          const state = lingYuThreeSidebar?.getState()
+          this.$set(this.cacheMoreFilters, 'lingyu', state)
+          break
+        }
+        case 'jfArea': {
+          const { jfAreaSelector } = this.$refs
+          const state = jfAreaSelector?.getState()
+          this.$set(this.cacheMoreFilters, 'jfArea', state)
+          break
+        }
+        case 'projectArea': {
+          const { projectAreaSelector } = this.$refs
+          const state = projectAreaSelector?.getState()
+          this.$set(this.cacheMoreFilters, 'projectArea', state)
+          break
+        }
+        case 'industry': {
+          const { industrySelector } = this.$refs
+          const state = industrySelector?.getState()
+          this.$set(this.cacheMoreFilters, 'industry', state)
+          break
+        }
+      }
+      this.popupState(type, false)
+    },
+    async showSearchModeHelp() {
+      return await this.$dialog.alert({
+        title: '搜索模式',
+        messageAlign: 'left',
+        className: 'j-confirm-dialog',
+        confirmButtonText: '我知道了',
+        message:
+          '<p>精准搜索: 搜索结果必须完全包含完整的关键词。如搜索"医疗设备" ,搜索结果一定完整包含“医疗设备”才能被搜索到,而“医疗的设备”或“设备医疗”的项目不会被搜索到。</p><br /><p>模糊搜索: 系统会先自动智能分词然后再进行搜索。如搜索"医疗设备" ,系统会自动分成“医疗”“设备”然后进行搜索,只要项目中出现“医疗”和“设备”都会被搜索到,前提是两个词必须一同出现在一则公告内,不分先后顺序。</p>'
+      })
+    },
+    onNoPower() {
+      this.$emit('noPower')
+    },
+    checkboxGroupClick(item) {
+      if (item.disabled) {
+        this.onNoPower()
+      }
+    },
+    onScrollTargetElement(data) {
+      const { index } = data
+      this.navTabClick(index)
+    },
+    navTabClick(index) {
+      const cellItems = document.querySelectorAll('.more-filter-item')
+      const scrollEle = document.querySelector('.scroll-nav-right')
+      let total = cellItems[index].offsetTop
+      scrollEle.scrollTop = total
+      this.markClickScroll = true
+      // cellItems[index].scrollIntoView({ behavior: 'smooth' })
+      setTimeout(() => {
+        this.markClickScroll = false
+      }, 300)
+    },
+    handleScroll: throttle(function () {
+      if (this.markClickScroll) return
+      const cellItems = document.querySelectorAll('.more-filter-item')
+      const offsetTopArr = []
+      cellItems.forEach((item) => {
+        offsetTopArr.push(item.offsetTop)
+      })
+      const scrollTop = document.querySelector('.scroll-nav-right')?.scrollTop
+      let navIndex = 0
+      for (let index = 0; index < offsetTopArr.length; index++) {
+        if (scrollTop >= offsetTopArr[index] - 20) {
+          navIndex = index
+        }
+      }
+      this.$refs.scrollNav.setActive(navIndex)
+    }, 300)
+  }
+}
+</script>
+
+
+<style>
+.bmjzzt-container {
+  padding: 0 12px;
+}
+</style>
+<style lang="scss" scoped>
+::v-deep {
+  .van-dropdown-menu__bar {
+    padding: 0 4px;
+  }
+  // .van-dropdown-menu__item {
+  //   justify-content: flex-start;
+  //   &:nth-of-type(1) {
+  //     flex: 3;
+  //   }
+  //   &:nth-of-type(2) {
+  //     flex: 3;
+  //   }
+  //   &:nth-of-type(3) {
+  //     flex: 4;
+  //   }
+  //   &:nth-of-type(4) {
+  //     flex: 3;
+  //   }
+  // }
+  .van-dropdown-menu__title {
+    padding: 0 8px 0;
+    color: $gray_9;
+    &--active {
+      color: $main;
+    }
+  }
+  .highlight {
+    color: $main;
+  }
+  .mr-4 {
+    margin-right: 4px;
+  }
+}
+
+.fix-pop-height {
+  height: 100%;
+  ::v-deep {
+    .j-main,
+    .default-sidebar-height {
+      height: 100%;
+    }
+
+    .area-city-sidebar {
+      .default-sidebar-height {
+        height: calc(60vh - 14vw);
+        max-height: calc(60vh - 14vw);
+      }
+    }
+  }
+}
+
+.popup-sub-title {
+  font-size: 14px;
+  line-height: 20px;
+  color: #5f5e64;
+}
+
+.more-keywords-component {
+  padding: 8px 0;
+}
+.search-mode-filter {
+  .search-mode-label-icon {
+    margin-left: 8px;
+    font-size: 20px;
+    color: $main;
+  }
+  ::v-deep {
+    .cell-title {
+      display: flex;
+      align-items: center;
+      font-size: 12px;
+      font-weight: bold;
+    }
+  }
+}
+
+.scope-tag {
+  margin-left: 8px;
+  color: $main;
+  background-color: $color_main_background;
+}
+
+.one-line-filter-item {
+  padding-top: 15px;
+  padding-bottom: 15px;
+}
+.more-filter-item {
+  ::v-deep {
+    .j-row-cell-content {
+      padding: 12px 0;
+    }
+    .j-row-cell-hd .cell-title,
+    .cell-title-text,
+    .van-cell__title,
+    .search-mode-filter-label-text {
+      font-weight: bold;
+      font-size: 12px;
+    }
+    .van-cell__value {
+      font-size: 11px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      text-align: right;
+    }
+    .icon-help1 {
+      font-size: 16px;
+    }
+    .keywords-input-group {
+      .van-field__control {
+        font-size: 10px;
+      }
+    }
+  }
+  &:not(:last-of-type) {
+    //border-bottom: 1px solid $border_color_3;
+  }
+}
+.v-vip-icon {
+  display: inline-block;
+  width: 14px;
+  height: 14px;
+  background: url(@/assets/image/icon/vip/v_icon.png) no-repeat center;
+  background-size: contain;
+  margin-bottom: 1px;
+}
+</style>

+ 73 - 0
apps/mobile/src/components/search/sun/lingyu/index.vue

@@ -0,0 +1,73 @@
+<template>
+  <ThreeSidebar
+    ref="uThreeSidebar"
+    class="lingyu-three-sidebar"
+    :initSource="initSource"
+    :tag-text-only-selected-count="true"
+    :use-province-city-split="true"
+
+  />
+</template>
+
+<script>
+import ThreeSidebar from '@/components/selector/common-three-sidebar'
+import { lingyuDataSource } from '@/data/selector'
+
+export default {
+  name: 'LingYuThreeSidebar',
+  components: {
+    ThreeSidebar,
+  },
+  data(){
+    return {
+      initSource: []
+    }
+  },
+  created() {
+    this.calcSource()
+  },
+  methods: {
+    calcSource() {
+      const standardData = lingyuDataSource.map((p) => {
+        return {
+          name: p.label,
+          label: p.label,
+          children: p.children.map((c) => {
+            return {
+              name: c.label,
+              label: c.label,
+              children: c.children.map((a) => {
+                return {
+                  name: a.label,
+                  label: a.label
+                }
+              })
+            }
+          })
+        }
+      })
+      this.initSource = standardData
+    },
+    setState(e) {
+      const sidebar = this.$refs.uThreeSidebar
+      sidebar.setState(e)
+    },
+    getState() {
+      const sidebar = this.$refs.uThreeSidebar
+      return sidebar.getState()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.lingyu-three-sidebar {
+  height: 100%;
+  ::v-deep {
+    .default-sidebar-height {
+      height: calc(60vh - 15vw)!important;
+      max-height: calc(60vh - 15vw)!important;
+    }
+  }
+}
+</style>

+ 1007 - 0
apps/mobile/src/components/selector/common-three-sidebar/index.vue

@@ -0,0 +1,1007 @@
+<template>
+  <div class="zhima-three-sidebar j-container">
+    <div class="sidebar-header j-header" v-if="showSearch">
+      <van-search
+        v-model="searchContent"
+        show-action
+        placeholder="输入标签名称搜索"
+        clearable
+        :class="{ focus: inputFocus }"
+        @focus="onFocus"
+        @blur="onBlur"
+        @clear="onSearchClear"
+        @input="onSearch"
+      >
+        <template #left-icon>
+          <i class="j-icon j-base-icon icon-big-search"></i>
+        </template>
+        <template #action>
+          <div @click="onSearch">搜索</div>
+        </template>
+      </van-search>
+    </div>
+    <div class="sidebar-content j-main">
+      <div v-show="searchEmpty" class="empty-container">暂无相关选项</div>
+      <div v-show="!searchEmpty" class="sidebar-content-list">
+        <SideBar
+          :key="refreshKey"
+          ref="firstSidebar"
+          class="area-city-sidebar-selector area-three-sidebar first-sidebar"
+          :height="height"
+          :class="{
+            'province-city-split': useProvinceCitySplit
+          }"
+          :when-parent-level0-selected="whenParentLevel0Selected"
+          :source-list="provinceList"
+          :children-add-all="childrenAddAll"
+          :children-add-text="childrenAddText"
+          :before-child-change="beforeChildChange"
+          :show-parent-when-children-empty="false"
+          :lazy-render="lazyRender"
+          @onChange="onChangeFirst"
+          @rendered="rendered"
+        >
+          <template #tab-tag="{ parent }">
+            <slot name="tab-tag" :parent="parent">
+              <van-tag
+                v-if="
+                  parent.value &&
+                  sourceFirstCount[parent.value] &&
+                  sourceFirstCount[parent.value]._children_selectedCount
+                "
+                round
+                type="primary"
+                :class="{
+                  visible:
+                    sourceFirstCount[parent.value]._children_selectedCount === 0
+                }"
+              >
+                {{ sourceFirstCount[parent.value]._children_selectedCount }}
+              </van-tag>
+            </slot>
+          </template>
+          <template #tab-content="{ parent }">
+            <!-- 嵌套的 side-bar 组件 -->
+            <SideBar
+              :ref="`secondSidebar_${parent.id}` || ''"
+              class="area-city-sidebar-selector second-sidebar"
+              :class="{
+                'disabled-city': disabledCitySelect,
+                'province-city-split': useProvinceCitySplit
+              }"
+              :height="height"
+              :children-add-all="childrenAddAll"
+              :children-add-text="childrenAddText"
+              :source-list="parent.children"
+              :before-parent-change="beforeParentChange"
+              :before-child-change="beforeChildChange"
+              :show-parent-when-children-empty="false"
+              @onChange="onChange($event, parent.value)"
+            >
+              <template #tab-item="{ parent }">
+                <div
+                  v-show="!isGAT(parent.name)"
+                  class="title-name"
+                  v-html="parent.name"
+                />
+                <div v-show="!isGAT(parent.name)" class="title-text">
+                  <slot name="tab-tag" :parent="parent">
+                    <van-tag
+                      v-if="
+                        tagTextOnlySelectedCount &&
+                        parent._children_selectedCount
+                      "
+                      round
+                      type="primary"
+                    >
+                      {{ parent._children_selectedCount }}
+                    </van-tag>
+                  </slot>
+                </div>
+              </template>
+              <template #default="{ parent, child }">
+                <slot name="default" v-bind="{ parent, child }">
+                  <template
+                    v-if="
+                      parent.level !== 0 &&
+                      child.level === 0 &&
+                      useProvinceCitySplit
+                    "
+                  >
+                    <van-cell is-link :border="false">
+                      <span v-html="child.name" />
+                      <template #right-icon>
+                        <span
+                          class="j-icon checkbox"
+                          :class="{ checked: child._selected }"
+                        />
+                      </template>
+                    </van-cell>
+                    <div class="children-title border-line-b" @click.stop>
+                      <div class="c-title-text">市级</div>
+                      <van-tag
+                        v-if="disabledCitySelect"
+                        round
+                        plain
+                        type="danger"
+                        @click="disabledCityClick"
+                      >
+                        开通
+                      </van-tag>
+                    </div>
+                  </template>
+                </slot>
+              </template>
+            </SideBar>
+          </template>
+        </SideBar>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Cell, Search, Tag } from 'vant'
+import SideBar from '@/ui/sidebar-selector'
+import { getRandomString } from '@/utils/'
+
+export default {
+  name: 'AreaThreeSidebar',
+  components: {
+    [Cell.name]: Cell,
+    [Tag.name]: Tag,
+    [Search.name]: Search,
+    SideBar
+  },
+  props: {
+    height: {
+      type: String,
+      default: ''
+    },
+    value: {
+      type: [Object, undefined, null],
+      default: undefined
+    },
+    initSource: {
+      type: [Array],
+      default: () => []
+    },
+    showSearch: {
+      type: Boolean,
+      default: false
+    },
+    lazyRender: {
+      type: Boolean,
+      default: false
+    },
+    /**
+     * 省份城市布局分割
+     */
+    useProvinceCitySplit: {
+      type: Boolean,
+      default: false
+    },
+    /**
+     * 城市选择禁用
+     */
+    disabledCitySelect: {
+      type: Boolean,
+      default: false
+    },
+    /**
+     * 数据统计tag显示
+     */
+    tagTextOnlySelectedCount: {
+      type: Boolean,
+      default: false
+    },
+    /**
+     * 二级子项 "全部" 文字的替换
+     */
+    childrenAddText: String,
+    formatSourceData: Function,
+    /**
+     * 更改确认函数
+     *
+     * 如果传入该函数,点击二级子项会触发,返回true会确认成功,返回false则会阻止更改
+     */
+    beforeChange: Function
+  },
+  data() {
+    return {
+      refreshKey: 0,
+      searchContent: '',
+      inputFocus: false,
+      cacheState: {},
+      whenParentLevel0Selected: false,
+      provinceListMap: {
+        // A: [
+        //     {
+        //         name: '安徽',
+        //         expanded: false,
+        //         canExpanded: true,
+        //         selectedState: '',
+        //         children: []
+        //     }
+        // ]
+      },
+      provinceList: [],
+      // 懒加载下,加载了哪些
+      renderedList: [],
+      // indexBar数据
+      indexList: [],
+      provinceExp: {
+        name: '安徽',
+        value: '',
+        // 展开状态
+        expanded: false,
+        // 是否可以展开
+        canExpanded: false,
+        children: []
+      },
+      sourceFirstCount: {},
+      // 全国二级菜单名
+      allCountryRefName: null,
+      // 二级菜单ref名
+      secondRefNameObj: {}
+    }
+  },
+  computed: {
+    // 懒加载下渲染的省份详细信息列表
+    renderedInfoList() {
+      return this.provinceList.filter((p) => {
+        return this.renderedList.includes(p.name)
+      })
+    },
+    childrenAddAll() {
+      return !this.searchContent
+    },
+    showedProvinceList() {
+      return this.provinceList.filter((v) => !v.tabHide)
+    },
+    // 是否没有搜索到数据
+    searchEmpty() {
+      if (this.searchContent && this.showedProvinceList.length === 0) {
+        return true
+      } else {
+        return false
+      }
+    },
+    calcValue() {
+      return this.value || this.cacheState
+    }
+  },
+  watch: {
+    value: {
+      deep: true,
+      handler(n) {
+        this.setState(n)
+      }
+    }
+  },
+  created() {
+    this.init(this.initSource)
+  },
+  mounted() {
+    this.setState()
+    setTimeout(() => {
+      this.setActiveTab(1)
+    }, 0)
+  },
+  methods: {
+    resetFilter(val = '') {
+      this.searchContent = val
+      this.onSearch()
+    },
+    onFocus() {
+      this.inputFocus = true
+    },
+    onBlur() {
+      this.inputFocus = false
+    },
+    lazyInitData(list) {
+      this.init(list)
+      this.$nextTick(() => {
+        this.refreshKeyValue()
+      })
+    },
+    refreshKeyValue() {
+      this.refreshKey = Date.now()
+      setTimeout(() => {
+        this.setActiveTab(1)
+        this.setState(this.value)
+      }, 0)
+    },
+    syncInput(e) {
+      this.$emit('input', e)
+    },
+    syncCacheState(e) {
+      this.cacheState = e || {}
+    },
+    setActiveTab(num) {
+      const { firstSidebar } = this.$refs
+      firstSidebar.setActiveTab(num)
+    },
+    onSearch() {
+      this.calcMatch(this.searchContent)
+    },
+    onSearchClear() {
+      this.calcMatch(this.searchContent)
+    },
+    calcMatch(searchValue = '') {
+      this.provinceList.forEach((level1) => {
+        // 一级匹配
+        const { searchShow: level1ItemShow } = this.doMatch(searchValue, level1)
+        this.$set(level1, 'tabHide', !level1ItemShow)
+        // 二级匹配
+        level1.children.forEach((level2) => {
+          // 如果一级匹配到了,其子集都要显示
+          const { searchShow: level2ItemShow } = this.doMatch(
+            searchValue,
+            level2
+          )
+          let needHide = !level2ItemShow
+          if (level1ItemShow) {
+            needHide = false
+          }
+          this.$set(level2, 'tabHide', needHide)
+          if (level2ItemShow) {
+            this.$set(level1, 'tabHide', false)
+          }
+
+          level2.children.forEach((level3) => {
+            const { searchShow: level3ItemShow } = this.doMatch(
+              searchValue,
+              level3
+            )
+            let needHide = !level3ItemShow
+            if (level1ItemShow || level2ItemShow) {
+              needHide = false
+            }
+            this.$set(level3, 'tabHide', needHide)
+            if (level3ItemShow) {
+              this.$set(level1, 'tabHide', false)
+              this.$set(level2, 'tabHide', false)
+            }
+          })
+        })
+      })
+
+      this.refreshKeyValue()
+    },
+    doMatch(searchValue, item) {
+      let notAll = true
+      if (searchValue) {
+        notAll = item.label !== '全部'
+      }
+
+      const matched = item.label.includes(searchValue)
+      if (matched && searchValue) {
+        item.name = item.label.replace(
+          searchValue,
+          `<span class="highlight-text">${searchValue}</span>`
+        )
+      } else {
+        item.name = item.label
+      }
+
+      let searchShow = true
+      if (searchValue) {
+        searchShow = notAll && matched
+      }
+
+      return {
+        notAll,
+        matched,
+        searchShow
+      }
+    },
+    /**
+     *  返回需要的数组格式
+     */
+    getStandardData(initData = []) {
+      const format = this.formatSourceData
+      // const standardData = initData.map((p) => {
+      //   return {
+      //     name: p.name,
+      //     label: p.name,
+      //     children: p.children.map((c) => {
+      //       return {
+      //         name: c.name,
+      //         label: c.name,
+      //         children: c.children.map((a) => {
+      //           return {
+      //             name: a,
+      //             label: a
+      //           }
+      //         })
+      //       }
+      //     })
+      //   }
+      // })
+      if (format) {
+        return format(initData)
+      } else {
+        return initData
+      }
+    },
+    // 整理城市数据列表(并初始化indexBar数据)
+    init(uList) {
+      // 整理数据得到List,同时获得indexList
+      const list = this.getStandardData(uList)
+      this.initProvinceList(list)
+    },
+    initProvinceList(list) {
+      const sourceList = JSON.parse(JSON.stringify(list))
+      sourceList.unshift({
+        name: '全部'
+      })
+      const provinceList = []
+      this.secondRefNameObj = {}
+      this.allCountryRefName = null
+
+      sourceList.forEach((province) => {
+        if (province.name === '全部') {
+          province.level = 0
+        } else {
+          province.level = 1
+        }
+        const cities = []
+        if (Array.isArray(province.children)) {
+          province.children.forEach((city) => {
+            // 将区县数据取出,处理成新数组,放入城市
+            let districtChildren = []
+            if (Array.isArray(city.children)) {
+              districtChildren = city.children.map((dItem) => {
+                return {
+                  topName: province.name,
+                  parentName: city.name,
+                  name: dItem.name,
+                  label: dItem.name,
+                  value: dItem.name,
+                  id: `adi-${getRandomString(8).toLowerCase()}`,
+                  level: 3
+                }
+              })
+            }
+            cities.push({
+              id: `ac-${getRandomString(8).toLowerCase()}`,
+              name: city.name,
+              label: city.name,
+              value: city.name,
+              level: 2,
+              parentName: province.name,
+              sourceChildrenCount: districtChildren.length, // 子集真实数量
+              children: districtChildren // 区县
+            })
+          })
+        }
+        const provinceExp = {
+          id: `ap-${getRandomString(8).toLowerCase()}`,
+          name: province.name,
+          label: province.name,
+          value: province.name,
+          level: province.level,
+          sourceChildrenCount: cities.length, // 子集真实数量
+          children: cities,
+          hasThree: true
+        }
+        provinceList.push(provinceExp)
+        // 记录下二级ref
+        this.$set(
+          this.secondRefNameObj,
+          provinceExp.name,
+          `secondSidebar_${provinceExp.id}`
+        )
+        if (provinceExp.name === '全部') {
+          this.allCountryRefName = `secondSidebar_${provinceExp.id}`
+        }
+      })
+      this.provinceList = provinceList
+    },
+    disabledCityClick() {
+      this.$emit('onDisabledCityClick')
+    },
+    beforeChildChange(parent, child) {
+      if (this.beforeChange) {
+        const otherPayload = {
+          type: 'areaCityThree',
+          componentVm: this
+        }
+        return this.beforeChange(parent, child, otherPayload)
+      }
+      if (this.disabledCitySelect) {
+        if (child.level === 0 && parent.level === 0) {
+          return true
+        } else {
+          this.disabledCityClick()
+          return false
+        }
+      } else {
+        return true
+      }
+    },
+    // 二级城市选择,拦截处理
+    beforeParentChange(level) {
+      if (this.disabledCitySelect) {
+        if (level === 0) {
+          return true
+        } else {
+          this.disabledCityClick()
+          return false
+        }
+      } else {
+        return true
+      }
+    },
+    getState() {
+      if (this.lazyRender) {
+        const { state } = this.getStateWithLazyRender()
+        return state
+      } else {
+        const { state } = this.getStateMore()
+        return state
+      }
+    },
+    /**
+     * 最后输出数据结构
+     * 参数area示例
+     * {
+     *    澳门: {}
+     *    北京: {
+     *      北京: ['朝阳区']
+     *    },
+     *    河南: {
+     *      南阳市: [],
+     *      郑州: ['金水区'],
+     *      洛阳市: ['栾川县','老城区']
+     *    }
+     * }
+     */
+    getStateMore() {
+      // 选中状态
+      const state = {}
+      // 选中数量统计
+      const stateCounter = {}
+      // 所有二级市区遍历
+      for (const refKey in this.secondRefNameObj) {
+        const refName = this.secondRefNameObj[refKey]
+        const renderList = this.$refs[refName]?.renderList || []
+
+        for (let i = 0; i < renderList.length; i++) {
+          const parent = renderList[i]
+          const children = parent.children
+          const provinceName = parent.parentName
+          // 全国选中
+          if (parent.level === 0 && provinceName === '全部') {
+            if (parent._selected) {
+              break
+            }
+            continue
+          }
+          // 当前省份下,全部选中(保存当前省份)
+          if (parent.level === 0 && provinceName !== '全部') {
+            // 市区【全部】和区县【全部】都选中
+            if (parent._selected) {
+              state[provinceName] = {}
+              break
+            }
+            continue
+          }
+          // 当前市全选中(【市区】和【全部】选中)
+          if (parent.level === 2 && parent._selected) {
+            if (!state[provinceName]) {
+              state[provinceName] = {}
+            }
+            this.$set(state[provinceName], parent.value, [])
+            continue
+          }
+          // 区县选择
+          if (Array.isArray(children)) {
+            const childSelected = []
+            for (let j = 0; j < children.length; j++) {
+              const child = children[j]
+              if (child.level === 0) {
+                continue
+              } else {
+                if (child._selected) {
+                  childSelected.push(child.value)
+                }
+              }
+            }
+
+            if (childSelected.length > 0) {
+              if (!state[provinceName]) {
+                state[provinceName] = {}
+              }
+              if (!state[provinceName][parent.value]) {
+                this.$set(state[provinceName], parent.value, [])
+              }
+              state[provinceName][parent.value] = childSelected
+            }
+          }
+        }
+      }
+      return {
+        state,
+        stateCounter
+      }
+    },
+    // 懒加载下的计算
+    getStateWithLazyRender() {
+      const prevValue = JSON.parse(JSON.stringify(this.calcValue))
+      const { state } = this.getStateMore()
+      let newValue = Object.assign({}, prevValue)
+      // 1. 如果新统计数据的在this.value中,则替换。
+      if (Object.keys(state).length > 0) {
+        for (const key in state) {
+          newValue[key] = state[key]
+        }
+      }
+      // 2. 比较老数据是否在新数据中,如果不在,并且被render了,说明被删除了
+      if (Object.keys(prevValue).length > 0) {
+        for (const key in prevValue) {
+          if (!state[key] && this.renderedList.includes(key)) {
+            delete newValue[key]
+          }
+        }
+      }
+
+      if (
+        this.renderedList.includes('全部') &&
+        Object.keys(state).length === 0
+      ) {
+        newValue = {}
+      }
+
+      return {
+        state: newValue
+      }
+    },
+    /**
+     * 设置组件状态
+     * getState中获取的数据能够直接传入进行状态恢复
+     * 全部不选中传入-1
+     */
+    setState(state = {}) {
+      this.syncCacheState(state)
+      const firstSidebar = this.$refs.firstSidebar
+      firstSidebar.setAllState(false)
+      firstSidebar.refreshAllChildrenState(false)
+      if (!state || Object.keys(state).length === 0) {
+        // 重置第二级,并且选中全国
+        this.resetSelect()
+      } else {
+        this.setSidebarState(state)
+        this.$refs[this.allCountryRefName]?.setParentLevel0State(false)
+      }
+      // 重新计算parent数据统计
+      firstSidebar.refreshAllChildrenState()
+
+      if (this.lazyRender) {
+        // 先清空,在重新计算
+        this.sourceFirstCount = {}
+        // 当开启懒加载后,根据state,计算顶级省份标签数据
+        this.calcSourceFirstCount(state)
+      }
+
+      this.syncInput(state || {})
+    },
+    setSidebarState(state) {
+      if (this.lazyRender) {
+        this.renderedInfoList.forEach((render) => {
+          // 将对应子组件置空
+          this.resetSecondSidebar(render)
+        })
+      }
+      for (const proName in state) {
+        this.setSidebarChildrenState(state, proName)
+      }
+    },
+    setSidebarChildrenState(state, proName) {
+      const refName = this.secondRefNameObj[proName]
+      const refItem = this.$refs[refName]
+      if (!refItem) {
+        return
+      }
+      // 选择了全省
+      const stateProvince = state[proName]
+      // 第一级省份下选中的市区数量
+      let _children_selectedCount = 0
+      refItem.setState((renderList) => {
+        // 选择了全省
+        if (stateProvince && Object.keys(stateProvince).length === 0) {
+          refItem.setAllState(true)
+          refItem.refreshAllChildrenState(true)
+          // 更新第一级 side-bar 的状态
+          _children_selectedCount = renderList.length - 1
+        } else {
+          renderList.forEach((parent) => {
+            // 当前市下选择的区域数组
+            const stateDistrict = stateProvince[parent.value]
+            if (stateDistrict) {
+              if (Array.isArray(stateDistrict)) {
+                _children_selectedCount += 1
+                // 选择了全市
+                if (stateDistrict.length === 0) {
+                  refItem.setChildrenState(parent.children, true)
+                } else {
+                  // 选择了部分区域
+                  parent.children.forEach((child) => {
+                    if (stateDistrict.includes(child.value)) {
+                      child._selected = true
+                    }
+                  })
+                }
+              }
+              refItem.checkChildrenAllChecked(parent, parent.children)
+            }
+          })
+        }
+        // 更新第一级 side-bar 的状态
+        this.$set(this.sourceFirstCount, proName, {
+          _children_selectedCount,
+          _children_count: renderList.length - 1
+        })
+      })
+      refItem.refreshAllChildrenState()
+    },
+    // 重置所有选择
+    resetSelect() {
+      const { firstSidebar } = this.$refs
+      firstSidebar.setAllState(false)
+      firstSidebar.refreshAllChildrenState(false)
+      const firstRenderList = firstSidebar.renderList
+      for (const item of firstRenderList) {
+        this.resetSecondSidebar(item)
+      }
+      this.sourceFirstCount = {}
+    },
+    // 重置某个省份下的市
+    resetSecondSidebar(item) {
+      const secondRef = this.$refs[`secondSidebar_${item.id}`]
+      secondRef?.setAllState(false)
+      secondRef?.refreshAllChildrenState(false)
+      if (item.name === '全部') {
+        secondRef?.setParentLevel0State(true)
+      }
+    },
+    // 处理第一级 side-bar 组件的变化事件
+    onChangeFirst({ parent, child, renderList }) {
+      if (parent.level === 0 && parent.name === '全部') {
+        this.resetSelect()
+      } else {
+        this.$set(this.sourceFirstCount, parent.value, {
+          _children_selectedCount: parent._children_selectedCount,
+          _children_count: parent._children_count
+        })
+        this.$refs.firstSidebar.setParentLevel0State(false)
+      }
+    },
+    // 处理第二级 side-bar 组件的变化事件
+    onChange({ parent, child, renderList }, value) {
+      this.disposeSecondCount({ parent, child, renderList }, value)
+      const stateValue = this.getState()
+      const payload = { parent, child, value: stateValue }
+      this.syncInput(payload.value)
+      console.log(payload)
+      this.$emit('change', payload)
+    },
+    // 处理二级菜单选择
+    disposeSecondCount({ parent, child, renderList }, value) {
+      if (parent.level === 0 && parent.parentName === '全部') {
+        this.resetSelect()
+        return
+      }
+      // 将全国选择重置掉
+      if (this.allCountryRefName) {
+        this.$refs[this.allCountryRefName]?.setParentLevel0State(false)
+      }
+      const selectedNum =
+        renderList?.filter((temp) => temp._children_selectedCount > 0)
+          ?.length || 0
+      const result = {
+        _children_selectedCount: selectedNum,
+        _children_count: renderList.length - 1
+      }
+      // 更新第一级 side-bar 的状态
+      this.$set(this.sourceFirstCount, value, result)
+      this.$refs.firstSidebar.setParentLevel0State(false)
+    },
+    // 是否是港澳台地区
+    isGAT(val) {
+      const GATList = ['香港', '澳门', '台湾']
+      return val && GATList.includes(val)
+    },
+    // 懒加载开始时才会生效,首次渲染时触发
+    rendered({ name, title }) {
+      const state = this.calcValue
+      this.renderedList.push(title)
+      this.renderedCallback(state, { name, title })
+    },
+    renderedCallback(state, { title }) {
+      if (state[title]) {
+        this.setSidebarChildrenState(state, title)
+      }
+    },
+    // 根据省份名称和state,计算子选中个数
+    calcSourceFirstCount(state) {
+      if (!state) return
+      for (const pName in state) {
+        const pd = state[pName]
+        const target = this.getInfoWithNameFromProvinceList(pName)
+        if (target) {
+          const map = {
+            _children_selectedCount: 0,
+            _children_count: target.sourceChildrenCount
+          }
+          if (Object.keys(pd).length > 0) {
+            map._children_selectedCount = Object.keys(pd).length
+          } else {
+            map._children_selectedCount = target.sourceChildrenCount
+          }
+
+          // 处理港澳台子项0的情况
+          for (const key in map) {
+            const c = map[key]
+            if (c <= 0) {
+              map[key] = 1
+            }
+          }
+
+          // 更新第一级 side-bar 的状态
+          this.$set(this.sourceFirstCount, pName, {
+            _children_selectedCount: map._children_selectedCount,
+            _children_count: map._children_count
+          })
+        }
+      }
+    },
+    getInfoWithNameFromProvinceList(name) {
+      return this.provinceList.find((p) => {
+        return p.value === name
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebar-header {
+  ::v-deep {
+    .van-search--show-action {
+      padding-right: 12px;
+    }
+    .van-search__action {
+      color: $main;
+    }
+    .van-search.focus {
+      .van-search__content {
+        border-color: $main;
+      }
+    }
+    .van-search__content {
+      border-radius: 4px;
+      border: 1px solid rgba(0, 0, 0, 0.05);
+    }
+    .van-field {
+      height: 30px;
+      line-height: 22px;
+      font-size: 14px;
+    }
+    .van-field__control::placeholder {
+      font-size: 14px;
+    }
+  }
+}
+
+.province-city-split {
+  ::v-deep {
+    .content-list-item-container {
+      &::after {
+        height: 0;
+      }
+    }
+  }
+  .children-title {
+    display: flex;
+    align-items: center;
+    padding: 8px 16px;
+    font-size: 12px;
+    color: #171826;
+    line-height: 22px;
+    &.border-line-b {
+      &::after {
+        left: 16px;
+        right: 16px;
+      }
+    }
+    .c-title-text {
+      margin-right: 8px;
+    }
+  }
+}
+
+.disabled-city {
+  ::v-deep {
+    .content-list-item-container {
+      opacity: 0.6;
+    }
+    .content-list-item-container.allChildBtn {
+      opacity: 1;
+    }
+    .van-tab .title-name {
+      opacity: 0.6;
+    }
+  }
+}
+.area-city-sidebar-selector {
+  ::v-deep {
+    $fontSize: 13px;
+    .van-tab {
+      padding: 10px 4px 10px 12px;
+      font-size: $fontSize;
+      line-height: 20px;
+      .van-tag {
+        display: inline-block;
+        font-size: 10px;
+        padding: 0 4px;
+        line-height: 14px;
+        min-width: 14px;
+        text-align: center;
+      }
+      .title-name {
+        width: 84px;
+      }
+      .title-list-item {
+        width: 100%;
+        padding-right: 4px;
+      }
+    }
+    .van-cell {
+      padding: 10px 4px 10px 12px;
+      font-size: $fontSize;
+      line-height: 20px;
+      .van-cell__title {
+        margin-right: 10px;
+      }
+      .j-icon {
+        width: 16px;
+        height: 16px;
+      }
+      &.active-cell {
+        .van-cell__title {
+          color: #2abed1;
+        }
+      }
+    }
+    .j-icon.checkbox {
+      width: 14px;
+      height: 14px;
+    }
+    &.first-sidebar {
+      .van-tab {
+        width: 111px;
+        .title-text {
+          margin-left: 2px;
+        }
+      }
+    }
+    &.second-sidebar {
+      .van-tab {
+        width: 111px;
+        .title-text {
+          margin-left: 5px;
+        }
+      }
+      .van-cell {
+        padding-right: 6px;
+      }
+    }
+  }
+}
+
+.empty-container {
+  padding: 12px;
+}
+</style>

+ 31 - 10
apps/mobile/src/components/selector/date-time-group/index.vue

@@ -6,7 +6,7 @@
     <van-field
       class="field-item"
       :value="start.value"
-      :placeholder="start.placeholder"
+      :placeholder="startPlaceholder"
       @click="clickField('start')"
       readonly
     />
@@ -14,7 +14,7 @@
     <van-field
       class="field-item"
       :value="end.value"
-      :placeholder="end.placeholder"
+      :placeholder="endPlaceholder"
       @click="clickField('end')"
       readonly
     />
@@ -27,7 +27,7 @@
       :style="popup.style"
     >
       <PopupLayout
-        title="请选择开始时间"
+        :title="'请选择' + startPlaceholder"
         @closeIconClick="popupShow.start = false"
       >
         <div slot="default">
@@ -63,7 +63,7 @@
       :style="popup.style"
     >
       <PopupLayout
-        title="请选择结束时间"
+        :title="'请选择' + endPlaceholder"
         @closeIconClick="popupShow.end = false"
       >
         <div slot="default">
@@ -107,6 +107,11 @@ export default {
     PopupLayout
   },
   props: {
+    // 最大时间是否可以选择一个未来的时间,默认最大时间为当天
+    endTimeFeature: {
+      type: Boolean,
+      default: false
+    },
     /**
      * 是否高亮
      */
@@ -118,6 +123,14 @@ export default {
       type: Boolean,
       default: false
     },
+    startPlaceholder: {
+      type: String,
+      default: '开始时间'
+    },
+    endPlaceholder: {
+      type: String,
+      default: '结束时间'
+    },
     startTimeRemap: {
       // 初始重置时间
       type: Number,
@@ -129,8 +142,16 @@ export default {
     }
   },
   data() {
+    const now = dayjs()
+    const threeYearLater = now.add(3, 'year');
+    const endMaxDate = this.endTimeFeature ? threeYearLater.toDate() : now.toDate()
+    const startMinDate = new Date(2015, 0, 1)
     return {
       debug: import.meta.env.DEV,
+      conf: {
+        startMinDate: startMinDate,
+        endMaxDate: endMaxDate
+      },
       picker: {
         type: 'date',
         showToolBar: false,
@@ -145,16 +166,16 @@ export default {
         valueStamp: '',
         placeholder: '开始时间',
         currentDate: new Date(),
-        minDate: new Date(2010, 0, 1),
-        maxDate: new Date()
+        minDate: startMinDate,
+        maxDate: endMaxDate
       },
       end: {
         value: '',
         valueStamp: '',
         placeholder: '结束时间',
         currentDate: new Date(),
-        minDate: new Date(2010, 0, 1),
-        maxDate: new Date()
+        minDate: startMinDate,
+        maxDate: endMaxDate
       },
       popupShow: {
         start: false,
@@ -219,9 +240,9 @@ export default {
       }
       // 重置最大值和最小值
       if (type === 'start') {
-        this.end.minDate = new Date(2015, 0, 1)
+        this.end.minDate = this.conf.startMinDate
       } else if (type === 'end') {
-        this.start.maxDate = new Date()
+        this.start.maxDate = this.conf.endMaxDate
       }
       if (type === 'start') {
         this[type].currentDate = this.startTimeRemap

+ 15 - 0
apps/mobile/src/components/selector/date-time-list/index.vue

@@ -12,6 +12,9 @@
     <DateTimeGroup
       v-show="dateTimeGroupActive"
       :highlight="dateTimeGroupActive"
+      :startPlaceholder="startPlaceholder"
+      :endPlaceholder="endPlaceholder"
+      :endTimeFeature="endTimeFeature"
       @change="onChange"
       ref="dateTimeGroup"
     />
@@ -41,6 +44,10 @@ export default {
         return []
       }
     },
+    endTimeFeature: {
+      type: Boolean,
+      default: false
+    },
     options: {
       type: Array,
       default() {
@@ -57,6 +64,14 @@ export default {
         ]
       }
     },
+    startPlaceholder: {
+      type: String,
+      default: '开始时间'
+    },
+    endPlaceholder: {
+      type: String,
+      default: '结束时间'
+    },
     beforeChangeAction: Function,
     defaultSelectedKey: {
       type: String,

+ 160 - 0
apps/mobile/src/components/treasure-box/SunshineGuide.vue

@@ -0,0 +1,160 @@
+<template>
+  <div class="sunshine-guide">
+    <van-overlay
+      :show="show"
+      :custom-style="{ height: 'calc(100% - 13.333vw)', 'z-index': '100' }"
+    >
+      <div class="guide-wrapper" @click.stop>
+        <div class="van-popup--safe-area-inset-bottom sunshine-popup">
+          <div class="sunshine-popup-title">
+            <span class="title">直采</span>
+          </div>
+          <div class="sunshine-popup-content">
+            <div class="action-item">
+              <i class="j-icon j-base-icon icon-sun-info"></i>
+              <span>采购信息</span>
+            </div>
+            <div class="action-item">
+              <i class="j-icon j-base-icon icon-sun-release"></i>
+              <span>采购发布</span>
+            </div>
+            <div class="action-item">
+              <i class="j-icon j-base-icon icon-sun-manage"></i>
+              <span>采购管理</span>
+            </div>
+          </div>
+          <div class="guide-dialog">
+            <div class="guide-dialog-content">
+              海量企业直发采购需求,供应商可直接对接采购部门
+            </div>
+            <div class="guide-dialog-footer">
+              <button class="dialog-button-default" @click="onKnow">
+                我知道了
+              </button>
+              <button class="dialog-button-confirm" @click="onMore">
+                了解更多
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </van-overlay>
+  </div>
+</template>
+
+<script>
+import { Overlay } from 'vant'
+export default {
+  components: {
+    [Overlay.name]: Overlay
+  },
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {}
+  },
+  watch: {
+    show(newVal, oldVal) {
+      console.log(newVal, oldVal)
+    }
+  },
+  methods: {
+    onKnow() {
+      this.$emit('know')
+    },
+    onMore() {
+      this.$emit('more')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.sunshine-guide {
+  .guide-wrapper {
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-end;
+    height: 100%;
+  }
+  .sunshine-popup {
+    position: relative;
+    width: 100%;
+    background: #fff;
+    z-index: 2028;
+  }
+  .sunshine-popup-title {
+    padding: 16px 16px 8px;
+    font-size: 16px;
+    line-height: 24px;
+    color: #171826;
+  }
+  .sunshine-popup-content {
+    display: flex;
+    align-items: center;
+    padding: 8px 16px;
+  }
+  .action-item {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 80px;
+    margin-right: 8px;
+    .j-icon {
+      width: 36px;
+      height: 36px;
+    }
+    span {
+      padding-top: 8px;
+      padding-bottom: 4px;
+      font-size: 11px;
+      line-height: 18px;
+    }
+  }
+  .guide-dialog {
+    position: absolute;
+    top: -130px;
+    right: 10px;
+    width: 275px;
+    height: 180px;
+    padding: 0 38px;
+    background: url(@/assets/image/treasure-box/sun-guide.png) no-repeat center
+      center;
+    background-size: 100% 100%;
+    &-content {
+      padding-top: 68px;
+      font-size: 12px;
+      line-height: 16px;
+      color: #1d1d1d;
+    }
+    &-footer {
+      display: flex;
+      align-items: center;
+      margin-top: 8px;
+      .dialog-button-default,
+      .dialog-button-confirm {
+        flex-shrink: 0;
+        padding: 3px 20px;
+        font-size: 12px;
+        line-height: 22px;
+        border-radius: 4px;
+      }
+      .dialog-button-default {
+        margin-right: 18px;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        color: #5f5e64;
+        background: #fff;
+      }
+      .dialog-button-confirm {
+        color: #fff;
+        background: $color_main;
+        border: 1px solid $color_main;
+      }
+    }
+  }
+}
+</style>

+ 1 - 1
apps/mobile/src/composables/attachment-download/component/AttachmentDownload.vue

@@ -33,7 +33,7 @@
           </template>
         </div>
       </div>
-      <div class="content-file-attachment-actions">
+      <div class="content-file-attachment-actions" v-if="canShowTip">
         <span
           class="action-button"
           @click="chargeFilePack"

+ 5 - 0
apps/mobile/src/data/links.js

@@ -224,5 +224,10 @@ export const LINKS = {
     app: '/jyapp/big/page/report_analysis',
     h5: '/jyapp/big/page/report_analysis',
     wx: '/big/wx/page/report_analysis'
+  },
+  阳光采购落地页: {
+    app: '/jyapp/frontPage/sunshine/free/index',
+    h5: '/jyapp/frontPage/sunshine/free/index',
+    wx: '/weixin/frontPage/sunshine/free/index'
   }
 }

+ 191 - 0
apps/mobile/src/data/selector.js

@@ -432,3 +432,194 @@ export const searchModeList = [
     key: '1'
   }
 ]
+
+// 领域数据源
+export const lingyuDataSource = [
+  {
+    label: '货物',
+    value: '',
+    children: [  
+      {  
+        value: 'A01',  
+        label: '房屋和构筑物',  
+        children: [  
+          { value: 'A01010000', label: '房屋' },  
+          { value: 'A01020000', label: '构筑物' },  
+          { value: 'A01030000', label: '土地' }  
+        ]
+      },  
+      {  
+        value: 'A02',  
+        label: '设备',  
+        children: [  
+          { value: 'A02010000', label: '信息化设备' },  
+          { value: 'A02020000', label: '办公设备' },  
+          { value: 'A02030000', label: '车辆' },  
+          { value: 'A02040000', label: '图书档案设备' },  
+          { value: 'A02050000', label: '机械设备' },  
+          { value: 'A02060000', label: '电气设备' },  
+          { value: 'A02070000', label: '雷达、无线电和卫星导航设备' },  
+          { value: 'A02080000', label: '通信设备' },  
+          { value: 'A02090000', label: '广播、电视、电影设备' },  
+          { value: 'A02100000', label: '仪器仪表' },  
+          { value: 'A02110000', label: '电子和通信测量仪器' },  
+          { value: 'A02120000', label: '计量标准器具及量具、衡器' },  
+          { value: 'A02130000', label: '探矿、采矿、选矿和造块设备' },  
+          { value: 'A02140000', label: '石油天然气开采设备' },  
+          { value: 'A02150000', label: '石油和化学工业设备' },  
+          { value: 'A02160000', label: '炼焦和金属冶炼轧制设备' },  
+          { value: 'A02170000', label: '电力工业设备' },  
+          { value: 'A02180000', label: '非金属矿物制品工业设备' },  
+          { value: 'A02190000', label: '核工业设备' },  
+          { value: 'A02200000', label: '航空航天工业设备' },  
+          { value: 'A02210000', label: '工程机械' },  
+          { value: 'A02220000', label: '农业和林业机械' },  
+          { value: 'A02230000', label: '木材采集和加工设备' },  
+          { value: 'A02240000', label: '食品加工设备' },  
+          { value: 'A02250000', label: '饮料加工设备' },  
+          { value: 'A02260000', label: '烟草加工设备' },  
+          { value: 'A02270000', label: '粮油作物和饲料加工设备' },  
+          { value: 'A02280000', label: '纺织设备' },  
+          { value: 'A02290000', label: '缝纫、服饰、制革和毛皮加工设备' },  
+          { value: 'A02300000', label: '造纸和印刷机械' },  
+          { value: 'A02310000', label: '化学药品和中药设备' },  
+          { value: 'A02320000', label: '医疗设备' },  
+          { value: 'A02330000', label: '电工、电子生产设备' },  
+          { value: 'A02340000', label: '安全生产设备' },  
+          { value: 'A02350000', label: '邮政设备' },  
+          { value: 'A02360000', label: '环境污染防治设备' },  
+          { value: 'A02370000', label: '政法、消防、检测设备' },  
+          { value: 'A02380000', label: '水工机械' },  
+          { value: 'A02390000', label: '货币处理设备' },  
+          { value: 'A02400000', label: '殡葬设备及用品' },  
+          { value: 'A02410000', label: '铁路运输设备' },  
+          { value: 'A02420000', label: '水上交通运输设备' },  
+          { value: 'A02430000', label: '航空器及其配套设备' },  
+          { value: 'A02440000', label: '海洋仪器设备' },  
+          { value: 'A02450000', label: '文艺设备' },  
+          { value: 'A02460000', label: '体育设备设施' },  
+          { value: 'A02470000', label: '娱乐设备' }  
+        ]  
+      },  
+      {  
+        value: 'A03',  
+        label: '文物和陈列品',  
+        children: [  
+          { value: 'A03020000', label: '可移动文物' },  
+          { value: 'A03030000', label: '文创衍生品' },  
+          { value: 'A03040000', label: '标本' },  
+          { value: 'A03050000', label: '模型' }  
+        ]  
+      },  
+      {  
+        value: 'A04',  
+        label: '图书和档案',  
+        children: [  
+          { value: 'A04010000', label: '图书' },  
+          { value: 'A04020000', label: '期刊' },  
+          { value: 'A04030000', label: '资料' },  
+          { value: 'A04040000', label: '档案' }  
+        ]  
+      },  
+      {  
+        value: 'A05',  
+        label: '家具和用具',  
+        children: [  
+          { value: 'A05010000', label: '家具' },  
+          { value: 'A05020000', label: '用具' },  
+          { value: 'A05030000', label: '装具' },  
+          { value: 'A05040000', label: '办公用品' }  
+        ]  
+      },  
+      {  
+        value: 'A06',  
+        label: '特种动植物',  
+        children: [  
+          { value: 'A06010000', label: '特种用途动物' },  
+          { value: 'A06020000', label: '特种用途植物' }  
+        ]  
+      },
+      {  
+        value: 'A07',  
+        label: '物资',  
+        children: [  
+          {  
+            value: 'A07010000',  
+            label: '建筑建材'  
+          },  
+          {  
+            value: 'A07020000',  
+            label: '医药品'  
+          },  
+          {  
+            value: 'A07030000',  
+            label: '农林牧渔业产品'  
+          },  
+          {  
+            value: 'A07040000',  
+            label: '矿与矿物'  
+          },  
+          {  
+            value: 'A07050000',  
+            label: '电力、城市燃气、蒸汽和热水、水'  
+          },  
+          {  
+            value: 'A07060000',  
+            label: '食品、饮料和烟草原料'  
+          },  
+          {  
+            value: 'A07070000',  
+            label: '炼焦产品、炼油产品'  
+          },  
+          {  
+            value: 'A07080000',  
+            label: '基础化学品及相关产品'  
+          },  
+          {  
+            value: 'A07090000',  
+            label: '橡胶、塑料、玻璃和陶瓷制品'  
+          },  
+          {  
+            value: 'A07100000',  
+            label: '纸及纸质品'  
+          }  
+        ]  
+      },  
+      {  
+        value: 'A08',  
+        label: '无形资产',  
+        children: [  
+          {  
+            value: 'A08010000',  
+            label: '专利类无形资产'  
+          },  
+          {  
+            value: 'A08020000',  
+            label: '非专利技术类无形资产'  
+          },  
+          {  
+            value: 'A08030000',  
+            label: '著作权类无形资产'  
+          },  
+          {  
+            value: 'A08040000',  
+            label: '资源资质类无形资产'  
+          },  
+          {  
+            value: 'A08050000',  
+            label: '商标权类无形资产'  
+          },  
+          {  
+            value: 'A08060000',  
+            label: '信息数据类无形资产'  
+          },  
+          {  
+            value: 'A08070000',  
+            label: '经营类无形资产'  
+          }  
+        ]  
+      }  
+    ]
+  }
+]
+

+ 16 - 0
apps/mobile/src/data/service-list.js

@@ -187,6 +187,22 @@ export const serviceListData = [
       }
     ]
   },
+  {
+    阳光直采: [
+      {
+        two: '采购信息',
+        three: '企业公开直采信息,直接对接采购人。',
+        four: '议价',
+        five: '议价'
+      },
+      {
+        two: '采购发布',
+        three: '采购发布成功后,可收到至少5家供应商参与报价。',
+        four: '限时免费',
+        five: '限时免费'
+      }
+    ]
+  },
   {
     人脉管理: [
       {

+ 16 - 0
apps/mobile/src/router/modules/search.js

@@ -42,6 +42,14 @@ const MiddleSearch = {
         title: '采购单位搜索'
       }
     },
+    {
+      path: 'sun',
+      name: 'search-middle-sun',
+      component: () => import('@/views/search/middle/sun/index.vue'),
+      meta: {
+        title: '阳光直采'
+      }
+    },
     {
       path: 'supplier',
       name: 'search-middle-supplier',
@@ -97,6 +105,14 @@ const ResultSearch = {
         title: '招标采购搜索'
       }
     },
+    {
+      path: 'sun',
+      name: 'search-sun',
+      component: () => import('@/views/search/result/sun/index.vue'),
+      meta: {
+        title: '阳光直采'
+      }
+    },
     {
       path: 'company',
       name: 'search-company',

+ 11 - 0
apps/mobile/src/ui/project-cell/index.vue

@@ -55,6 +55,9 @@
             :class="pushSourceTag.className"
             >{{ pushSourceTag.text }}</span
           >
+          <span class="tag-item red" v-if="publishType">
+            <slot name="tag-item">{{ publishType }}</slot>
+          </span>
           <span v-if="isFile && fileText" class="tag-item title-file main">
             <slot>{{ fileText.replace(/[\[\]]/g, '') }}</slot>
           </span>
@@ -163,6 +166,11 @@ export default {
       type: String,
       default: ''
     },
+    // 发布平台标签
+    publishType: {
+      type: String,
+      default: ''
+    },
     ellipsis: {
       type: Boolean,
       default: true
@@ -464,6 +472,9 @@ export default {
     &.red {
       color: $orange_red;
       background-color: $color_red_background;
+      &.border {
+        border: 1px solid $orange_red;
+      }
     }
     &--time {
       color: #9b9ca3;

+ 1 - 0
apps/mobile/src/utils/comput/index.js

@@ -1,3 +1,4 @@
 export * from './view'
 export * from './constant'
 export * from './colors'
+export * from './text'

+ 46 - 0
apps/mobile/src/utils/comput/text.js

@@ -0,0 +1,46 @@
+export function calcAreaText(area = {}, splitter = '、') {
+  if (!area) return
+  if (Object.keys(area).length <= 0) {
+    return '全国'
+  }
+  // 选择全省的
+  const pInfo = {
+    list: [],
+    textList: []
+  }
+  // 选全市的
+  const cInfo = {
+    list: [],
+    textList: []
+  }
+  // 选区的
+  const xInfo = {
+    list: [],
+    textList: []
+  }
+
+  for (const pName in area) {
+    const pObj = area[pName]
+    if (pObj && Object.keys(pObj).length > 0) {
+      for (const cName in pObj) {
+        const cArr = pObj[cName]
+        if (Array.isArray(cArr) && cArr.length > 0) {
+          xInfo.list = xInfo.list.concat(cArr)
+          // const newTextList = cArr.map(item => `${cName}${item}`)
+          const newTextList = cArr.map(item => `${item}`)
+          xInfo.textList = xInfo.textList.concat(newTextList)
+        } else {
+          cInfo.list.push(cName)
+          cInfo.textList.push(`${cName}`)
+        }
+      }
+    } else {
+      pInfo.list.push(pName)
+      pInfo.textList.push(pName)
+    }
+  }
+
+  // console.log(pInfo, cInfo, xInfo)
+  const textArr = [].concat(pInfo.textList).concat(cInfo.textList).concat(xInfo.textList)
+  return textArr.join(splitter)
+}

Деякі файли не було показано, через те що забагато файлів було змінено