浏览代码

Merge branch 'dev/1.0.36_zsy' of jianyu/web into feature/v1.0.36

zhangsiya 1 年之前
父节点
当前提交
7072176dca

+ 162 - 0
apps/bigmember_pc/src/assets/js/selector.js

@@ -859,4 +859,166 @@ export const buyerContactData = [
     value: 'y'
   }
 ]
+// 搜索模式列表
+export const searchModeList = [
+  {
+    label: '精准搜索',
+    key: '0'
+  },
+  {
+    label: '模糊搜索',
+    key: '1'
+  }
+]
+
+// 搜索接口参数表
+export const biddingSearchListType = [
+  'fType', // 免费用户
+  'pType', // 付费用户
+  'vType', // 超级订阅用户
+  'mType', // 大会员用户
+  'eType' // 商机管理用户
+]
+
+// 招标搜索范围
+export const biddingSearchScope = [
+  {
+    label: '标题搜索',
+    key: 'title'
+  },
+  {
+    label: '正文搜索',
+    key: 'content'
+  },
+  {
+    label: '附件',
+    key: 'file'
+  },
+  {
+    label: '项目名称/标的物',
+    key: 'ppa',
+    needPower: true
+  },
+  {
+    label: '采购单位',
+    key: 'buyer',
+    needPower: true
+  },
+  {
+    label: '中标企业',
+    key: 'winner',
+    needPower: true
+  },
+  {
+    label: '招标代理机构',
+    key: 'agency',
+    needPower: true
+  }
+]
+
+export const biddingSearchTime = [
+  {
+    label: '最近7天',
+    key: 'lately-7'
+  },
+  {
+    label: '最近30天',
+    key: 'lately-30'
+  },
+  {
+    label: '最近1年',
+    key: 'thisyear'
+  },
+  {
+    label: '最近3年',
+    key: 'threeyear',
+    needPower: true
+  },
+  {
+    label: '最近5年',
+    key: 'fiveyear',
+    needPower: true
+  },
+  {
+    label: '自定义',
+    key: 'exact',
+    needPower: true
+  }
+]
+
+// 是否有联系方式
+export const biddingSearchConcat = [
+  {
+    label: '不限',
+    key: ''
+  },
+  {
+    label: '有联系方式',
+    key: 'y'
+  }
+]
+
+// 附件
+export const biddingSearchFileExists = [
+  {
+    label: '全部',
+    key: '0'
+  },
+  {
+    label: '有附件',
+    key: '1'
+  },
+  {
+    label: '无附件',
+    key: '-1'
+  }
+]
+
+// 查看状态
+export const biddingSearchViewStatus = [
+  {
+    label: '全部',
+    key: ''
+  },
+  {
+    label: '未查看',
+    key: '0'
+  },
+  {
+    label: '已查看',
+    key: '1'
+  }
+]
+
+// 信息来源
+export const biddingSearchInfoSource = [
+  {
+    label: '全部',
+    key: '0'
+  },
+  {
+    label: '企业手动分发',
+    key: '3'
+  },
+  {
+    label: '企业自动分发',
+    key: '2'
+  },
+  {
+    label: '个人订阅',
+    key: '1'
+  }
+]
+
+// 关键词匹配模式
+export const wordsModeList = [
+  {
+    label: '包含所有关键词',
+    key: '0'
+  },
+  {
+    label: '包含任意关键词',
+    key: '1'
+  }
+]
 

+ 6 - 6
apps/bigmember_pc/src/components/filter-items/BuyerTypeSelector.vue

@@ -51,11 +51,11 @@ export default {
       selectedVal: []
     }
   },
-  watch: {
-    value (val) {
-      this.selectedVal = this.formatData(val)
-    }
-  },
+  // watch: {
+  //   value (val) {
+  //     this.selectedVal = this.formatData(val)
+  //   }
+  // },
   computed: {
     computedVal () {
       return this.selectedVal.length ? `${this.placeholder}${this.selectedVal.length}个` : ''
@@ -64,7 +64,7 @@ export default {
   methods: {
     onChange (value) {
       this.selectedVal = this.formatData(value)
-      this.$emit('onChange', value)
+      this.$emit('change', value)
     },
     formatData (data = {}) {
       const level1AndLevel2 = []

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

@@ -64,7 +64,7 @@ export default {
   methods: {
     onChange (value) {
       this.selectedVal = this.formatData(value)
-      this.$emit('onChange', value)
+      this.$emit('change', value)
     },
     formatData (data = {}) {
       const level1AndLevel2 = []

+ 9 - 0
apps/bigmember_pc/src/components/selector/InfoTypeSelector.vue

@@ -52,8 +52,16 @@ export default {
     oneLevelSelected: {
       type: Boolean,
       default: false
+    },
+    value: {
+      type: Array,
+      default: () => []
     }
   },
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
   data() {
     return {}
   },
@@ -77,6 +85,7 @@ export default {
     },
     onChange(selected) {
       this.$emit('onChange', selected)
+      this.$emit('change', selected)
     }
   }
 }

+ 495 - 136
apps/bigmember_pc/src/utils/format/search-bid-filter.js

@@ -1,152 +1,464 @@
 import dayjs from 'dayjs'
-
+import { InfoTypeTransform } from './info-type-transform'
+import {
+  biddingSearchScope,
+  biddingSearchConcat,
+  wordsModeList,
+  searchModeList,
+  biddingSearchTime,
+  buyerclassListMapExp,
+  industryListMapExp
+} from '@/assets/js/selector'
+import { dateFormatter } from '@jy/util'
 
 
 /**
- * 整理已存筛选数据,去除空数据
- * 需要和 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('/')
-    })
+export class FilterHistoryAjaxModel2ViewModel {
+  static formatAll(map) {
+    // wordsMode
+    const { wordsMode, wordsModeText } = this.formatWordsMode(map.wordsMode)
+    // 精准匹配/模糊匹配
+    const { searchMode, searchModeText } = this.formatSearchMode(map.searchMode)
+    // 搜索范围整理
+    const { scope, scopeText } = this.formatScope(map.selectType)
+    // 行业整理
+    const { industry, industryText } = this.formatIndustry(map.industry)
+    // 地区整理
+    const regionMap = this.formatRegion(map.regionMap)
+    // 金额筛选整理
+    const price = this.formatPriceText(map.minprice, map.maxprice)
+    // 时间筛选整理
+    const { publishTime, publishTimeText } = this.formatTime(map.publishtime)
+    // 信息类型
+    const { infoType, infoTypeText } = this.formatInfoType(map.subtype)
+    // 采购单位
+    const { buyerClass, buyerClassText } = this.formatBuyerClass(map.buyerclass)
+    // 联系方式
+    const { basicData: buyerTel, basicDataText: buyerTelText } = this.formatContact(map.buyertel)
+    const { basicData: winnerTel, basicDataText: winnerTelText } = this.formatContact(map.winnertel)
+    // 附件
+    const fileExists = this.formatAttach(map.fileExists)
+
+    const formatted = {
+      keywords: map.searchvalue,
+      additionalWords: map.additionalWords,
+      wordsMode,
+      wordsModeText,
+      searchMode,
+      searchModeText,
+      searchGroup: map.searchGroup !== undefined ? String(map.searchGroup) : '0',
+      scope,
+      scopeText,
+      industry,
+      industryText,
+      regionMap,
+      price,
+      dateTime: publishTime, // 标讯搜索恢复数据可能要用到?
+      publishTime,
+      publishTimeText,
+      infoType,
+      infoTypeText,
+      buyerClass,
+      buyerClassText,
+      buyerTel,
+      buyerTelText,
+      winnerTel,
+      winnerTelText,
+      notkey: map.notkey,
+      buyer: map.buyer,
+      winner: map.winner,
+      agency: map.agency,
+      fileExists
+    }
+
+    // 删去undefined/null的项
+    for (const key in formatted) {
+      if (!formatted[key]) {
+        delete formatted[key]
+      }
+    }
+    return formatted
   }
-  if (Array.isArray(industryText) && industryText.length) {
-    formattedList.push({
-      label: '行业:',
-      text: industryText.join('/')
-    })
+
+  static mapToList(map) {
+    let list = []
+    if (!map) return list
+    for (const key in map) {
+      if (Array.isArray(map[key])) {
+        list = list.concat(map[key])
+      }
+    }
+    return list
   }
-  if (Array.isArray(AreaCityText) && AreaCityText.length) {
-    formattedList.push({
-      label: '地区:',
-      text: AreaCityText.join('/')
-    })
+
+  /**
+   * 关键词匹配方式wordsModeList整理
+   * (输出包含文字描述和选择器使用的数据结构)
+   * @param String m '0'/'1'
+   * @returns Object
+   */
+  static formatWordsMode(m) {
+    const result = {
+      wordsMode: undefined,
+      wordsModeText: undefined
+    }
+    if (!m) {
+      m = '0'
+    }
+    m = String(m)
+    const target = wordsModeList.find((item) => m === item.key)
+    if (target) {
+      result.wordsMode = [target.key]
+      result.wordsModeText = target.label
+    }
+    return result
   }
-  if (priceText) {
-    formattedList.push({
-      label: '金额:',
-      text: priceText
-    })
+
+  /**
+   * 搜索方式整理 精准匹配/模糊匹配
+   * @param String m '0'/'1'
+   * @returns Object
+   */
+  static formatSearchMode(m) {
+    const result = {
+      searchMode: undefined,
+      searchModeText: undefined
+    }
+    // m为空默认为0
+    if (!m) {
+      m = '0'
+    }
+    m = String(m)
+    const target = searchModeList.find((item) => m === item.key)
+    if (target) {
+      result.searchMode = [target.key]
+      result.searchModeText = target.label
+    }
+    return result
   }
-  if (publishTimeText) {
-    formattedList.push({
-      label: '时间:',
-      text: publishTimeText
+
+  /**
+   * 搜索范围整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:'content,title,ppa'
+   */
+  static formatScope(val) {
+    const result = {
+      scope: undefined,
+      scopeText: undefined
+    }
+    if (!val) return result
+    const map = {}
+    biddingSearchScope.forEach((item) => {
+      map[item.key] = item.label
     })
+    // 将字符转换为中文
+    const selectKeyArr = val.split(',')
+    const selectKeyTextArr = selectKeyArr
+      .map((key) => {
+        return map[key]
+      })
+      .filter((key) => !!key)
+
+    result.scope = selectKeyArr
+    result.scopeText = selectKeyTextArr ? selectKeyTextArr.toString().replace(/,/g, ",") : ''
+    return result
   }
 
   /**
-   * searchGroup === '1' 表示为非超前项目
-   * searchGroup === '2' 表示为超前项目
-   * 如果此时信息类型为空。则显示其所有类型的文字,而非"全部"两个字
+   * 行业整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:'机械设备_工程机械,机械设备_车辆,机械设备_其他机械设备,行政办公_通用办公设备,行政办公_办公家具'
    */
-  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)
+  static formatIndustry(val) {
+    const result = {
+      industry: undefined,
+      industryText: undefined
     }
-  }
-  if (Array.isArray(infoTypeText) && infoTypeText.length) {
-    formattedList.push({
-      label: '信息类型:',
-      text: infoTypeText.join('/')
+    if (!val || val === '全部') return result
+
+    const industry = {}
+    let industryText = []
+
+    // 整理行业
+    val.split(',').forEach((v) => {
+      const vSplit = v.split('_')
+      const industryChildren = industry[vSplit[0]]
+      if (Array.isArray(industryChildren)) {
+        industryChildren.push(vSplit[1])
+      } else {
+        industry[vSplit[0]] = [vSplit[1]]
+      }
     })
-  }
-  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 (Object.keys(industry).length) {
+      result.industry = industry
+    }
+
+    // 整理行业text
+    // 统计完整行业数量
+    const calcChildrenCount = {}
+    for (const key in industryListMapExp) {
+      calcChildrenCount[key] = industryListMapExp[key].length
+    }
+    // 如果行业数据为全部,则只显示一级行业,否则需要显示二级行业
+    for (const key in industry) {
+      if (industry[key].length === calcChildrenCount[key]) {
+        industryText.push(key)
+      } else {
+        industryText = [...industryText, ...industry[key]]
       }
     }
+    if (industryText.length) {
+      result.industryText = industryText ? industryText.toString().replace(/,/g, ",") : ''
+    }
+    return result
   }
-  if (buyerClassText) {
-    formattedList.push({
-      label: '采购单位类型:',
-      text: buyerClassText.join('/')
-    })
+
+  /**
+   * 三级地区整理
+   * @params Object regionMap
+   * {
+   *    北京: {
+   *      朝阳区: [],
+   *      东城区: []
+   *    },
+   *    河南: {
+   *      南阳市: [],
+   *      郑州: ['金水区'],
+   *      洛阳市: ['栾川县']
+   *    },
+   *    澳门: {}
+   * }
+   * @returns Object
+   *
+   */
+  static formatRegion (region) {
+    if (!region || Object.keys(region).length === 0) return '全国'
+    var arr = []
+    for (var povince in region) {
+      if (Object.keys(region[povince]).length === 0) {
+        arr.push(povince)
+      } else {
+        for (var city in region[povince]) {
+          if (region[povince][city].length === 0) {
+            arr.push(city)
+          } else {
+            arr.push(...region[povince][city])
+          }
+        }
+      }
+    }
+    return arr.toString().replace(/,/g, ",")
   }
-  if (winnerConcatText) {
-    formattedList.push({
-      label: '中标企业联系方式:',
-      text: winnerConcatText.join('/')
-    })
+
+  /**
+   * 价格区间处理
+   * @param start
+   * @param end
+   * @param unit
+   * @returns {string}
+   */
+  static formatPriceText(start, end, unit = '万元') {
+    let priceText = ''
+    if (start && end) {
+      priceText = `${start}-${end}${unit}`
+    } else if (start && !end) {
+      priceText = `${start}${unit}以上`
+    } else if (!start && end) {
+      priceText = `${end}${unit}以下`
+    }
+    return priceText
   }
-  if (buyerConcatText) {
-    formattedList.push({
-      label: '采购单位联系方式:',
-      text: buyerConcatText.join('/')
+
+  /**
+   * 时间整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:'1653321600_1654012800'
+   * 参数val示例:'fiveyear'
+   */
+  static formatTime(val) {
+    const result = {
+      publishTime: undefined,
+      publishTimeText: undefined
+    }
+    if (!val) return result
+    const map = {}
+    biddingSearchTime.forEach((item) => {
+      map[item.key] = item.label
     })
+    const t = {
+      start: 0,
+      end: 0,
+      exact: 'exact'
+    }
+    // 如果是精确时间
+    if (val.includes('_')) {
+      const split = val.split('_')
+      const start = split[0] * 1000
+      const end = split[1] * 1000
+      const textArr = []
+      if (start && !isNaN(start)) {
+        t.start = start
+        textArr[0] = dateFormatter(start, 'yyyy/MM/dd')
+      }
+      if (end && !isNaN(end)) {
+        t.end = end
+        textArr[1] = dateFormatter(end, 'yyyy/MM/dd')
+      }
+
+      let publishTimeText = ''
+      if (textArr[0] && textArr[1]) {
+        publishTimeText = textArr.join('-')
+      } else if (textArr[0] && !textArr[1]) {
+        publishTimeText = `${textArr[0]}以后`
+      } else if (!textArr[0] && textArr[1]) {
+        publishTimeText = `${textArr[1]}以前`
+      }
+      // 计算text
+      result.publishTimeText = publishTimeText
+    } else {
+      t.exact = val
+      result.publishTimeText = map[val] || ''
+    }
+    result.publishTime = t
+    return result
   }
-  if (notKey) {
-    formattedList.push({
-      label: '排除词:',
-      text: notKey.split(',').join(' ')
+
+  /**
+   * 信息类型整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:'采购意向,中标,成交,废标,流标'
+   */
+  static formatInfoType(val) {
+    const result = {
+      infoType: undefined,
+      infoTypeText: undefined
+    }
+    if (!val || val === '全部') return result
+    const arr = val.split(',').map((v) => {
+      // 把<拟建>替换成<拟建项目>
+      if (v === '拟建') {
+        return '拟建项目'
+      } else {
+        return v
+      }
     })
+    const obj = InfoTypeTransform.formatListToMap(arr)
+    result.infoType = InfoTypeTransform.mapToList(obj)
+
+    const map = InfoTypeTransform.listToMap(result.infoType)
+    result.infoTypeText = InfoTypeTransform.formatMapToList(map)
+    return {
+      infoType: result.infoType, // 恢复组件的数组内容
+      infoTypeText: result.infoTypeText ? result.infoTypeText.toString().replace(/,/g, ",") : ''// 文字
+    }
   }
-  if (buyerList) {
-    formattedList.push({
-      label: '采购单位:',
-      text: buyerList.split(',').join(' ')
-    })
+  /**
+   * 采购单位类型整理
+   * @param String val
+   * @returns Object
+   *
+   * 参数val示例:'住建,传媒,建筑业,能源化工,批发零售,信息技术,运输物流,制造业,住宿餐饮'
+   */
+  static formatBuyerClass(val) {
+    const result = {
+      buyerClass: undefined,
+      buyerClassText: undefined
+    }
+    if (!val || val === '全部') return result
+    result.buyerClass = val.split(',')
+
+    // 逻辑和行业类似,如果二级全选,则展示一级分类文字,否则展示二级分类文字
+    // 1. 把选中的整理成原始数据结构
+    const keyList = []
+    const keyListMap = {}
+    for (const key in buyerclassListMapExp) {
+      keyList.push(key)
+      if (val.indexOf(key) !== -1) {
+        // 存在
+        if (Array.isArray(keyListMap[key])) {
+          keyListMap[key] = []
+        }
+      }
+      if (Array.isArray(buyerclassListMapExp[key])) {
+        // 循环二级子项
+        buyerclassListMapExp[key].forEach((item) => {
+          if (val.indexOf(item) !== -1) {
+            // 存在
+            if (Array.isArray(keyListMap[key])) {
+              keyListMap[key].push(item)
+            } else {
+              keyListMap[key] = [item]
+            }
+          }
+        })
+      }
+    }
+    // 判断是否某一项全选了
+    let buyerClassText = []
+    for (const key in keyListMap) {
+      if (keyListMap[key].length === buyerclassListMapExp[key].length) {
+        buyerClassText.push(key)
+      } else {
+        buyerClassText = [...buyerClassText, ...keyListMap[key]]
+      }
+    }
+    if (buyerClassText.length) {
+      result.buyerClassText = buyerClassText ? buyerClassText.toString().replace(/,/g, ",") : ''
+    }
+    return {
+      buyerClass: result.buyerClass,
+      buyerClassText: result.buyerClassText,
+      keyListMap: keyListMap
+    }
   }
-  if (winnerList) {
-    formattedList.push({
-      label: '中标企业:',
-      text: winnerList.split(',').join(' ')
+
+  /**
+   * 处理联系方式
+   * @param val
+   * @returns {{basicDataText: undefined, basicData: undefined}}
+   */
+  static formatContact(val = '') {
+    const result = {
+      basicData: undefined,
+      basicDataText: undefined
+    }
+    const map = {}
+    biddingSearchConcat.forEach((item) => {
+      map[item.key] = item.label
     })
+
+    if (val) {
+      result.basicData = [val]
+      result.basicDataText = map[val]
+    }
+    return result
   }
-  if (agencyList) {
-    formattedList.push({
-      label: '代理机构:',
-      text: agencyList.split(',').join(' ')
-    })
+
+  /**
+   * 处理-附件
+   * @param val
+   * @returns {string}
+   */
+  static formatAttach (val) {
+    if (val == 1 || val == '1') {
+      return '有附件'
+    } else if (val == -1 || val == '-1') {
+      return '无附件'
+    } else {
+      return '全部'
+    }
   }
-  return formattedList
 }
 
 
@@ -164,13 +476,13 @@ export class FilterHistoryViewModel2AjaxModel {
     // 地区整理
     // const { area, city } = this.formatAreaCity(map.area)
     // 金额筛选整理
-    const price = this.formatPrice(map.price)
+    const { minPrice, maxPrice } = this.formatPrice(map.price)
     // 时间筛选整理
     // const publishTime = this.formatTime(map.publishTime)
     // 信息类型
     const subtype = this.formatInfoType(map.subtype)
-    // 采购单位
-    const { buyerClass } = this.formatBuyerClass(map.buyerclass)
+    // 采购单位类型
+    const buyerClass = this.formatBuyerClass(map.buyerclass)
     // 包含关系、多个关键词
     const { additionalWords, wordsMode } = this.formatSelectMoreKey(map.selectMoreKey, map.additionalWords, map.wordsMode)
 
@@ -178,24 +490,24 @@ export class FilterHistoryViewModel2AjaxModel {
       searchvalue: map.input,
       selectType,
       industry,
-      minprice: map.minprice,
-      maxprice: map.maxprice,
-      publishtime: map.publishtime, // 发布时间
+      minprice: minPrice,
+      maxprice: maxPrice,
+      publishtime: map.publishTime, // 发布时间
       subtype,   // 信息类型
-      buyerclass: buyerClass, // 采购单位
+      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, // 附件
+      fileExists: map.fileExists.toString(), // 附件
       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, // 标讯搜索恢复数据可能要用到?
+      dateTime:  map.publishTime, // 标讯搜索恢复数据可能要用到?
     }
 
     // 删去undefined/null的项
@@ -276,12 +588,14 @@ export class FilterHistoryViewModel2AjaxModel {
    * 金额整理
    * @returns String
    */
-  static formatPrice(price = { start: '', end: '' }, split = '-') {
-    const { start, end } = price
-    if (start || end) {
-      return [start || '', end || ''].join(split)
-    } else {
-      return ''
+  static formatPrice(price, split = '-') {
+    let priceArr = []
+    if(price) {
+      priceArr = price.split(split)
+    }
+    return {
+      minPrice: priceArr[0] || '',
+      maxPrice: priceArr[1] || '',
     }
   }
 
@@ -365,7 +679,7 @@ export class FilterHistoryViewModel2AjaxModel {
    * 格式化包含关键词模式、包含关键词
    */
   static formatSelectMoreKey (selectMoreKey, additionalWords, wordsMode) {
-    let aWords = '' // 附加关键词筛选模式
+    let aWords = '' // 附加关键词
     let wMode = 0 // 附加关键词筛选模式
     if(selectMoreKey) {
       aWords = additionalWords ? additionalWords.join(',') : ''
@@ -377,3 +691,48 @@ export class FilterHistoryViewModel2AjaxModel {
     }
   }
 }
+
+
+/**
+ * 三级地区对象转换成,单个area、city、district
+ */
+function areaObjToSingle(obj, split = ',') {
+  const map = {
+    area: '',
+    city: '',
+    district: ''
+  }
+  if (!obj) return map
+  const area = []
+  let 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 cKey + '_' + temp
+              })
+              district = district.concat(resetArr)
+            }
+          }
+        }
+      }
+    }
+  }
+  return {
+    area: area.join(split),
+    city: city.join(split),
+    district: district.join(split)
+  }
+}

+ 172 - 0
apps/bigmember_pc/src/views/search/bidding/components/save-filter-dialog.vue

@@ -0,0 +1,172 @@
+<script setup>
+import { computed } from 'vue'
+import { SearchBidModel } from '../model'
+const { viewFilterParams: currentFilter } = SearchBidModel
+
+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.price"><span>价格区间:</span>{{ currentFilter.price }}</div>
+          <div class="save-value-bg" v-if="currentFilter.publishTime"><span>发布时间:</span>{{ currentFilter.publishTimeText }}</div>
+          <div class="save-value-bg" v-if="currentFilter.fileExists != '全部'"><span>附件:</span>{{ currentFilter.fileExists }}</div>
+        </div>
+        <div class="save-value-bg" v-if="currentFilter.regionMap"><span>项目地区:</span>{{ currentFilter.regionMap }}</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>
+.filter-dialog{
+  padding: 32px;
+  border-radius: 8px!important;
+}
+.filter-dialog > .el-dialog__header {
+  padding: 0;
+}
+.filter-dialog > .el-dialog__header > .el-dialog__title{
+  color: #1D1D1D;
+}
+.save-filter-dialog > .el-dialog__body{
+  padding: 0 0 32px!important;
+}
+.filter-dialog .filter-save-item{
+  display: flex;
+  margin-top: 20px;
+  line-height: 18px;
+}
+
+.filter-dialog .btn-group,
+.filter-messagebox .btn-group{
+  width: 132px;
+  height: 36px;
+  padding: 0;
+  border-radius: 6px;
+  font-size: 16px;
+}
+.filter-dialog .btn-group.confirm-btn,
+.filter-messagebox .btn-group.confirm-btn{
+  background: #2cb7ca;
+  margin-right: 52px;
+  border: 0;
+  color: #fff;
+}
+.filter-dialog .btn-group.confirm-btn:hover,
+.filter-dialog .btn-group.confirm-btn:focus,
+.filter-messagebox .btn-group.confirm-btn:hover,
+.filter-dialog .btn-group.confirm-btn:focus{
+  color: #fff;
+}
+.filter-data-container{
+  max-height: 400px;
+  overflow-y: scroll;
+  width: 694px;
+}
+.filter-dialog ::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 8px!important;
+}
+
+.filter-save-item .save-label{
+  min-width: 60px;
+  text-align: right;
+  color: #636467;
+  font-size: 12px;
+}
+.filter-save-item .save-value {
+  margin-left: 8px;
+  flex: 1;
+  color: #1D1D1D;
+  text-align: left;
+  font-size: 12px;
+}
+.filter-save-item .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;
+}
+.filter-save-item .save-value-bg > span{
+  color: #636467;
+}
+.filter-save-item .search_model{
+  margin-top: 4px;
+  font-size: 12px;
+  line-height: 18px;
+  color: #686868;
+}
+
+</style>

+ 145 - 0
apps/bigmember_pc/src/views/search/bidding/components/search-filter-header.vue

@@ -0,0 +1,145 @@
+<script setup>
+import { ref } from 'vue'
+import SaveFilterDialog from '../components/save-filter-dialog.vue'
+import { SearchBidModel } from '../model/index'
+const { onSaveFilter, saveFilterDialogVisible, saveFilterCancel, saveFilterConfirm} = SearchBidModel
+const fixedTop = ref(false)
+const showFilter = ref(true)
+function toggleFilter() {}
+</script>
+
+<template>
+  <div class="filter-header-container" :class="fixedTop ? 'fixed' : ''">
+    <div class="filter-header" :class="fixedTop ? 'fixed-top' : ''">
+      <div
+        class="f-h-label"
+        @click="toggleFilter"
+      >
+        <span>筛选条件</span>
+        <i class="iconfont icon-xiala highlight-text" :class="{ 'is-reverse': showFilter}"></i>
+      </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>
+    <save-filter-dialog
+      v-if='saveFilterDialogVisible'
+      :visible="saveFilterDialogVisible"
+      @cancel='saveFilterCancel'
+      @confirm='saveFilterConfirm'
+    ></save-filter-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+  //.filter-header-container {
+  //  &.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;
+  //
+  //    .fixed-top {
+  //      position: relative;
+  //      width: auto;
+  //      top: unset;
+  //      border-bottom: unset;
+  //    }
+  //  }
+  //}
+
+  .filter-header{
+    width:100%;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 16px 0 22px;
+    height: 50px;
+    line-height: 50px;
+    background: #fff;
+    border-bottom: 1px solid #ECECEC;
+    border-top-left-radius: 8px;
+    border-top-right-radius: 8px;
+
+    &.fixed-top{
+      position: fixed;
+      top: 52px;
+      width: 1200px;
+      z-index: 99;
+    }
+
+    //border-top: unset;
+    //border-left: unset;
+    //border-right: unset;
+    //background: transparent;
+
+    .f-h-label{
+      width: 100px;
+      padding-left: 10px;
+      background-repeat: no-repeat;
+      background-position: right center;
+      background-size: 16px 16px;
+      cursor: pointer;
+      & > span{
+        display: inline-block;
+        height: 100%;
+        font-size: 16px;
+        color: #2cb7ca;
+        border-bottom: 2px solid #2cb7ca;
+      }
+      //&.caret-up{
+      //  background-image: url("/images/biddingSearch/icon-caret-top-gold.png");
+      //}
+      //&.caret-down{
+      //  background-image: url("/images/biddingSearch/icon-caret-bottom-gold.png");
+      //}
+    }
+    .f-h-action{
+      display: flex;
+      align-items: center;
+
+      .action-item{
+        padding: 0 10px;
+        height: 30px;
+        line-height: 30px;
+        border-radius: 4px;
+        font-size: 14px;
+        text-align: center;
+        cursor: pointer;
+      }
+      .reset-item{
+        margin-right: 16px;
+        border: 1px solid #2cb7ca;
+        color: #2cb7ca;
+        background-color: #fff;
+      }
+      .has-item{
+        margin-right: 16px;
+        border: 1px solid #E0E0E0;
+        color: #1D1D1D;
+        background-color: #fff;
+      }
+      .save-item{
+        background: #2CB7CA;
+        border: 1px solid #2cb7ca;
+        color: #fff;
+      }
+    }
+    .icon-xiala{
+      display: inline-block;
+      font-size: 16px;
+      flex-shrink: 0;
+      transform: rotate(0deg);
+      transition: transform .5s;
+      &.is-reverse{
+        transform: rotate(180deg);
+      }
+    }
+  }
+</style>

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

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

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

@@ -90,7 +90,7 @@ const SearchBidMoreSchema = [
     }
   },
   {
-    key: 'priceScope',
+    key: 'price',
     label: '金额区间',
     defaultVal: '',
     _name: 'type',

+ 2 - 8
apps/bigmember_pc/src/views/search/bidding/index.vue

@@ -2,7 +2,7 @@
 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 searchFilterHeader from '@/views/search/bidding/components/search-filter-header.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'
@@ -34,15 +34,9 @@ const {
       <search-bid-header></search-bid-header>
     </div>
     <div class="search-bidding-filter-container b-rd-8px">
-      <searchHeaderTitle></searchHeaderTitle>
+      <searchFilterHeader></searchFilterHeader>
       <search-bid-filter></search-bid-filter>
     </div>
-
-    <div class="bg-white b-rd-8px p-16px m-t-16px m-b-16px">
-      <h1>当前筛选值:</h1>
-      {{ filterState }}
-    </div>
-
     <search-list
       class="search-bidding-list-container b-rd-8px"
       v-bind="searchListProps"

+ 17 - 6
apps/bigmember_pc/src/views/search/bidding/model/base.js

@@ -188,20 +188,26 @@ export default function () {
   /**
    * 保存、重置筛选条件部分
    **/
-  const { saveFilterDialog, filterSaveParams } = saveFilterActionsModel
+  const {
+    checkFilterPass,
+    filterSaveParams,
+    viewFilterParams,
+    saveFilterDialogVisible,
+    saveFilterCancel,
+    saveFilterConfirm
+  } = saveFilterActionsModel()
 
   /**
    * 保存筛选条件
    */
-  function onSaveFilter() {
+  async function onSaveFilter() {
     const originParams = Object.assign(
       {},
       filterState.value,
       inputKeywordsState.value
     )
-    const config = {
-      filter: originParams
-    }
+    const config = { filter: originParams }
+    await checkFilterPass(config)
   }
 
   return {
@@ -218,6 +224,11 @@ export default function () {
     doChangeSelect,
     doChangePageNum,
     doChangePageSize,
-    onSaveFilter
+    onSaveFilter, // 保存筛选条件
+    filterSaveParams, // 保存format后的筛选条件
+    viewFilterParams, // 保存提示框查看筛选条件
+    saveFilterDialogVisible, // 保存筛选条件弹窗
+    saveFilterConfirm,
+    saveFilterCancel
   }
 }

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

@@ -16,7 +16,7 @@ export function useSearchFilterModel() {
     // 附件
     fileExists: '',
     // 金额区间
-    priceScope: '',
+    price: '',
     // 采购单位类型
     buyerclass: {},
     // 采购单位联系方式

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

@@ -1,50 +1,64 @@
 import { computed, reactive, ref, getCurrentInstance } from 'vue'
 import { checkBiddingFilterPass } from '@/api/modules'
-import  { FilterHistoryViewModel2AjaxModel } from '@/utils'
+import  { FilterHistoryViewModel2AjaxModel, FilterHistoryAjaxModel2ViewModel} 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)
+   const { savedFilterListMaxCount: maxCount } = saveConfig
+   const saveFilterDialogVisible = ref(false)
+   let filterSaveParams = ref({})
+   let viewFilterParams = ref({})
 
   // 检测筛选条件是否可保存
-  //  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)
+   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(maxCount) {
+     //   return showToast(`对不起,最多可保存${maxCount}个筛选条件。`)
+     // }
+     const { data: id, error_code: code = 0, error_msg: msg } = await checkBiddingFilterPass(filterSaveParams.value)
+     if(code === 0) {
+       viewFilterParams.value = FilterHistoryAjaxModel2ViewModel.formatAll(filterSaveParams.value)
+       saveFilterDialogVisible.value = true
+
+     } else {
+      if (msg) {
+        if (msg.includes('无用户身份')) {
+          showToast('请登录')
+        } else {
+          showToast(msg)
+        }
+      }
+    }
+  }
+
+  // 取消保存筛选条件操作
+  function saveFilterCancel () {
+    saveFilterDialogVisible.value = false
+  }
+
+  // 确定保存筛选条件操作
+  function saveFilterConfirm () {
+    console.log(33333)
+  }
 
   return {
-    saveFilterDialog,
+    saveFilterDialogVisible,
     filterSaveParams,
+    viewFilterParams,
+    checkFilterPass,
+    saveFilterCancel,
+    saveFilterConfirm
   }
 }