فهرست منبع

Merge branch 'feature/v1.0.36' of https://jygit.jydev.jianyu360.cn/jianyu/web into dev/v1.0.36_wmh

wenmenghao 1 سال پیش
والد
کامیت
d9e43c012f
25فایلهای تغییر یافته به همراه889 افزوده شده و 289 حذف شده
  1. 1 0
      apps/bigmember_pc/src/api/modules/index.js
  2. 10 0
      apps/bigmember_pc/src/api/modules/search.js
  3. 10 16
      apps/bigmember_pc/src/components/filter-items/KeywordTagsSelector.vue
  4. 26 16
      apps/bigmember_pc/src/components/filter-items/SearchScopeSelector.vue
  5. 25 20
      apps/bigmember_pc/src/components/selector/SearchTimeScopeSelector.vue
  6. 1 1
      apps/bigmember_pc/src/components/toast/index.js
  7. 12 0
      apps/bigmember_pc/src/store/user.js
  8. 379 0
      apps/bigmember_pc/src/utils/format/search-bid-filter.js
  9. 1 0
      apps/bigmember_pc/src/utils/index.js
  10. 1 1
      apps/bigmember_pc/src/views/search/bidding/components/search-bid-filter.vue
  11. 1 1
      apps/bigmember_pc/src/views/search/bidding/components/search-bid-header.vue
  12. 30 0
      apps/bigmember_pc/src/views/search/bidding/components/search-header-title.vue
  13. 13 1
      apps/bigmember_pc/src/views/search/bidding/constant/search-filters.js
  14. 7 6
      apps/bigmember_pc/src/views/search/bidding/index.vue
  15. 223 0
      apps/bigmember_pc/src/views/search/bidding/model/base.js
  16. 6 184
      apps/bigmember_pc/src/views/search/bidding/model/index.js
  17. 57 0
      apps/bigmember_pc/src/views/search/bidding/model/modules/data-export-actions.js
  18. 1 1
      apps/bigmember_pc/src/views/search/bidding/model/modules/filter-keywords.js
  19. 7 5
      apps/bigmember_pc/src/views/search/bidding/model/modules/filter.js
  20. 50 0
      apps/bigmember_pc/src/views/search/bidding/model/modules/save-filter-actions.js
  21. 10 26
      apps/bigmember_pc/src/views/search/components/search-schema-filter.vue
  22. 1 1
      apps/bigmember_pc/src/views/search/layout/search-list.vue
  23. 7 1
      apps/bigmember_pc/src/views/search/purchase/model/index.js
  24. 3 4
      data/data-models/modules/quick-search/api/search-bid.js
  25. 7 5
      data/data-models/modules/quick-search/plugins/search-bid.js

+ 1 - 0
apps/bigmember_pc/src/api/modules/index.js

@@ -29,3 +29,4 @@ export * from './crmApplication'
 export * from './pay'
 export * from './message'
 export * from './business'
+export * from './search'

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

@@ -0,0 +1,10 @@
+import request from '@/api'
+
+// 检查已存筛选是否pass
+export function checkBiddingFilterPass(data) {
+  return request({
+    url: '/jyapi/jybx/base/checkSearchScreen',
+    method: 'POST',
+    data: data
+  })
+}

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

@@ -7,7 +7,7 @@
   >
     <KeywordTagsSelectorContent
       slot="empty"
-      :list='list'
+      :list='value'
       :placeholder='inputPlaceholder'
       :max-tip='maxTip'
       :disabled='disabled'
@@ -20,15 +20,12 @@
 
 <script>
 import  Layout from '@/components/filter-items/Layout.vue'
-import { Dropdown, DropdownMenu} from 'element-ui'
 import KeywordTagsSelectorContent from './KeywordTagsSelectorContent'
 
 export default {
   components: {
-    [Dropdown.name]: Dropdown,
-    [DropdownMenu.name]: DropdownMenu,
-    KeywordTagsSelectorContent,
-    Layout
+    Layout,
+    KeywordTagsSelectorContent
   },
   props: {
     type: {
@@ -56,12 +53,6 @@ export default {
       type: Number,
       default: 15
     },
-    list: {
-      type: Array,
-      default: () => {
-        return []
-      }
-    },
     // 数组最大长度
     maxListLength: {
       type: Number,
@@ -77,19 +68,22 @@ export default {
       default: () => []
     }
   },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
   data () {
-    return {
-    }
+    return {}
   },
   computed: {
     computedVal () {
-      return  this.list.length ? `${this.placeholder} ${this.list.length || 0}/${this.maxListLength}` : ''
+      return  this.value.length ? `${this.placeholder} ${this.value.length || 0}/${this.maxListLength}` : ''
     }
   },
   created() {},
   methods: {
     keywordTagsChange (list) {
-      this.$emit('onChange', list)
+      this.$emit('change', list)
     }
   }
 }

+ 26 - 16
apps/bigmember_pc/src/components/filter-items/SearchScopeSelector.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="search-scope-selector">
-    <CheckboxGroupSelector v-model="value" :options="options" @change="onChange">
-      <template #tips="{prop}">
+    <CheckboxGroupSelector :value="value" :options="options" @change="onChange">
+      <template #tips="{ prop }">
         <div v-if="prop === 'winner'">
           <span class="old-user-free">老用户免费专享</span>
         </div>
@@ -10,11 +10,21 @@
           placement="right"
           :append-to-body="false"
           width="333"
-          trigger="hover">
+          trigger="hover"
+        >
           <div class="popover-text">
-            <span v-if="prop ==='buyer'"><strong>"采购单位"</strong>采购单位名称中包含输入关键词的会展示出来</span>
-            <span v-if="prop === 'winner'"><strong>"中标企业"</strong>中标企业名称中包含输入关键词的会展示出来</span>
-            <span v-if="prop === 'agency'"><strong>"招标代理机构"</strong>招标代理机构中包含输入关键词的会展示出来</span>
+            <span v-if="prop === 'buyer'"
+              ><strong>"采购单位"</strong
+              >采购单位名称中包含输入关键词的会展示出来</span
+            >
+            <span v-if="prop === 'winner'"
+              ><strong>"中标企业"</strong
+              >中标企业名称中包含输入关键词的会展示出来</span
+            >
+            <span v-if="prop === 'agency'"
+              ><strong>"招标代理机构"</strong
+              >招标代理机构中包含输入关键词的会展示出来</span
+            >
           </div>
           <i class="icon-help-img" slot="reference"></i>
         </el-popover>
@@ -39,13 +49,13 @@ export default {
     [Popover.name]: Popover,
     CheckboxGroupSelector
   },
-  data () {
+  data() {
     return {
       options: searchScopeData
     }
   },
   methods: {
-    onChange (value) {
+    onChange(value) {
       this.$emit('change', value)
     }
   }
@@ -53,10 +63,10 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.search-scope-selector{
+.search-scope-selector {
   display: flex;
   margin: 16px 0;
-  .s-header{
+  .s-header {
     margin-right: 10px;
     min-width: 120px;
     font-size: 14px;
@@ -64,25 +74,25 @@ export default {
     color: #686868;
     text-align: right;
   }
-  ::v-deep{
+  ::v-deep {
     .checkbox-item {
       margin-right: 20px;
     }
-    .popover-text{
-      strong{
+    .popover-text {
+      strong {
         font-weight: bold;
         color: #1d1d1d;
       }
     }
-    .old-user-free{
+    .old-user-free {
       display: inline-block;
       margin-left: 6px;
       padding: 2px 8px;
       border-radius: 2px;
-      background: #EAF8FA;
+      background: #eaf8fa;
       font-size: 12px;
       line-height: 18px;
-      color: #2ABED1;
+      color: #2abed1;
     }
     .icon-help-img {
       display: inline-block;

+ 25 - 20
apps/bigmember_pc/src/components/selector/SearchTimeScopeSelector.vue

@@ -37,37 +37,42 @@ export default {
       selectTime: {
         start: 0,
         end: 0,
-        exact: 'all'
+        exact: ''
       }
     }
   },
   watch: {
     value: {
       handler(val) {
-        const time = this.transformTimeBefore(val)
-        if (time.indexOf('_') === -1) {
-          this.selectTime = {
-            start: 0,
-            end: 0,
-            exact: time
-          }
-        } else {
-          const times = time.split('_')
-          this.selectTime = {
-            start: times[0] * 1000,
-            end: times[1] * 1000,
-            exact: 'exact'
-          }
-        }
-        if (this.$refs.content) {
-          this.$refs.content.setState(this.selectTime)
-        }
+        this.syncState(val)
       },
       deep: true
     }
   },
-  created() {},
+  mounted() {
+    this.syncState(this.value)
+  },
   methods: {
+    syncState(val) {
+      const time = this.transformTimeBefore(val)
+      if (time.indexOf('_') === -1) {
+        this.selectTime = {
+          start: 0,
+          end: 0,
+          exact: time
+        }
+      } else {
+        const times = time.split('_')
+        this.selectTime = {
+          start: times[0] * 1000,
+          end: times[1] * 1000,
+          exact: 'exact'
+        }
+      }
+      if (this.$refs.content) {
+        this.$refs.content.setState(this.selectTime)
+      }
+    },
     setState(data) {
       return this.$refs.content.setState(data)
     },

+ 1 - 1
apps/bigmember_pc/src/components/toast/index.js

@@ -5,7 +5,7 @@ const ToastConstructor = vue.extend(toastComponent)
 let ToastHistory = {}
 
 // 定义弹出组件的函数 接收2个参数, 要显示的文本 和 显示时间
-function showToast(text, duration = 2000) {
+export function showToast(text, duration = 2000) {
   if (ToastHistory.el) {
     ToastHistory.destory()
   }

+ 12 - 0
apps/bigmember_pc/src/store/user.js

@@ -22,6 +22,14 @@ const vtMap = {
   bigmember: 'm'
 }
 
+const UserVTypeMap = {
+  v: 'vType',
+  m: 'mType',
+  s: 'eType',
+  f: 'fType',
+  q: 'eType' // 企业订阅-也需调商机管理订阅信息接口,定义q为了区分企业订阅和个人订阅(商机管理)所传参数
+}
+
 export default {
   namespaced: true,
   state: () => ({
@@ -406,6 +414,10 @@ export default {
         return vtMap.free
       }
     },
+    // 获取后端所需的用户 type,用于接口
+    userType(state, getters) {
+      return UserVTypeMap[getters.vt] || 'fType'
+    },
     // 大会员子账号
     isSubCount: (state) => state.info.isSubCount,
     // 大会员权限

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

@@ -0,0 +1,379 @@
+import dayjs from 'dayjs'
+
+
+
+/**
+ * 整理已存筛选数据,去除空数据
+ * 需要和 FilterHistoryAjaxModel2ViewModel.formatAll 配合使用
+ */
+export function filterHistoryNotEmptyFormat(formatted) {
+  const {
+    searchGroup,
+    scopeText,
+    industryText,
+    AreaCityText,
+    priceText,
+    publishTimeText,
+    buyerClassText,
+    winnerConcatText,
+    buyerConcatText,
+    fileExists,
+    notKey,
+    buyerList,
+    winnerList,
+    agencyList
+  } = formatted
+  let { infoTypeText } = formatted
+
+  const formattedList = []
+  if (Array.isArray(scopeText) && scopeText.length) {
+    formattedList.push({
+      label: '搜索范围:',
+      text: scopeText.join('/')
+    })
+  }
+  if (Array.isArray(industryText) && industryText.length) {
+    formattedList.push({
+      label: '行业:',
+      text: industryText.join('/')
+    })
+  }
+  if (Array.isArray(AreaCityText) && AreaCityText.length) {
+    formattedList.push({
+      label: '地区:',
+      text: AreaCityText.join('/')
+    })
+  }
+  if (priceText) {
+    formattedList.push({
+      label: '金额:',
+      text: priceText
+    })
+  }
+  if (publishTimeText) {
+    formattedList.push({
+      label: '时间:',
+      text: publishTimeText
+    })
+  }
+
+  /**
+   * searchGroup === '1' 表示为非超前项目
+   * searchGroup === '2' 表示为超前项目
+   * 如果此时信息类型为空。则显示其所有类型的文字,而非"全部"两个字
+   */
+  const infoTypeAll =
+    !infoTypeText || (Array.isArray(infoTypeText) && infoTypeText.length === 0)
+  if (infoTypeAll) {
+    if (searchGroup === '1') {
+      // 非超前项目
+      infoTypeText = infoTypeNotAdvancedList
+        .map((i) => i.value)
+        .filter((i) => !!i)
+    } else if (searchGroup === '2') {
+      // 超前项目
+      infoTypeText = infoTypeAdvancedList
+        .map((i) => {
+          if (i && i.value && i.value.includes('拟建')) {
+            return i.name
+          } else {
+            return i.value
+          }
+        })
+        .filter((i) => !!i)
+    }
+  }
+  if (Array.isArray(infoTypeText) && infoTypeText.length) {
+    formattedList.push({
+      label: '信息类型:',
+      text: infoTypeText.join('/')
+    })
+  }
+  if (fileExists && Array.isArray(fileExists)) {
+    // 附件选择全部时候,则不显示
+    const fileState = fileExists.join('')
+    if (fileState !== '0') {
+      const textInfo = biddingSearchFileExists.find((item) => {
+        return fileState === item.key
+      })
+      if (textInfo) {
+        formattedList.push({
+          label: '附件:',
+          text: textInfo.label
+        })
+      }
+    }
+  }
+  if (buyerClassText) {
+    formattedList.push({
+      label: '采购单位类型:',
+      text: buyerClassText.join('/')
+    })
+  }
+  if (winnerConcatText) {
+    formattedList.push({
+      label: '中标企业联系方式:',
+      text: winnerConcatText.join('/')
+    })
+  }
+  if (buyerConcatText) {
+    formattedList.push({
+      label: '采购单位联系方式:',
+      text: buyerConcatText.join('/')
+    })
+  }
+  if (notKey) {
+    formattedList.push({
+      label: '排除词:',
+      text: notKey.split(',').join(' ')
+    })
+  }
+  if (buyerList) {
+    formattedList.push({
+      label: '采购单位:',
+      text: buyerList.split(',').join(' ')
+    })
+  }
+  if (winnerList) {
+    formattedList.push({
+      label: '中标企业:',
+      text: winnerList.split(',').join(' ')
+    })
+  }
+  if (agencyList) {
+    formattedList.push({
+      label: '代理机构:',
+      text: agencyList.split(',').join(' ')
+    })
+  }
+  return formattedList
+}
+
+
+/**
+ * 保存筛选条件
+ * 将组件初始化filter,格式成保存筛选条件接口需要数据格式
+ * 前端标准数据转接口中的数据
+ */
+export class FilterHistoryViewModel2AjaxModel {
+  static formatAll(map) {
+    // 搜索范围整理
+    const selectType = this.formatScope(map.selectType)
+    // 行业整理
+    const industry = this.formatIndustry(map.industry)
+    // 地区整理
+    // const { area, city } = this.formatAreaCity(map.area)
+    // 金额筛选整理
+    const price = this.formatPrice(map.price)
+    // 时间筛选整理
+    // const publishTime = this.formatTime(map.publishTime)
+    // 信息类型
+    const subtype = this.formatInfoType(map.subtype)
+    // 采购单位
+    const { buyerClass } = this.formatBuyerClass(map.buyerclass)
+    // 包含关系、多个关键词
+    const { additionalWords, wordsMode } = this.formatSelectMoreKey(map.selectMoreKey, map.additionalWords, map.wordsMode)
+
+    const formatted = {
+      searchvalue: map.input,
+      selectType,
+      industry,
+      minprice: map.minprice,
+      maxprice: map.maxprice,
+      publishtime: map.publishtime, // 发布时间
+      subtype,   // 信息类型
+      buyerclass: buyerClass, // 采购单位
+      buyertel: map.buyertel,  // 采购单位联系方式
+      winnertel: map.winnertel,  // 中标企业联系方式
+      notkey: map.notkey ? map.notkey.join(',') : '', // 排除词
+      buyer: map.buyer ? map.buyer.join(',') : '',  // 采购单位
+      winner: map.winner ? map.winner.join(',') : '', // 中标企业
+      agency: map.agency ? map.agency.join(',') : '', // 招标代理机构
+      fileExists: map.fileExists, // 附件
+      regionMap: map.regionMap, // 地区
+      searchGroup: map.searchGroup, // 搜索分组:默认0:全部;1:招标采购公告;2:超前项目
+      searchMode: Number(map.searchMode),  // 搜索模式:0:精准搜索;1:模糊搜索
+      wordsMode: wordsMode, // 搜索关键词模式;默认0:包含所有,1:包含任意
+      additionalWords: additionalWords, //关键词:附加关键词(副:五组,每组最多15个字符)
+      dateTime:  map.publishtime, // 标讯搜索恢复数据可能要用到?
+    }
+
+    // 删去undefined/null的项
+    for (const key in formatted) {
+      if (formatted[key] === '' || formatted[key] === undefined || formatted[key] === null) {
+        delete formatted[key]
+      }
+    }
+
+    return formatted
+  }
+
+  /**
+   * 搜索范围整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:[content, title ,ppa]
+   */
+  static formatScope(val = [], split = ',') {
+    if (!Array.isArray(val)) return ''
+    return val.join(split)
+  }
+
+  /**
+   * 行业整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:
+   * {
+   *    机械设备: ['工程机械', '车辆', '其他机械设备', '办公家具']
+   * }
+   */
+  static formatIndustry(val = {}, split = ',') {
+    let industry = ''
+    if (!val || Object.keys(val).length === 0) return industry
+
+    const industryArr = []
+
+    for (const key in val) {
+      if (Array.isArray(val[key])) {
+        val[key].forEach((item) => {
+          industryArr.push(`${key}_${item}`)
+        })
+      }
+    }
+
+    industry = industryArr.join(split)
+
+    return industry
+  }
+
+  /**
+   * 三级地级市地区整理
+   * @param String area
+   * @param String city
+   * @returns Object
+   *
+   * 参数area示例
+   * {
+   *    北京: {
+   *      朝阳区: [],
+   *      东城区: []
+   *    },
+   *    河南: {
+   *      南阳市: [],
+   *      郑州: ['金水区'],
+   *      洛阳市: ['栾川县']
+   *    },
+   *    澳门: {}
+   * }
+   */
+  static formatAreaCity(p = {}, split = ',') {
+    return areaObjToSingle(p, split)
+  }
+  /**
+   * 金额整理
+   * @returns String
+   */
+  static formatPrice(price = { start: '', end: '' }, split = '-') {
+    const { start, end } = price
+    if (start || end) {
+      return [start || '', end || ''].join(split)
+    } else {
+      return ''
+    }
+  }
+
+  /**
+   * 时间整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数time: 时间选择器选择结果
+   * {
+   *    start: '',
+   *    end: '',
+   *    exact: 'exact'
+   * }
+   *
+   * exact: 是否只输出精确结果
+   * split: 精确结果的分隔符
+   */
+  static formatTime(time, exact = false, split = '_') {
+    let sortedTime = ''
+    if (!time) return sortedTime
+    const { start, end } = time
+    if (exact || time.exact === 'exact') {
+      const startVal = start ? dayjs(start).unix() : ''
+      const endVal = end ? dayjs(end).unix() : ''
+      sortedTime = [startVal, endVal].join(split)
+      if (startVal || endVal) {
+        return [startVal, endVal].join(split)
+      } else {
+        return [].join(split)
+      }
+    } else {
+      sortedTime = time.exact || ''
+    }
+    return sortedTime
+  }
+
+  /**
+   * 信息类型整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:{}
+   */
+  static formatInfoType(infoType, split = ',') {
+    let arr = []
+    if (!infoType) return arr.join(split)
+    for (const key in infoType) {
+      arr = arr.concat(infoType[key])
+    }
+    return arr.join(split)
+  }
+
+  /**
+   * 采购单位类型整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:{}
+   */
+  static formatBuyerClass(val, split = ',') {
+    let buyerClass = ''
+    if (!val || Object.keys(val).length === 0) return buyerClass
+
+    buyerClass = []
+
+    for (const key in val) {
+      if (Array.isArray(val[key])) {
+        val[key].forEach((item) => {
+          buyerClass.push(item)
+        })
+      }
+    }
+
+    buyerClass = buyerClass.join(split)
+
+    return buyerClass
+  }
+
+  /**
+   * 格式化包含关键词模式、包含关键词
+   */
+  static formatSelectMoreKey (selectMoreKey, additionalWords, wordsMode) {
+    let aWords = '' // 附加关键词筛选模式
+    let wMode = 0 // 附加关键词筛选模式
+    if(selectMoreKey) {
+      aWords = additionalWords ? additionalWords.join(',') : ''
+      wMode = Number(wordsMode)
+    }
+    return {
+      additionalWords: aWords,
+      wordsMode: wMode
+    }
+  }
+}

+ 1 - 0
apps/bigmember_pc/src/utils/index.js

@@ -7,3 +7,4 @@ export * from './globalFunctions'
 export * from './format/money'
 export * from './format/ad'
 export * from './format/date'
+export * from './format/search-bid-filter'

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

@@ -1,5 +1,5 @@
 <script setup>
-import SearchBidModel from '../model/index'
+import { SearchBidModel } from '../model/index'
 import SearchSchemaFilter from '@/views/search/components/search-schema-filter.vue'
 import {
   SearchBidBaseSchema,

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

@@ -2,7 +2,7 @@
 import SearchHeaderCard from '@/views/search/components/search-header-card.vue'
 import KeywordTagsPc from '@/views/search/components/keyword-tags.vue'
 import CommonSingleChoice from '@/components/filter-items/CommonSingleChoice.vue'
-import SearchBidModel from '../model/index'
+import { SearchBidModel } from '../model/index'
 
 const { inputKeywordsState, doQuery, searchModelOptions, SearchTabsModel } =
   SearchBidModel

+ 30 - 0
apps/bigmember_pc/src/views/search/bidding/components/search-header-title.vue

@@ -0,0 +1,30 @@
+<script setup>
+import { SearchBidModel } from '../model/index'
+const { onSaveFilter } = SearchBidModel
+import { computed, getCurrentInstance, ref } from 'vue'
+const fixedTop = ref(false)
+const showFilter = ref(true)
+function toggleFilter() {}
+console.log(getCurrentInstance())
+</script>
+
+<template>
+  <div class="filter-header-container" :class="fixedTop ? 'fixed' : ''">
+    <div class="filter-header" :class="fixedTop ? 'fixed-top' : ''">
+      <div
+        class="f-h-label"
+        :class="showFilter ? 'caret-up' : 'caret-down'"
+        @click="toggleFilter"
+      >
+        <span>筛选条件</span>
+      </div>
+      <div class="f-h-action nologin-hide">
+        <!--        <span class="action-item reset-item" @click="onResetFilter">重置筛选条件</span>-->
+        <!--        <span class="action-item has-item" @click="onHasFilter">已存筛选条件 ${filterCounts}</span>-->
+        <span class="action-item save-item" @click="onSaveFilter"
+          >保存筛选条件</span
+        >
+      </div>
+    </div>
+  </div>
+</template>

+ 13 - 1
apps/bigmember_pc/src/views/search/bidding/constant/search-filters.js

@@ -7,10 +7,11 @@ import AttachmentSelector from '@/components/filter-items/AttachmentSelector.vue
 import AmountRangeSelector from '@/components/filter-items/AmountRangeSelector.vue'
 import IndustrySelector from '@/components/filter-items/IndustrySelector.vue'
 import KeywordTagsSelector from '@/components/filter-items/KeywordTagsSelector'
+import RegionSelector from '@/components/filter-items/RegionSelector'
 
 const SearchBidBaseSchema = [
   {
-    key: 'publishtime',
+    key: 'publishTime',
     label: '发布时间:',
     defaultVal: 'thisyear',
     _name: 'time',
@@ -55,6 +56,17 @@ const SearchBidBaseSchema = [
 ]
 
 const SearchBidMoreSchema = [
+  {
+    key: 'regionMap',
+    label: '地区',
+    defaultVal: '',
+    _name: 'type',
+    _type: 'component',
+    expand: {
+      component: RegionSelector,
+      hooks: {}
+    }
+  },
   {
     key: 'industry',
     label: '行业',

+ 7 - 6
apps/bigmember_pc/src/views/search/bidding/index.vue

@@ -2,13 +2,17 @@
 import { computed, reactive, ref } from 'vue'
 import SearchBidHeader from '@/views/search/bidding/components/search-bid-header.vue'
 import SearchBidFilter from '@/views/search/bidding/components/search-bid-filter.vue'
+import searchHeaderTitle from '@/views/search/bidding/components/search-header-title.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 SearchBidModel from './model/index'
+import { useSearchBidModel, SearchBidModel } from './model/index'
 
-// 解构业务所需 model \ fn
+// 初始化模型
+useSearchBidModel()
+
+// 初始化模型 解构业务所需 model \ fn
 const {
   filterState,
   inputKeywordsState,
@@ -22,9 +26,6 @@ const {
   doChangePageNum,
   doChangePageSize
 } = SearchBidModel
-
-// 查询数据
-// doQuery()
 </script>
 
 <template>
@@ -32,8 +33,8 @@ const {
     <div class="search-bidding-header-container b-rd-8px m-t-24px">
       <search-bid-header></search-bid-header>
     </div>
-
     <div class="search-bidding-filter-container b-rd-8px">
+      <searchHeaderTitle></searchHeaderTitle>
       <search-bid-filter></search-bid-filter>
     </div>
 

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

@@ -0,0 +1,223 @@
+import { computed, reactive } from 'vue'
+import { without } from 'lodash'
+// 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 { useStore } from '@/store'
+
+export default function () {
+  // 解构基础业务
+  const APIModel = useQuickSearchModel({
+    type: 'search-bid'
+  })
+
+  const {
+    list,
+    total,
+    loading,
+    finished,
+    selectIds,
+    listIds,
+    searchResultCount,
+    isSelectSomeCheckbox,
+    selectCheckboxCount,
+    isSelectListAllCheckbox,
+    doToggleItemSelection,
+    doToggleListSelection,
+    doClearAllSelection,
+    doQuery: doRunQuery
+  } = APIModel
+
+  const {
+    inputKeywordsState,
+    searchModelOptions,
+    getFormatAPIParams: getFormatOfInputKeywords
+  } = useSearchInputKeywordsModel()
+  const { filterState, getFormatAPIParams: getFormatOfFilter } =
+    useSearchFilterModel()
+  const {
+    headerActions,
+    listItemStyleType,
+    activeItemStyleType,
+    activeHeaderActions
+  } = useSearchListHeaderActionsModel()
+  const SearchTabsModel = useSearchTabsModel()
+
+  // 列表状态
+  const listState = reactive({
+    finished,
+    loading,
+    pageNum: 1,
+    pageSize: 5,
+    total
+  })
+
+  // 当前展示的列表
+  const activeList = computed(() => {
+    return list.value.map((v) => {
+      v.id = v._id
+      v.checked = selectIds.value.includes(v.id)
+      return v
+    })
+  })
+
+  // search-list 组件所需参数
+  const searchListProps = computed(() => {
+    return {
+      isSelectAllCheckbox: isSelectListAllCheckbox.value,
+      isSelectSomeCheckbox: isSelectSomeCheckbox.value,
+      selectCheckboxCount: selectCheckboxCount.value,
+      searchResultCount: searchResultCount.value,
+      headerActions: headerActions.value,
+      list: activeList.value,
+      listState: 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)
+  }
+
+  /**
+   * 列表顶部按钮操作事件统一入口
+   * @param item - 按钮原型
+   * @param item.key - 按钮标识
+   */
+  function doListHeaderAction(item) {
+    const { key } = item
+    switch (key) {
+      case 'refined-list': {
+        doChangeItemStyleType(key)
+        break
+      }
+      case 'detailed-list': {
+        doChangeItemStyleType(key)
+        break
+      }
+      case 'table': {
+        doChangeItemStyleType(key)
+        break
+      }
+    }
+  }
+
+  // 全选复选框事件
+  function doChangeAllSelect(type) {
+    doToggleListSelection(type)
+  }
+
+  // 单个复选框事件
+  function doChangeSelect(item) {
+    doToggleItemSelection(item.id)
+  }
+
+  // 分页事件
+  function doChangePageNum(page) {
+    listState.pageNum = page
+    doQuery()
+  }
+
+  // 分页大小事件
+  function doChangePageSize(size) {
+    listState.pageSize = size
+    listState.pageNum = 1
+    doQuery()
+  }
+
+  // 获取 store getters
+  const userType = computed(() => {
+    return useStore().getters['user/userType']
+  })
+
+  /**
+   * 格式化请求参数
+   * @param [params] - 可选值,部分情况会提供,默认会和该函数返回值进行合并
+   */
+
+  function getParams(params = {}) {
+    // 合并所有模型的搜索筛选项
+    const result = Object.assign(
+      {
+        reqType: 'lastNews',
+        pageNum: listState.pageNum,
+        pageSize: listState.pageSize,
+        // 该接口与用户身份有关
+        _expand: {
+          type: userType.value
+        }
+      },
+      getFormatOfInputKeywords(),
+      getFormatOfFilter(),
+      params
+    )
+    return result
+  }
+
+  /**
+   * 统一查询入口
+   * - 拦截 doQuery 进行一些返回值处理
+   * @param [params] - 可选值,默认会和 getParams(params) 返回值进行合并
+   */
+  function doQuery(params = {}) {
+    return doRunQuery(getParams(params)).then((res) => {
+      // 用于搜索关键词高亮
+      inputKeywordsState.value.matchKeys = res.origin?.heightWords?.split(
+        ' '
+      ) || [inputKeywordsState.value.input]
+    })
+  }
+
+  /**
+   * 保存、重置筛选条件部分
+   **/
+  const { saveFilterDialog, filterSaveParams } = saveFilterActionsModel
+
+  /**
+   * 保存筛选条件
+   */
+  function onSaveFilter() {
+    const originParams = Object.assign(
+      {},
+      filterState.value,
+      inputKeywordsState.value
+    )
+    const config = {
+      filter: originParams
+    }
+  }
+
+  return {
+    searchModelOptions,
+    searchListProps,
+    SearchTabsModel,
+    inputKeywordsState,
+    filterState,
+    listState,
+    activeItemStyleType,
+    doQuery,
+    doListHeaderAction,
+    doChangeAllSelect,
+    doChangeSelect,
+    doChangePageNum,
+    doChangePageSize,
+    onSaveFilter
+  }
+}

+ 6 - 184
apps/bigmember_pc/src/views/search/bidding/model/index.js

@@ -1,187 +1,9 @@
-import { computed, reactive } from 'vue'
-import { without } from 'lodash'
-// 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 useModel from './base'
 
-// 解构基础业务
-const APIModel = useQuickSearchModel({
-  type: 'search-bid'
-})
-
-const {
-  list,
-  total,
-  loading,
-  finished,
-  selectIds,
-  listIds,
-  searchResultCount,
-  isSelectSomeCheckbox,
-  selectCheckboxCount,
-  isSelectListAllCheckbox,
-  doToggleItemSelection,
-  doToggleListSelection,
-  doClearAllSelection,
-  doQuery: doRunQuery
-} = APIModel
-
-const {
-  inputKeywordsState,
-  searchModelOptions,
-  getFormatAPIParams: getFormatOfInputKeywords
-} = useSearchInputKeywordsModel()
-const { filterState, getFormatAPIParams: getFormatOfFilter } =
-  useSearchFilterModel()
-const {
-  headerActions,
-  listItemStyleType,
-  activeItemStyleType,
-  activeHeaderActions
-} = useSearchListHeaderActionsModel()
-const SearchTabsModel = useSearchTabsModel()
-
-// 列表状态
-const listState = reactive({
-  finished,
-  loading,
-  pageNum: 1,
-  pageSize: 5,
-  total
-})
-
-// 当前展示的列表
-const activeList = computed(() => {
-  return list.value.map((v) => {
-    v.id = v._id
-    v.checked = selectIds.value.includes(v.id)
-    return v
-  })
-})
-
-// search-list 组件所需参数
-const searchListProps = computed(() => {
-  return {
-    isSelectAllCheckbox: isSelectListAllCheckbox.value,
-    isSelectSomeCheckbox: isSelectSomeCheckbox.value,
-    selectCheckboxCount: selectCheckboxCount.value,
-    searchResultCount: searchResultCount.value,
-    headerActions: headerActions.value,
-    list: activeList.value,
-    listState: 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)
-}
-
-/**
- * 列表顶部按钮操作事件统一入口
- * @param item - 按钮原型
- * @param item.key - 按钮标识
- */
-function doListHeaderAction(item) {
-  const { key } = item
-  switch (key) {
-    case 'refined-list': {
-      doChangeItemStyleType(key)
-      break
-    }
-    case 'detailed-list': {
-      doChangeItemStyleType(key)
-      break
-    }
-    case 'table': {
-      doChangeItemStyleType(key)
-      break
-    }
-  }
-}
-
-// 全选复选框事件
-function doChangeAllSelect(type) {
-  doToggleListSelection(type)
-}
-
-// 单个复选框事件
-function doChangeSelect(item) {
-  doToggleItemSelection(item.id)
-}
-
-// 分页事件
-function doChangePageNum(page) {
-  listState.pageNum = page
-  doQuery()
+let SearchBidModel = {}
+function useSearchBidModel() {
+  SearchBidModel = useModel()
+  return SearchBidModel
 }
 
-// 分页大小事件
-function doChangePageSize(size) {
-  listState.pageSize = size
-  listState.pageNum = 1
-  doQuery()
-}
-
-/**
- * 格式化请求参数
- * @param [params] - 可选值,部分情况会提供,默认会和该函数返回值进行合并
- */
-
-function getParams(params = {}) {
-  // 合并所有模型的搜索筛选项
-  const result = Object.assign(
-    {
-      reqType: 'lastNews',
-      pageNumber: listState.pageNum,
-      pageSize: listState.pageSize
-    },
-    getFormatOfInputKeywords(),
-    getFormatOfFilter(),
-    params
-  )
-  return result
-}
-
-/**
- * 统一查询入口
- * - 拦截 doQuery 进行一些返回值处理
- * @param [params] - 可选值,默认会和 getParams(params) 返回值进行合并
- */
-function doQuery(params = {}) {
-  return doRunQuery(getParams(params)).then((res) => {
-    // 用于搜索关键词高亮
-    inputKeywordsState.value.matchKeys = res.origin?.heightWords?.split(
-      ' '
-    ) || [inputKeywordsState.value.input]
-  })
-}
-
-export default {
-  searchModelOptions,
-  searchListProps,
-  SearchTabsModel,
-  inputKeywordsState,
-  filterState,
-  listState,
-  activeItemStyleType,
-  doQuery,
-  doListHeaderAction,
-  doChangeAllSelect,
-  doChangeSelect,
-  doChangePageNum,
-  doChangePageSize
-}
+export { useSearchBidModel, SearchBidModel }

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

@@ -0,0 +1,57 @@
+import { computed, ref } from 'vue'
+import { ajaxGetDontPromptAgain, ajaxSetDontPromptAgain } from '@/api/modules/'
+import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
+
+
+// 判断是否展示弹框
+async function getDontPromptAgain() {
+  const { error_code: code, isPrompt } = await ajaxGetDontPromptAgain()
+  return {
+    code,
+    isPrompt
+  }
+}
+
+async function toDataExportEvent() {
+  // const res = await getPushListExport(this.vt, query)
+  // this.exportLoading = false
+  // if (res.data) {
+  //   const link = `/front/dataExport/toCreateOrderPage/${res.data}`
+  //   tryCallHooks({
+  //     fn: () => {
+  //       this.$BRACE.methods.open({
+  //         route: {
+  //           link
+  //         }
+  //       })
+  //     },
+  //     spareFn: () => {
+  //       location.href = link
+  //       // window.open(link)
+  //     }
+  //   })
+  // }
+}
+
+export function dataExportActionsModel (config = {}) {
+  const { list, loading, finished, total, selectedIds } = config
+  const { code, isPrompt } = getDontPromptAgain()
+  if(code === 0) {
+    const countBool = ref(false)
+    // 限制2000条提示弹窗
+    const showDataExportDialog = ref(false)
+    if(selectedIds > 0) {
+      countBool.value = selectedIds >= 2000
+    } else {
+      countBool.value = total >= 2000
+    }
+    if (isPrompt && countBool) {
+      showDataExportDialog.value = isPrompt
+    } else {
+      // toDataExportEvent()
+    }
+  } else {
+    // toDataExportEvent()
+  }
+
+}

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

@@ -25,7 +25,7 @@ export function useSearchInputKeywordsModel() {
 
   function getFormatAPIParams() {
     const params = {
-      searchvalue: inputKeywordsState.value.input,
+      keyWords: inputKeywordsState.value.input,
       searchMode: Number(inputKeywordsState.value.searchMode)
     }
 

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

@@ -4,11 +4,13 @@ export function useSearchFilterModel() {
   // 筛选组件状态
   const filterState = ref({
     // 发布时间
-    publishtime: 'thisyear',
+    publishTime: 'thisyear',
     // 搜索范围
     selectType: ['content', 'title'],
     // 信息类型
     subtype: [],
+    // 地区
+    regionMap: {},
     // 行业
     industry: {},
     // 附件
@@ -26,20 +28,20 @@ export function useSearchFilterModel() {
     // 采购单位
     buyer: [],
     // 中标企业
-    winner : [],
+    winner: [],
     // 招标代理机构
     agency: []
   })
 
   function getFormatAPIParams() {
     const params = {
-      publishtime: filterState.value.publishtime,
+      publishTime: filterState.value.publishTime,
       selectType: filterState.value.selectType.join(','),
-      subtype: filterState.value.subtype,
+      subtype: filterState.value.subtype.join(','),
       notkey: filterState.value.notkey.join(','),
       buyer: filterState.value.buyer.join(','),
       winner: filterState.value.winner.join(','),
-      agency: filterState.value.agency.join(','),
+      agency: filterState.value.agency.join(',')
     }
 
     return params

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

@@ -0,0 +1,50 @@
+import { computed, reactive, ref, getCurrentInstance } from 'vue'
+import { checkBiddingFilterPass } from '@/api/modules'
+import  { FilterHistoryViewModel2AjaxModel } from '@/utils'
+import { showToast } from '@/components/toast'
+import { ajaxGetSearchBidList } from '@jy/data-models/modules/quick-search/api/search-bid'
+
+const saveConfig = {
+  savedFilterListMaxCount: 10 //筛选条件保存条数最大值
+}
+
+export function saveFilterActionsModel () {
+   let filterSaveParams = reactive({})
+   const saveFilterDialog = ref(false)
+
+  // 检测筛选条件是否可保存
+  //  async function checkFilterPass (config) {
+  //    const  { filter } = config
+  //    filterSaveParams = FilterHistoryViewModel2AjaxModel.formatAll(filter)
+  //
+  //    const hasOneKey = ref(filterSaveParams.buyer || filterSaveParams.winner || filterSaveParams.agency)
+  //    if (!filterSaveParams.searchvalue && !filterSaveParams.additionalWords && !filterSaveParams.industry && !hasOneKey) {
+  //      return showToast('请先输入关键词')
+  //    }
+  //    const { savedFilterListMaxCount: maxCount} = saveConfig
+  //    if(maxCount) {
+  //      return showToast(`对不起,最多可保存${maxCount}个筛选条件。`)
+  //    }
+  //    const { data: id, error_code: code = 0, error_msg: msg } = await checkBiddingFilterPass(filterSaveParams)
+  //   if(code === 0) {
+  //     saveFilterDialog.value = true
+  //   } else {
+  //     if (msg) {
+  //       if (msg.includes('无用户身份')) {
+  //         showToast('请登录')
+  //       } else {
+  //         showToast(msg)
+  //       }
+  //     }
+  //   }
+  // }
+  // console.log(111111)
+  //  console.log(checkFilterPass)
+
+
+
+  return {
+    saveFilterDialog,
+    filterSaveParams,
+  }
+}

+ 10 - 26
apps/bigmember_pc/src/views/search/components/search-schema-filter.vue

@@ -1,5 +1,4 @@
 <script setup>
-import { ref, watch } from 'vue'
 import { assign } from 'lodash'
 
 const props = defineProps({
@@ -27,26 +26,11 @@ const props = defineProps({
 
 const emit = defineEmits(['input', 'change'])
 
-const tranState = ref(assign({}, props.value))
-
-watch(
-  tranState,
-  (newValue) => {
-    emit('input', newValue)
-    emit('change')
-  },
-  { deep: true }
-)
-
-watch(
-  () => props.value,
-  (newValue) => {
-    tranState.value = newValue
-  }
-)
-
-function doChange(key, val) {
-  tranState.value[key] = val
+function doChangeInput(key, event) {
+  const result = assign({}, props.value, {
+    [key]: event
+  })
+  emit('input', result)
   emit('change')
 }
 
@@ -80,17 +64,17 @@ const getPrefix = {
       </div>
       <div>
         <!-- @component 自定义组件  -->
+        <!-- @input @change 兼容不同组件的输出  -->
         <Component
           v-if="item._type === 'component'"
           :is="item.expand.component"
           :ref="getPrefix.component + item._name"
-          v-model="tranState[item.key]"
+          :value="props.value[item.key]"
+          @input="doChangeInput(item.key, $event)"
+          @change="doChangeInput(item.key, $event)"
           v-bind="item.expand.props"
           v-on="{
-            ...item.expand.hooks,
-            onChange: (val) => {
-              doChange(item.key, val)
-            }
+            ...item.expand.hooks
           }"
         >
         </Component>

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

@@ -42,7 +42,7 @@ const props = defineProps({
     })
   }
 })
-
+console.log(props.headerActions)
 const canShowPagination = computed(() => {
   if (props.listState.loading && props.listState.pageNum === 1) {
     return false

+ 7 - 1
apps/bigmember_pc/src/views/search/purchase/model/index.js

@@ -1,6 +1,7 @@
 import { computed, reactive, ref } from 'vue'
 import useQuickSearchModel from '@jy/data-models/modules/quick-search/model'
 import { filterState } from './modules/filter'
+import { useStore } from '@/store'
 
 // 解构基础业务
 const APIModel = useQuickSearchModel({
@@ -77,11 +78,16 @@ function doChangeFilter() {
   doQuery()
 }
 
+// 获取 store getters
+const userType = computed(() => {
+  return useStore().getters['user/userType']
+})
+
 function getParams() {
   return {
     // 该接口与用户身份有关
     _expand: {
-      type: 'eType'
+      type: userType.value
     },
     searchType: 'title',
     keywords: inputKeywordsState.value.input,

+ 3 - 4
data/data-models/modules/quick-search/api/search-bid.js

@@ -1,9 +1,8 @@
 import { useRequest } from '../../../api'
-import qs from 'qs'
-export function ajaxGetSearchBidList(data) {
+export function ajaxGetSearchBidList(type, data) {
   return useRequest({
-    url: '/front/pcAjaxReq',
+    url: `/jyapi/jybx/core/${type}/searchList`,
     method: 'post',
-    data: qs.stringify(data)
+    data
   })
 }

+ 7 - 5
data/data-models/modules/quick-search/plugins/search-bid.js

@@ -10,15 +10,17 @@ export default class SearchBidListApi extends SearchListApiBase {
    * 覆写请求
    */
   async ajaxQuery(params) {
-    return ajaxGetSearchBidList(params).then((res) => {
+    const type = params._expand.type
+    delete params._expand
+    return ajaxGetSearchBidList(type, params).then((res) => {
       console.log(res, 'res')
-      let success = res?.status === 1
+      let success = res?.error_code === 0
 
       return {
         success: success,
-        list: res?.list || [],
-        total: res?.total || 0,
-        origin: res
+        list: res.data?.list || [],
+        total: res.data?.total || 0,
+        origin: res.data
       }
     })
   }