Explorar o código

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

Signed-off-by: tangshizhe <48740614+tangshizhe@users.noreply.github.com>
tangshizhe hai 1 ano
pai
achega
62a4164b5b
Modificáronse 86 ficheiros con 3480 adicións e 441 borrados
  1. 31 30
      apps/bigmember_pc/.eslintrc.cjs
  2. 1 0
      apps/bigmember_pc/config/proxy.js
  3. 21 0
      apps/bigmember_pc/src/api/modules/business.js
  4. 2 0
      apps/bigmember_pc/src/api/modules/index.js
  5. 11 0
      apps/bigmember_pc/src/api/modules/message.js
  6. 8 0
      apps/bigmember_pc/src/api/modules/pay.js
  7. 37 0
      apps/bigmember_pc/src/api/modules/workspace.js
  8. BIN=BIN
      apps/bigmember_pc/src/assets/images/business/tip-bg.png
  9. BIN=BIN
      apps/bigmember_pc/src/assets/images/icon/fireworks.png
  10. BIN=BIN
      apps/bigmember_pc/src/assets/images/icon/icon_right2.png
  11. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/db.png
  12. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/my-claim.png
  13. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/my-collect.png
  14. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/my-customer.png
  15. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/my-gen.png
  16. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/my-project.png
  17. BIN=BIN
      apps/bigmember_pc/src/assets/images/workspace/qb.png
  18. 3 1
      apps/bigmember_pc/src/components/collect-info/CollectInfo.vue
  19. 2 1
      apps/bigmember_pc/src/components/common/ContentLayout.vue
  20. 0 1
      apps/bigmember_pc/src/components/forecast/ForeCast.vue
  21. 2 1
      apps/bigmember_pc/src/router/router-interceptors.js
  22. 6 0
      apps/bigmember_pc/src/router/routers.js
  23. 18 1
      apps/bigmember_pc/src/store/workspace.js
  24. 0 0
      apps/bigmember_pc/src/store/workspace/account-info.js
  25. 123 0
      apps/bigmember_pc/src/store/workspace/business-todo.js
  26. 6 2
      apps/bigmember_pc/src/store/workspace/common-use.js
  27. 60 0
      apps/bigmember_pc/src/store/workspace/important-news.js
  28. 60 0
      apps/bigmember_pc/src/store/workspace/industry-report.js
  29. 2 1
      apps/bigmember_pc/src/store/workspace/message.js
  30. 32 20
      apps/bigmember_pc/src/store/workspace/my-customer.js
  31. 2 2
      apps/bigmember_pc/src/store/workspace/subscribe.js
  32. 374 0
      apps/bigmember_pc/src/views/business/detail.vue
  33. 87 0
      apps/bigmember_pc/src/views/workspace/components/AccountInfo.vue
  34. 46 0
      apps/bigmember_pc/src/views/workspace/components/AnalysisReport.vue
  35. 193 0
      apps/bigmember_pc/src/views/workspace/components/BusinessToDo.vue
  36. 97 0
      apps/bigmember_pc/src/views/workspace/components/ChatList.vue
  37. 35 10
      apps/bigmember_pc/src/views/workspace/components/CommonUse.vue
  38. 3 0
      apps/bigmember_pc/src/views/workspace/components/MessageTips.vue
  39. 222 0
      apps/bigmember_pc/src/views/workspace/components/MyEquityList.vue
  40. 50 0
      apps/bigmember_pc/src/views/workspace/components/NewsList.vue
  41. 1 1
      apps/bigmember_pc/src/views/workspace/components/SubscribeList.vue
  42. 44 26
      apps/bigmember_pc/src/views/workspace/dashboard.vue
  43. 4 2
      apps/bigmember_pc/src/views/workspace/ui/ArticleItem.vue
  44. 139 0
      apps/bigmember_pc/src/views/workspace/ui/ChatItem.vue
  45. 112 0
      apps/bigmember_pc/src/views/workspace/ui/EquityItem.vue
  46. 6 4
      apps/bigmember_pc/src/views/workspace/ui/ListCard.vue
  47. 1 1
      apps/bigmember_pc/src/views/workspace/ui/WorkspaceCard.vue
  48. 12 0
      apps/mobile/src/api/modules/bigmember.js
  49. 19 0
      apps/mobile/src/api/modules/business.js
  50. 1 0
      apps/mobile/src/api/modules/index.js
  51. 23 13
      apps/mobile/src/api/modules/subscribe.js
  52. BIN=BIN
      apps/mobile/src/assets/image/business/tip-bg.png
  53. BIN=BIN
      apps/mobile/src/assets/image/icon/icon-phone.png
  54. BIN=BIN
      apps/mobile/src/assets/image/icon/icon-right2.png
  55. BIN=BIN
      apps/mobile/src/assets/image/public/goset.png
  56. 11 2
      apps/mobile/src/components/search/Layout.vue
  57. 2 1
      apps/mobile/src/components/search/bidding/filterHistoryDialog.vue
  58. 126 6
      apps/mobile/src/components/search/bidding/filters.vue
  59. 4 4
      apps/mobile/src/components/selector/keyword-input-group/index.vue
  60. 7 1
      apps/mobile/src/components/treasure-box/AllUse.vue
  61. 6 3
      apps/mobile/src/components/treasure-box/CommonUse.vue
  62. 12 0
      apps/mobile/src/router/modules/business.js
  63. 14 1
      apps/mobile/src/router/modules/tabbar.js
  64. 13 1
      apps/mobile/src/store/modules/treasureBox.js
  65. 21 4
      apps/mobile/src/ui/cell-card/index.vue
  66. 22 1
      apps/mobile/src/utils/format/modules/filter-history-formatter.js
  67. 310 0
      apps/mobile/src/views/business/Detail.vue
  68. 3 3
      apps/mobile/src/views/push/PushSetting.vue
  69. 17 0
      apps/mobile/src/views/search/filter-history/bidding.vue
  70. 13 1
      apps/mobile/src/views/search/middle/bidding/index.vue
  71. 43 3
      apps/mobile/src/views/search/result/bidding/index.vue
  72. 1 1
      apps/mobile/src/views/tabbar/Box.vue
  73. 1 1
      apps/mobile/src/views/tabbar/Home.vue
  74. 288 0
      apps/mobile/src/views/tabbar/RecommendedBannerlist.vue
  75. 442 154
      apps/mobile/src/views/tabbar/Subscribe.vue
  76. 15 4
      apps/mobile/src/views/treasurebox/FeatureSettings.vue
  77. 31 10
      apps/mobile/src/views/uersales/newuser/index.vue
  78. 1 0
      apps/mobile/vite.config.js
  79. 1 0
      apps/work-bench/.env.development
  80. 1 0
      apps/work-bench/.env.production
  81. 2 2
      apps/work-bench/public/index.html
  82. 0 49
      apps/work-bench/public/index.html.bak
  83. 1 1
      apps/work-bench/src/components/NavUserInfo.vue
  84. 1 1
      apps/work-bench/src/register-app.js
  85. 1 1
      packages/work-bench-frame/packages/components/Navbar/components/item.vue
  86. 174 69
      packages/work-bench-frame/packages/components/Navbar/index.vue

+ 31 - 30
apps/bigmember_pc/.eslintrc.cjs

@@ -1,30 +1,31 @@
-/* eslint-env node */
-require('@rushstack/eslint-patch/modern-module-resolution')
-module.exports = {
-  root: true,
-  env: {
-    browser: true,
-    node: true
-  },
-  globals: {
-    loginflag: true,
-    $: true,
-    JyObj: true,
-    _hmt: true,
-    vComponentChart: true
-  },
-  plugins: ['vue'],
-  rules: {
-    'vue/no-mutating-props': 'off',
-    'vue/multi-word-component-names': 'off',
-    'no-empty': ['error', { allowEmptyCatch: true }],
-    'no-unused-vars': 'off',
-    'no-console': 'off',
-    'no-debugger': 'warn'
-  },
-  extends: [
-    'plugin:vue/essential',
-    'eslint:recommended',
-    '@vue/eslint-config-prettier'
-  ]
-}
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+module.exports = {
+  root: true,
+  env: {
+    browser: true,
+    node: true
+  },
+  globals: {
+    loginflag: true,
+    $: true,
+    JyObj: true,
+    _hmt: true,
+    vComponentChart: true
+  },
+  plugins: ['vue'],
+  rules: {
+    'vue/no-mutating-props': 'off',
+    'vue/multi-word-component-names': 'off',
+    'no-empty': ['error', { allowEmptyCatch: true }],
+    'no-unused-vars': 'off',
+    'no-console': 'off',
+    'no-debugger': 'warn',
+    'no-useless-escape': 'off'
+  },
+  extends: [
+    'plugin:vue/essential',
+    'eslint:recommended',
+    '@vue/eslint-config-prettier'
+  ]
+}

+ 1 - 0
apps/bigmember_pc/config/proxy.js

@@ -16,6 +16,7 @@ const PrefixAPIS = [
   '/userCenter',
   '/jyMerge',
   '/jyapi',
+  '/message',
   // 资源
   '/commonFunctions',
   '/common-module'

+ 21 - 0
apps/bigmember_pc/src/api/modules/business.js

@@ -0,0 +1,21 @@
+import request from '@/api'
+import qs from 'qs'
+
+export function getBusinessDetails(data) {
+  data = qs.stringify(data)
+  return request({
+    baseURL: '/bigmember',
+    url: '/project/businessDetails',
+    method: 'POST',
+    data
+  })
+}
+
+// 是否需要完善信息提示
+export function isNeedCompleteInfo() {
+  return request({
+    baseURL: '/salesLeads',
+    url: '/businessRetain',
+    method: 'POST'
+  })
+}

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

@@ -27,3 +27,5 @@ export * from './course'
 export * from './jyMerge'
 export * from './crmApplication'
 export * from './pay'
+export * from './message'
+export * from './business'

+ 11 - 0
apps/bigmember_pc/src/api/modules/message.js

@@ -0,0 +1,11 @@
+import request from '@/api'
+
+// 获取聊天会话列表
+export function getUserSessionList(data) {
+  return request({
+    baseURL: '/jyapi',
+    url: '/message/userList',
+    method: 'post',
+    data
+  })
+}

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

@@ -48,3 +48,11 @@ export function filePackUseHistory(data) {
     data
   })
 }
+
+export function getUserAccountShow() {
+  return request({
+    baseURL: '/jypay',
+    url: '/user/account/show',
+    method: 'POST'
+  })
+}

+ 37 - 0
apps/bigmember_pc/src/api/modules/workspace.js

@@ -103,3 +103,40 @@ export function getWhatIsWorkspace() {
     method: 'get'
   })
 }
+
+// 工作台首页-要闻
+export function getWorkspaceNews() {
+  return request({
+    baseURL: '/front',
+    url: '/project/importantNews',
+    method: 'get'
+  })
+}
+
+// 工作台首页-行业分析简报
+export function getWorkspaceIndustryReport() {
+  return request({
+    baseURL: '/front',
+    url: '/project/deskAnalysisReport',
+    method: 'get'
+  })
+}
+
+// 工作台首页-我的企业关注、项目关注、标讯收藏、认领等权益数量(客户监控在另外接口返回)
+export function getWorkspaceEquityCount() {
+  return request({
+    baseURL: '/bigmember',
+    url: '/project/meMonitoring',
+    method: 'get'
+  })
+}
+
+// 工作台首页-商机情报、待办消息列表(getMessageCenterList接口简版)
+export function getBusinessTodoList(data) {
+  return request({
+    baseURL: '/jyapi/messageCenter',
+    url: '/WorkDeskList',
+    method: 'POST',
+    data
+  })
+}

BIN=BIN
apps/bigmember_pc/src/assets/images/business/tip-bg.png


BIN=BIN
apps/bigmember_pc/src/assets/images/icon/fireworks.png


BIN=BIN
apps/bigmember_pc/src/assets/images/icon/icon_right2.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/db.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/my-claim.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/my-collect.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/my-customer.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/my-gen.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/my-project.png


BIN=BIN
apps/bigmember_pc/src/assets/images/workspace/qb.png


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

@@ -531,7 +531,9 @@ export default {
         pc_list_monitor_more:
           '请升级大会员,可实时监控最多500个业主采购动态,洞察潜在商机。',
         pc_buyer_monitor_limit:
-          '监控业主数量已达上限,请升级大会员,可实时监控最多500个业主采购动态,洞察潜在商机。'
+          '监控业主数量已达上限,请升级大会员,可实时监控最多500个业主采购动态,洞察潜在商机。',
+        pc_project_businessDetails:
+          '请留下联系方式,我们会尽快联系您体验大会员全部功能!'
       },
       sourceDescMap: {
         pc_buyer_monitor_more: '采购单位画像页-超级订阅用户申请监控更多业主',

+ 2 - 1
apps/bigmember_pc/src/components/common/ContentLayout.vue

@@ -75,7 +75,8 @@ export default {
         bigvip_subreport_month: 'jy-pc-bigmember-month-content-right', // 数据月报右侧
         bigvip_subreport_week: 'jy-pc-bigmember-week-content-right', // 数据周报右侧
         ent_ser_portrait: 'jy-pc-bigmember-entportrayal-content-right',
-        custom_unit_portrayal: 'jy-pc-bigmember-unitportrayal-content-right'
+        custom_unit_portrayal: 'jy-pc-bigmember-unitportrayal-content-right',
+        business_detail: 'jy-pc-bigmember-businessdetail-content-right' // 商机情报详情页右侧code
       },
       adList: [
         // {

+ 0 - 1
apps/bigmember_pc/src/components/forecast/ForeCast.vue

@@ -525,7 +525,6 @@ import TimeSelector from '@/components/selector/TimeSelector.vue'
 import { moneyUnit, dateFormatter } from '@/utils'
 import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
 import { getPowerUrl } from '@/utils/power/redirect'
-import { getAssetsFile } from '@/utils'
 import {
   setFollowEnt,
   setCancelEnt,

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

@@ -40,7 +40,8 @@ const powerCheckWhiteList = [
   'unit_portrayal_id',
   'set-password',
   'portrayal_loading',
-  'recommen-list'
+  'recommen-list',
+  'business_detail'
 ]
 
 const regListCheck = function (regList, path) {

+ 6 - 0
apps/bigmember_pc/src/router/routers.js

@@ -251,5 +251,11 @@ export default [
     path: '/recommen-list',
     name: 'recommen-list',
     component: () => import('@/views/recommen-list/recommenList.vue')
+  },
+  // 商机情报-详情页
+  {
+    path: '/business_detail/:id',
+    name: 'business_detail',
+    component: () => import('@/views/business/detail.vue')
   }
 ]

+ 18 - 1
apps/bigmember_pc/src/store/workspace.js

@@ -9,6 +9,9 @@ import projectFollow from './workspace/project-follow'
 import entFollow from './workspace/ent-follow'
 import dataReport from './workspace/data-report'
 import asideOthers from './workspace/aside-others'
+import importantNews from './workspace/important-news'
+import industryReport from './workspace/industry-report'
+import businessTodo from './workspace/business-todo'
 
 export default {
   namespaced: true,
@@ -42,6 +45,17 @@ export default {
     dataReportShow(state, getters, rootState, rootGetters) {
       const { 'user/power': power } = rootGetters
       return Array.isArray(power) && power.includes(10)
+    },
+    // 业主监控显示(超级订阅、大会员自定义版(未购买“业主监控”服务)、大会员智慧版、大会员商机版1.0、大会员专家版1.0新版/老版商机管理、企业管理服务)
+    hasOwnerMonitoring(state, getters, rootState, rootGetters) {
+      const {
+        'user/svip': svip,
+        'user/entniche': entniche,
+        'user/isNewEntNiche': newEnt,
+        'user/bigmember': bigmember,
+        'user/isEntService': entService
+      } = rootGetters
+      return svip || entniche || newEnt || bigmember || entService
     }
   },
   modules: {
@@ -55,6 +69,9 @@ export default {
     entFollow,
     dataReport,
     asideOthers,
-    customerWatcher
+    customerWatcher,
+    importantNews,
+    industryReport,
+    businessTodo
   }
 }

+ 0 - 0
apps/bigmember_pc/src/store/workspace/account-info.js


+ 123 - 0
apps/bigmember_pc/src/store/workspace/business-todo.js

@@ -0,0 +1,123 @@
+import { getBusinessTodoList, clickMessage, readMark } from '@/api/modules'
+import { getAssetsFile } from '@/utils'
+import { dateMatter } from '@/utils/'
+
+export default {
+  namespaced: true,
+  state: () => ({
+    loading: true,
+    loaded: false,
+    businessList: [],
+    todoList: [],
+    showTodo: false
+  }),
+  mutations: {
+    changeShowTodo(state, data) {
+      state.showTodo = data
+    },
+    changeBusinessList(state, list = []) {
+      if (Array.isArray(list)) {
+        state.businessList = list
+      }
+    },
+    changeTodoList(state, list = []) {
+      if (Array.isArray(list)) {
+        state.todoList = list
+      }
+    },
+    changeLoading(state, f = false) {
+      state.loading = f
+    },
+    changeLoaded(state, f = false) {
+      state.loaded = f
+    }
+  },
+  actions: {
+    async getList({ dispatch }, payload) {
+      const defaultPrams = {
+        needDealtWithCount: true,
+        msgType: 5,
+        offset: 1,
+        size: 5
+      }
+      const params = Object.assign({}, defaultPrams, payload)
+      dispatch('getMessageList', params)
+    },
+    async getMessageList({ commit }, payload) {
+      try {
+        commit('changeLoading', true)
+        commit('changeLoaded', false)
+        const {
+          busData = [],
+          needDoData = [],
+          code
+        } = await getBusinessTodoList(payload)
+        if (code === 0) {
+          if (Array.isArray(busData)) {
+            busData.forEach((v) => {
+              v.time = dateMatter(v.createTime.replace(/-/g, '/'))
+              ;(v.img = getAssetsFile('workspace/qb.png')),
+                (v.dot = v.isRead === 0)
+            })
+            commit('changeBusinessList', busData)
+          }
+          if (Array.isArray(needDoData)) {
+            needDoData.forEach((v) => {
+              v.time = dateMatter(v.createTime.replace(/-/g, '/'))
+              ;(v.img = getAssetsFile('workspace/db.png')),
+                (v.dot = v.isRead === 0)
+            })
+            commit('changeTodoList', needDoData)
+          }
+          const { needDealtWithCount } = payload
+          if (needDealtWithCount) {
+            commit('changeShowTodo', !needDoData ? false : needDoData.length)
+          }
+        }
+        commit('changeLoading', false)
+        commit('changeLoaded', true)
+      } catch (error) {
+        commit('changeLoading', false)
+        commit('changeLoaded', true)
+        console.log(error)
+      }
+    },
+    // 标记已读
+    async remarkRead({ state, commit }, payload) {
+      await clickMessage({
+        msgLogId: payload.msgLogId,
+        platform: 1
+      })
+      if (payload.isRead === 0) {
+        try {
+          payload.isRead = 1
+          const params = {
+            msgId: payload.id ? Number(payload.id) : null
+          }
+          const { data } = await readMark(params)
+          if (data === 1) {
+            if (Array.isArray(state.businessList)) {
+              state.businessList.forEach((business) => {
+                if (business.id === payload.id) {
+                  business.dot = false
+                }
+              })
+              commit('changeBusinessList', state.businessList)
+            }
+            if (Array.isArray(state.todoList)) {
+              state.todoList.forEach((todo) => {
+                if (todo.id === payload.id) {
+                  todo.dot = false
+                }
+              })
+              commit('changeTodoList', state.todoList)
+            }
+          }
+        } catch (error) {
+          console.log(error)
+        }
+      }
+    }
+  },
+  getters: {}
+}

+ 6 - 2
apps/bigmember_pc/src/store/workspace/common-use.js

@@ -35,7 +35,7 @@ export default {
   namespaced: true,
   state: () => ({
     platform: 'PC',
-    maxCount: Infinity,
+    maxCount: 8,
     dialogShow: false,
     menuInfoList: [], // 请求回来的原始数组
     allFunctionsContainsUsable: [], // 全部功能,包含可用的和不可用的(扁平化后)
@@ -122,6 +122,9 @@ export default {
       if (Array.isArray(list)) {
         state.mainFunctions = list
       }
+    },
+    setCommonMaxCount(state, num) {
+      state.maxCount = num
     }
   },
   actions: {
@@ -183,7 +186,8 @@ export default {
           }
         )
         if (code === 0 && data) {
-          commit('setCommonList', data)
+          commit('setCommonList', data.list || [])
+          commit('setCommonMaxCount', data.num)
         } else {
           commit('setCommonList', [])
         }

+ 60 - 0
apps/bigmember_pc/src/store/workspace/important-news.js

@@ -0,0 +1,60 @@
+// 要闻
+import { getWorkspaceNews } from '@/api/modules'
+
+export default {
+  namespaced: true,
+  state: () => ({
+    params: {
+      pageNum: 0,
+      pageSize: 10
+    },
+    loading: true,
+    loaded: false,
+    list: []
+  }),
+  mutations: {
+    changeList(state, list = []) {
+      if (Array.isArray(list)) {
+        state.list = list
+      }
+    },
+    changeLoading(state, f = false) {
+      state.loading = f
+    },
+    changeLoaded(state, f = false) {
+      state.loaded = f
+    }
+  },
+  actions: {
+    async getList({ state, dispatch }) {
+      const { params } = state
+      await dispatch('doRequest', params)
+    },
+    async doRequest({ commit }, payload) {
+      try {
+        commit('changeLoading', true)
+        commit('changeLoaded', false)
+        const { data = [], error_code: code } = await getWorkspaceNews(payload)
+        if (code === 0 && data.length) {
+          const list = data.map((v) => {
+            return {
+              ...v,
+              // visited,
+              _id: v._id,
+              title: v.s_title,
+              time: v.time ? v.time : 0
+            }
+          })
+          commit('changeList', list)
+        }
+        commit('changeLoading', false)
+        commit('changeLoaded', true)
+        return data || []
+      } catch (error) {
+        commit('changeLoading', false)
+        commit('changeLoaded', true)
+        console.log(error)
+      }
+    }
+  }
+}

+ 60 - 0
apps/bigmember_pc/src/store/workspace/industry-report.js

@@ -0,0 +1,60 @@
+import { getWorkspaceIndustryReport } from '@/api/modules'
+
+export default {
+  namespaced: true,
+  state: () => ({
+    params: {
+      pageNum: 0,
+      pageSize: 10
+    },
+    loading: true,
+    loaded: false,
+    list: []
+  }),
+  mutations: {
+    changeList(state, list = []) {
+      if (Array.isArray(list)) {
+        state.list = list
+      }
+    },
+    changeLoading(state, f = false) {
+      state.loading = f
+    },
+    changeLoaded(state, f = false) {
+      state.loaded = f
+    }
+  },
+  actions: {
+    async getList({ state, dispatch }) {
+      const { params } = state
+      await dispatch('doRequest', params)
+    },
+    async doRequest({ commit }, payload) {
+      try {
+        commit('changeLoading', true)
+        commit('changeLoaded', false)
+        const { data = [], error_code: code } =
+          await getWorkspaceIndustryReport(payload)
+        if (code === 0 && data.length) {
+          const list = data.map((v) => {
+            return {
+              ...v,
+              // visited,
+              title: v.title,
+              url: v.url,
+              time: v.showTime ? v.showTime : 0
+            }
+          })
+          commit('changeList', list)
+        }
+        commit('changeLoading', false)
+        commit('changeLoaded', true)
+        return data || []
+      } catch (error) {
+        commit('changeLoading', false)
+        commit('changeLoaded', true)
+        console.log(error)
+      }
+    }
+  }
+}

+ 2 - 1
apps/bigmember_pc/src/store/workspace/message.js

@@ -54,7 +54,8 @@ export default {
         msgType: -1,
         isRead: 0,
         offset: 1,
-        size: 20
+        size: 20,
+        isfilterActive: true // P442新增参数:过滤活动类消息
       }
       dispatch('getMessageList', params)
     },

+ 32 - 20
apps/bigmember_pc/src/store/workspace/my-customer.js

@@ -21,7 +21,8 @@ export default {
     },
     loading: true,
     loaded: false,
-    list: []
+    list: [],
+    count: 0
   }),
   mutations: {
     changeList(state, list = []) {
@@ -34,6 +35,9 @@ export default {
     },
     changeLoaded(state, f = false) {
       state.loaded = f
+    },
+    changeCount(state, count) {
+      state.count = count
     }
   },
   actions: {
@@ -45,25 +49,33 @@ export default {
       try {
         commit('changeLoading', true)
         commit('changeLoaded', false)
-        const { data = {}, error_code: code } = await customerQuery(payload)
-        if (code === 0 && data && Array.isArray(data.list)) {
-          const list = data.list.slice(0, 5).map((v) => {
-            // const visited = this.pathVisited(
-            //   this.createPathItem(
-            //     '/myCustomer/*',
-            //     `id=${v.customer_id}
-            //   )
-            // )
-            return {
-              ...v,
-              // visited,
-              _id: v.customer_id,
-              title: v.customer_name,
-              unread: false,
-              time: v.updatetime ? +new Date(v.updatetime * 1000) : 0
-            }
-          })
-          commit('changeList', list)
+        const {
+          data = {},
+          list = [],
+          error_code: code
+        } = await customerQuery(payload)
+        if (code === 0) {
+          if (Array.isArray(list)) {
+            const newList = list.slice(0, 5).map((v) => {
+              // const visited = this.pathVisited(
+              //   this.createPathItem(
+              //     '/myCustomer/*',
+              //     `id=${v.customer_id}
+              //   )
+              // )
+              return {
+                ...v,
+                // visited,
+                _id: v.customer_id,
+                title: v.customer_name,
+                unread: false,
+                time: v.updatetime ? +new Date(v.updatetime * 1000) : 0
+              }
+            })
+            commit('changeList', newList)
+          }
+          // 客户监控数量
+          commit('changeCount', data?.count)
         }
         commit('changeLoading', false)
         commit('changeLoaded', true)

+ 2 - 2
apps/bigmember_pc/src/store/workspace/subscribe.js

@@ -5,7 +5,7 @@ export default {
   state: () => ({
     params: {
       pageNum: 1,
-      pageSize: 5,
+      pageSize: 10,
       format: 'table',
       area: '',
       time: ''
@@ -71,7 +71,7 @@ export default {
               _id: v._id,
               title: v.title,
               unread: false,
-              time: v.publishtime ? v.publishtime * 1000 : 0
+              time: v.publishTime ? v.publishTime * 1000 : 0
             }
           })
           // commit('changeHasKeyState', hasKey)

+ 374 - 0
apps/bigmember_pc/src/views/business/detail.vue

@@ -0,0 +1,374 @@
+<template>
+  <Layout class="business-detail-page">
+    <div class="business-header">
+      <div class="name">
+        {{ info.title }}
+      </div>
+      <div class="business-sub-row">
+        <div class="d-m-time common-time">
+          {{ dateFromNow(new Date(publishtime).getTime()) }}
+        </div>
+        <div class="business_option">
+          <div class="potential-col">
+            提前介入,把握商机,戳我查看
+            <em class="handle-em" @click="goPotentialPage">潜在项目预测</em
+            ><i class="icon_right"></i>
+          </div>
+        </div>
+      </div>
+    </div>
+    <section class="collect-info-tip" v-if="needComplete">
+      【商机情报】想获得更精准商机情报?立即<em
+        class="handle-em"
+        @click="completeInfo"
+        >完善信息</em
+      >。您也可以<em class="handle-em" @click="openCustomer">联系客服</em
+      >进行相关咨询。
+    </section>
+    <div class="business-content">
+      <section class="poten-box">
+        <div class="box-title">
+          <span class="left-line"></span>
+          <span>预测项目</span>
+        </div>
+        <div class="box-con">
+          <div class="list_name">
+            <span class="pur_company">采购单位</span>
+            <span class="list_pur_name">{{ info.buyer }}</span>
+          </div>
+          <div class="list_poten">
+            <div class="poten_unit">
+              <span class="poten_label"
+                ><span class="point"></span>预测线索</span
+              >
+              <span class="poten_name">{{
+                info.title && info.title.indexOf('】')
+                  ? info.title.substring(info.title.indexOf('】') + 1)
+                  : info.title
+              }}</span>
+            </div>
+            <div class="poten_unit mt8">
+              <span class="poten_label"
+                ><span class="point"></span>预测采购内容</span
+              >
+              <span class="poten_name">
+                <span>{{ info.purchasing }}</span>
+              </span>
+            </div>
+            <div class="poten_unit mt8">
+              <span class="poten_label">
+                <span class="point"></span>预测采购时间:
+                <span style="color: #1d1d1d">{{
+                  dateFormatter(yuceendtime, 'yyyy-MM-dd')
+                }}</span>
+              </span>
+            </div>
+          </div>
+        </div>
+      </section>
+      <section class="similar-box mt8" v-if="similarProject">
+        <div class="box-title">
+          <span class="left-line"></span>
+          <span>同类项目</span>
+        </div>
+        <div class="box-con">
+          <div class="pur_unit">
+            <span class="unit_label">同类项目:</span>
+            <span
+              class="unit_name handle-em"
+              @click="goViewDetail(similarProject.p_id)"
+              >{{ similarProject.p_orther }}</span
+            >
+          </div>
+          <div class="pur_unit mt8">
+            <span class="unit_label">联系人:</span>
+            <span class="unit_name">{{ similarProject.p_person }}</span>
+          </div>
+          <div class="pur_unit mt8">
+            <span class="unit_label">联系电话:</span>
+            <span class="unit_name">{{ similarProject.p_phone }}</span>
+          </div>
+        </div>
+      </section>
+    </div>
+    <!-- 留资弹窗 -->
+    <CollectInfo ref="collectRef"></CollectInfo>
+  </Layout>
+</template>
+
+<script>
+import Layout from '@/components/common/ContentLayout.vue'
+import CollectInfo from '@/components/collect-info/CollectInfo.vue'
+import { dateFromNow, dateFormatter } from '@/utils/'
+import { mapGetters } from 'vuex'
+import tdk from '@/utils/mixins/set-tdk.js'
+import { getBusinessDetails, isNeedCompleteInfo } from '@/api/modules/'
+export default {
+  name: 'business-opp-detail',
+  mixins: [tdk],
+  components: {
+    Layout,
+    CollectInfo
+  },
+  data() {
+    return {
+      info: {},
+      needComplete: false
+    }
+  },
+  computed: {
+    ...mapGetters('user', [
+      'bigmember' // 是否大会员
+    ]),
+    publishtime() {
+      return this.info.publishtime ? this.info.publishtime * 1000 : 0
+    },
+    yuceendtime() {
+      return this.info.yuceendtime ? this.info.yuceendtime * 1000 : 0
+    },
+    similarProject() {
+      return this.info.results && this.info.results[0]
+        ? this.info.results[0]
+        : {}
+    }
+  },
+  created() {
+    this.isNeedComplete()
+    this.getDetailInfo()
+  },
+  mounted() {
+    window.openCustomer = this.openCustomer
+  },
+  methods: {
+    dateFromNow,
+    dateFormatter,
+    setTdkInfo() {
+      const name = this.info.title
+      this.setPageTdkToView({
+        title: `${name}招标采购_中标信息 - 剑鱼标讯`,
+        keywords: `${name} ,${name} 招投标,${name} 招标采购,${name}中标信息,${name}通讯录,剑鱼标讯`,
+        description: `剑鱼标讯为您提供${name}相关的工商企业信息及招投标、中标信息服务,涵盖工商企业信息、企业通讯录、公司中标信息、项目动态、年度项目统计、月度中标金额统计、市场区域及客户分布等一系列相关信息服务,全面了解${name},就上剑鱼标讯官网。`
+      })
+    },
+    // 是否需要完善信息
+    async isNeedComplete() {
+      const { error_code: code, data } = await isNeedCompleteInfo()
+      if (code === 0) {
+        this.needComplete = !!data
+      }
+    },
+    // 获取信息详情
+    async getDetailInfo() {
+      const id = this.$route.params?.id || ''
+      const { error_code: code, data = {} } = await getBusinessDetails({ id })
+      if (code === 0 && data) {
+        this.info = data
+        this.setTdkInfo()
+      }
+    },
+    goViewDetail(id) {
+      const routeUrl = this.$router.resolve({
+        path: '/pro_follow_detail?sid=' + id
+      })
+      return window.open(routeUrl.href, '_blank')
+    },
+    // 完善信息
+    completeInfo() {
+      window.location.href =
+        '/swordfish/frontPage/user/sess/set_favorite?source=pc_project_businessDetails_improve'
+    },
+    // 联系客服
+    openCustomer() {
+      this.contactCustomer(this)
+    },
+    // 跳转潜在项目预测(无权限用户引导大会员留资,大会员引导跳转到潜在项目预测页面。)
+    goPotentialPage() {
+      if (this.bigmember) {
+        window.open('/swordfish/page_big_pc/forecast_list')
+      } else {
+        this.$refs.collectRef.isNeedSubmit('pc_project_businessDetails')
+      }
+    }
+  }
+}
+</script>
+<style lang="scss">
+.business-detail-page {
+  > .content-right.ad-container {
+    width: 200px !important;
+  }
+}
+</style>
+<style lang="scss" scoped>
+.business-detail-page {
+  margin: 32px auto;
+  .business-header {
+    position: relative;
+    padding: 24px 40px;
+    background: #fff;
+    line-height: 36px;
+    border-radius: 8px;
+    .name {
+      font-size: 24px;
+      line-height: 36px;
+      color: #252627;
+    }
+    .business-sub-row {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-top: 12px;
+    }
+    .common-time {
+      margin-left: 8px;
+      font-size: 12px;
+      font-family: Microsoft YaHei-Regular, Microsoft YaHei;
+      font-weight: 400;
+      color: #999999;
+      line-height: 18px;
+    }
+    .business_option {
+      display: flex;
+      align-items: center;
+    }
+    .potential-col {
+      padding: 4px 12px;
+      border-radius: 4px;
+      border: 1px solid #87dfea;
+      background: #eaf8fa;
+      font-size: 14px;
+      line-height: 22px;
+      margin-right: 16px;
+    }
+    .icon_right {
+      display: inline-block;
+      width: 12px;
+      height: 12px;
+      background: url('~@/assets/images/icon/icon_right2.png') no-repeat center;
+      background-size: contain;
+      margin-left: 4px;
+    }
+  }
+  .collect-info-tip {
+    font-size: 16px;
+    color: #1d1d1d;
+    line-height: 24px;
+    padding: 16px 0 16px 40px;
+    background: #fff url('~@/assets/images/business/tip-bg.png') no-repeat
+      center right;
+    background-size: contain;
+    margin-top: 16px;
+    border-radius: 8px;
+  }
+  .business-content {
+    background: #fff;
+    border-radius: 8px;
+    padding: 32px 40px 32px 0;
+    margin-top: 16px;
+
+    .poten-box {
+      .list_name {
+        display: flex;
+        align-items: center;
+        margin-bottom: 12px;
+
+        .pur_company {
+          margin-right: 8px;
+          padding: 2px 8px;
+          width: 68px;
+          height: 24px;
+          background: rgba(245, 101, 0, 0.1);
+          border-radius: 2px;
+          font-size: 13px;
+          font-weight: 400;
+          text-align: center;
+          color: #f56500;
+          line-height: 20px;
+        }
+
+        .list_pur_name {
+          color: #171826;
+          font-size: 16px;
+          line-height: 24px;
+        }
+      }
+
+      .list_poten {
+        display: flex;
+        flex-direction: column;
+
+        .poten_unit {
+          display: flex;
+          flex-direction: column;
+
+          .poten_label {
+            display: flex;
+            align-items: center;
+            font-size: 14px;
+            color: #999999;
+            line-height: 22px;
+
+            .point {
+              display: flex;
+              margin-right: 9px;
+              width: 4px;
+              height: 4px;
+              background: #ececec;
+              border-radius: 50%;
+            }
+          }
+
+          .poten_name {
+            margin-left: 12px;
+            font-size: 14px;
+            color: #1d1d1d;
+            line-height: 22px;
+          }
+        }
+      }
+    }
+
+    .similar-box {
+      .pur_unit {
+        font-size: 14px;
+        line-height: 22px;
+        .unit_label {
+          display: inline-block;
+          color: #999;
+          width: 70px;
+          text-align: right;
+        }
+        .unit_name {
+          color: #1d1d1d;
+        }
+      }
+    }
+    .box-title {
+      display: flex;
+      align-items: center;
+      font-size: 20px;
+      color: #1d1d1d;
+
+      .left-line {
+        display: inline-block;
+        background: #2abed1;
+        width: 3px;
+        height: 24px;
+        border-radius: 0 2px 2px 0;
+        margin-right: 37px;
+      }
+    }
+    .box-con {
+      padding: 8px 40px;
+      margin-top: 8px;
+    }
+    .mt8 {
+      margin-top: 8px;
+    }
+  }
+  .handle-em {
+    color: #2abed1 !important;
+    cursor: pointer;
+  }
+}
+</style>

+ 87 - 0
apps/bigmember_pc/src/views/workspace/components/AccountInfo.vue

@@ -0,0 +1,87 @@
+<template>
+  <section class="user-info-card">
+    <h4 class="user-info-title">
+      <span class="user-info-title-fireworks"></span>
+      <span class="user-info-title-text highlight-text"
+        >欢迎您!<i class="user-nickname">{{ accountInfo.nickName }}</i></span
+      >
+    </h4>
+    <p class="user-info-line user-info-type">
+      <span class="user-info-line-label">账号类型:</span>
+      <span class="user-info-line-value">{{ accountInfo.vipType }}</span>
+    </p>
+    <p class="user-info-line user-info-time" v-if="accountInfo.vipEntTime">
+      <span class="user-info-line-label">会员服务到期时间:</span>
+      <span class="user-info-line-value">{{ accountInfo.vipEntTime }}</span>
+    </p>
+  </section>
+</template>
+
+<script>
+import { getUserAccountShow } from '@/api/modules/'
+export default {
+  name: 'UserAccount',
+  data() {
+    return {
+      accountInfo: {
+        nickName: '',
+        vipType: '',
+        vipEntTime: ''
+      }
+    }
+  },
+  mounted() {
+    this.getAccount()
+  },
+  methods: {
+    async getAccount() {
+      const { data } = await getUserAccountShow()
+      if (data) {
+        const { nickname, vipType, vipEntTime } = data
+        this.accountInfo.nickName = nickname
+        this.accountInfo.vipType = vipType
+        this.accountInfo.vipEntTime = vipEntTime
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.user-info-card {
+  padding: 12px 20px;
+  margin-bottom: 16px;
+  border-radius: 8px;
+  background: #fff;
+  .user-info-title {
+    display: flex;
+    align-items: center;
+    margin-bottom: 6px;
+    &-fireworks {
+      display: inline-block;
+      width: 24px;
+      height: 24px;
+      margin-right: 8px;
+      background: url('~@/assets/images/icon/fireworks.png') no-repeat center;
+      background-size: contain;
+    }
+    &-text {
+      font-size: 16px;
+      line-height: 24px;
+    }
+  }
+  .user-info-line {
+    font-size: 14px;
+    line-height: 22px;
+    &-label {
+      color: #999999;
+    }
+    &-value {
+      color: #1d1d1d;
+    }
+  }
+  .user-info-type {
+    margin-bottom: 4px;
+  }
+}
+</style>

+ 46 - 0
apps/bigmember_pc/src/views/workspace/components/AnalysisReport.vue

@@ -0,0 +1,46 @@
+<template>
+  <ListCard
+    class="news-list"
+    :list="list"
+    title="分析报告"
+    @clickListItem="clickListItem"
+    @linkMore="linkMore"
+    :loading="loading"
+    :loaded="loaded"
+  >
+    <div slot="empty-content" class="empty-content">
+      <p>暂无数据</p>
+    </div>
+  </ListCard>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+import ListCard from '../ui/ListCard'
+
+export default {
+  name: 'AnalysisReport',
+  components: {
+    ListCard
+  },
+  computed: {
+    ...mapState({
+      loading: (state) => state.workspace.industryReport.loading,
+      loaded: (state) => state.workspace.industryReport.loaded,
+      list: (state) => state.workspace.industryReport.list
+    })
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    ...mapActions('workspace/industryReport', ['getList']),
+    clickListItem(item) {
+      window.open(item.url)
+    },
+    linkMore() {
+      window.open('/hybg/')
+    }
+  }
+}
+</script>

+ 193 - 0
apps/bigmember_pc/src/views/workspace/components/BusinessToDo.vue

@@ -0,0 +1,193 @@
+<template>
+  <section class="business-todo-card">
+    <header class="card-header">
+      <div class="card-header-tabs">
+        <span
+          :class="{
+            active: activeName === tab.type,
+            only: computedTabs.length === 1
+          }"
+          v-for="tab in computedTabs"
+          :key="tab.type"
+          @click="onTabClick(tab.type)"
+          >{{ tab.name }}</span
+        >
+      </div>
+      <div class="card-header-more" v-if="showMore" @click="linkMore">
+        <span class="more-text">更多</span>
+        <span class="el-icon-jy-blue-more"></span>
+      </div>
+    </header>
+    <transition name="el-fade-in-linear">
+      <div v-loading="loading">
+        <main class="card-main" v-show="activeName === 5">
+          <div class="chat-list">
+            <ChatItem
+              v-for="(item, index) in businessList"
+              :key="index"
+              v-bind="item"
+              @click="onBusinessItem(item)"
+            ></ChatItem>
+          </div>
+          <div class="empty-wrapper" v-show="!businessList.length && loaded">
+            <Empty class="empty-mini" direction="row" :mtb60="false">
+              <slot name="empty-content">暂无数据</slot>
+            </Empty>
+          </div>
+        </main>
+        <main class="card-main" v-show="activeName === 11">
+          <div class="chat-list">
+            <ChatItem
+              v-for="(item, index) in todoList"
+              :key="index"
+              v-bind="item"
+              @click="onBusinessItem(item)"
+            ></ChatItem>
+          </div>
+          <div class="empty-wrapper" v-show="!todoList.length && loaded">
+            <Empty class="empty-mini" direction="row" :mtb60="false">
+              <slot name="empty-content">暂无数据</slot>
+            </Empty>
+          </div>
+        </main>
+      </div>
+    </transition>
+  </section>
+</template>
+
+<script>
+import ChatItem from '../ui/ChatItem.vue'
+import Empty from '@/components/common/Empty.vue'
+import { mapState, mapActions } from 'vuex'
+import { debounce } from 'lodash'
+
+export default {
+  name: 'BusinessToDo',
+  components: {
+    ChatItem,
+    Empty
+  },
+  data() {
+    return {
+      tabs: [
+        { name: '商机情报', type: 5 },
+        { name: '待办', type: 11 }
+      ],
+      activeName: 5
+    }
+  },
+  computed: {
+    ...mapState({
+      loading: (state) => state.workspace.businessTodo.loading,
+      loaded: (state) => state.workspace.businessTodo.loaded,
+      showTodo: (state) => state.workspace.businessTodo.showTodo,
+      businessList: (state) => state.workspace.businessTodo.businessList,
+      todoList: (state) => state.workspace.businessTodo.todoList
+    }),
+    showMore() {
+      const businessType = this.activeName === 5
+      const todoLength = this.todoList.length
+      const businessLength = this.businessList.length
+      return businessType ? businessLength : todoLength
+    },
+    // P442需求:待办为空时,不展示该分类
+    computedTabs() {
+      return this.tabs.filter((item) => {
+        return this.showTodo ? item : item.type !== 11
+      })
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    ...mapActions('workspace/businessTodo', ['getList', 'remarkRead']),
+    linkMore() {
+      const type = this.activeName
+      window.open(`/swordfish/frontPage/messageCenter/sess/index?type=${type}`)
+    },
+    onTabClick(msgType) {
+      this.activeName = msgType
+    },
+    onBusinessItem: debounce(async function (item) {
+      try {
+        await this.remarkRead(item)
+        window.open(item.link)
+      } catch (error) {
+        window.open(item.link)
+      }
+    }, 300)
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@include diy-icon('blue-more', 16, 16);
+.business-todo-card {
+  flex: 1;
+  max-width: 49%;
+  border-radius: 8px;
+  background: #fff;
+  box-shadow: 0px 0px 18px 0px rgba(0, 0, 0, 0.02);
+  .card-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 10px 16px 10px 0;
+    &-tabs {
+      span {
+        display: inline-block;
+        padding: 0 16px;
+        font-size: 18px;
+        line-height: 28px;
+        color: #686868;
+        cursor: pointer;
+      }
+      .active {
+        position: relative;
+        color: #1d1d1d;
+        &::after {
+          position: absolute;
+          content: '';
+          left: 16px;
+          right: 16px;
+          bottom: -4px;
+          height: 2px;
+          background: $color_main;
+        }
+      }
+      .only {
+        color: #1d1d1d;
+        &::after {
+          display: none;
+        }
+      }
+    }
+    &-more {
+      display: flex;
+      align-items: center;
+      font-size: 14px;
+      color: $color_main;
+      line-height: 22px;
+      cursor: pointer;
+      .more-text {
+        margin-right: 4px;
+      }
+    }
+  }
+  .card-main {
+    padding: 0 12px 16px;
+    min-height: 246px;
+  }
+  ::v-deep {
+    .chat-item {
+      .chat-item-l {
+        width: 32px;
+        height: 32px;
+        background: transparent;
+        margin-right: 12px;
+      }
+    }
+  }
+}
+</style>

+ 97 - 0
apps/bigmember_pc/src/views/workspace/components/ChatList.vue

@@ -0,0 +1,97 @@
+<template>
+  <section class="chat-card">
+    <header class="chat-card-header">
+      <h4>聊天</h4>
+    </header>
+    <main class="chat-card-main">
+      <div class="chat-list">
+        <ChatItem
+          v-for="(item, index) in list"
+          :key="index"
+          v-bind="item"
+          @click="onClickItem(item)"
+        ></ChatItem>
+      </div>
+    </main>
+  </section>
+</template>
+
+<script>
+import ChatItem from '../ui/ChatItem'
+import { getUserSessionList } from '@/api/modules/'
+import { dateFormatter } from '@/utils/'
+export default {
+  name: 'ChatList',
+  components: {
+    ChatItem
+  },
+  data() {
+    return {
+      list: []
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      const { data } = await getUserSessionList({
+        userType: 2,
+        queryType: 2,
+        page: 1,
+        size: 3
+      })
+      if (data) {
+        const list = data.map((v) => {
+          if (v.type === 3) {
+            v.content = '[图片]'
+          } else if (v.type === 4) {
+            v.content = '[附件]'
+          }
+          return {
+            id: v.userId,
+            userType: v.userType,
+            title: v.name,
+            content: v.content
+              ? v.content
+                  .replace(/<img.*?src=[\"|\']?(.*?)[\"|\']*?>/g, '[图片]')
+                  .replace(/<[^>]+>/g, '')
+              : '',
+            img: v.headimg,
+            name: v.headimg ? '' : this.getName(v.name),
+            time: v.create_time
+              ? dateFormatter(v.create_time * 1000, 'yyyy-MM-dd')
+              : ''
+          }
+        })
+        this.list = list
+      }
+    },
+    getName(name) {
+      if (!name) return
+      return name.slice(-2)
+    },
+    onClickItem(item) {
+      window.open(
+        `/page_pc_social/customer?userId=${item.id}&userType=${item.userType}`
+      )
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.chat-card {
+  margin-bottom: 16px;
+  border-radius: 8px;
+  background: #fff;
+  &-header {
+    padding: 12px 20px 8px;
+    font-size: 18px;
+    line-height: 28px;
+  }
+  .chat-list {
+    padding: 0 12px 8px;
+  }
+}
+</style>

+ 35 - 10
apps/bigmember_pc/src/views/workspace/components/CommonUse.vue

@@ -1,7 +1,14 @@
 <template>
-  <WorkspaceCard class="work-common" title="常用功能">
+  <WorkspaceCard class="work-common">
+    <span slot="header-title">
+      我的常用
+      <span class="header-title-count"
+        >(<i class="highlight-text">{{ commonList.length }}</i
+        >/{{ maxCount }})</span
+      >
+    </span>
     <span slot="header-right" class="header-right-set" @click="setCommonFun"
-      ><i class="icon-set-img"></i> 设置</span
+      ><i class="icon-set-img"></i> 自定义</span
     >
     <div class="common-lists">
       <div
@@ -78,7 +85,10 @@
         @scroll="handleScroll($event)"
       >
         <div class="added-function">
-          <h3 class="added-title">已添加({{ addedList.length }})</h3>
+          <h3 class="added-title">
+            已添加(<span class="highlight-text">{{ addedList.length }}</span
+            >/{{ maxCount }})
+          </h3>
           <transition-group
             class="added-container"
             name="drag"
@@ -210,7 +220,8 @@ export default {
       enterIndex: '',
       tabActive: 0,
       tabFixed: false,
-      isScrollAnchor: true
+      isScrollAnchor: true,
+      calcList: []
     }
   },
   computed: {
@@ -415,6 +426,9 @@ export default {
       this.updateMainFunStatus(item)
     },
     onAddFun(item) {
+      if (this.addedList.length >= this.maxCount) {
+        return this.$toast(`已添加满${this.maxCount}个,请移除部分功能后再添加`)
+      }
       item.status = true
       this.addedList.unshift(item)
       this.updateMainFunStatus(item)
@@ -746,6 +760,14 @@ $main: #2cb7ca;
       }
     }
   }
+  .header-title-count {
+    font-size: 14px;
+    line-height: 22px;
+    color: #686868;
+  }
+  .card-content {
+    padding: 4px 16px 8px;
+  }
 }
 .icon-box-container {
   display: flex;
@@ -793,18 +815,21 @@ $main: #2cb7ca;
   cursor: pointer;
 }
 .common-lists {
-  padding: 0 20px;
+  height: 112px;
   display: flex;
+  overflow: hidden;
   flex-wrap: wrap;
   .list-item,
   .list-add {
-    width: 120px;
-    padding: 18px 0 24px;
+    width: 110px;
+    height: 112px;
+    padding: 8px 0;
     display: flex;
     flex-direction: column;
     align-items: center;
     text-align: center;
     cursor: pointer;
+    flex-shrink: 0;
   }
   .list-item {
     // flex: 1;
@@ -820,9 +845,9 @@ $main: #2cb7ca;
     font-size: 14px;
     line-height: 20px;
     color: #1d1d1d;
-    @media only screen and (max-width: 1280px) {
-      font-size: 12px;
-    }
+    // @media only screen and (max-width: 1280px) {
+    //   font-size: 12px;
+    // }
   }
   .item-img {
     width: 44px;

+ 3 - 0
apps/bigmember_pc/src/views/workspace/components/MessageTips.vue

@@ -111,6 +111,9 @@ export default {
   .el-carousel__container {
     height: 141px;
   }
+  .card-content{
+    padding: 2px 20px 16px;
+  }
 }
 
 .sub-text {

+ 222 - 0
apps/bigmember_pc/src/views/workspace/components/MyEquityList.vue

@@ -0,0 +1,222 @@
+<template>
+  <section class="my-equity-list">
+    <header class="card-header">我的</header>
+    <main class="card-main">
+      <EquityItem
+        v-for="(item, index) in calcList"
+        :key="index"
+        v-bind="item"
+        v-show="item.show"
+        @click="onClickItem(item)"
+      ></EquityItem>
+    </main>
+  </section>
+</template>
+
+<script>
+import { getWorkspaceEquityCount } from '@/api/modules/'
+import { mapGetters } from 'vuex'
+import EquityItem from '../ui/EquityItem.vue'
+import { getAssetsFile } from '@/utils'
+import { tryCallHooks } from '@jianyu/easy-inject-qiankun'
+
+export default {
+  name: 'MyEquityList',
+  components: {
+    EquityItem
+  },
+  data() {
+    return {
+      list: [
+        {
+          name: '项目进度监控',
+          count: 0,
+          unit: '个',
+          background: 'linear-gradient(#D2EEFB, #EBF6FB)',
+          icon: getAssetsFile('workspace/my-project.png'),
+          noText: '前往添加',
+          link: '/swordfish/page_big_pc/free/project_progress',
+          addLink: '/jylab/supsearch/index.html',
+          appType: 'outer',
+          addAppType: 'iframe',
+          openType: '_blank',
+          addOpenType: '_self'
+        },
+        {
+          name: '企业情报监控',
+          count: 0,
+          unit: '个',
+          background: 'linear-gradient(#D2FDEE, #ECFCF6)',
+          icon: getAssetsFile('workspace/my-gen.png'),
+          noText: '前往添加',
+          link: '/swordfish/page_big_pc/free/ent_follow',
+          addLink: '/jylab/entSearch/index.html',
+          appType: 'outer',
+          addAppType: 'iframe',
+          openType: '_blank',
+          addOpenType: '_self'
+        },
+        {
+          name: '收藏的标讯',
+          count: 0,
+          unit: '条',
+          background: 'linear-gradient(#FFEBD7, #FFF8F1)',
+          icon: getAssetsFile('workspace/my-collect.png'),
+          noText: '前往收藏',
+          link: '/swordfish/frontPage/collection/sess/index',
+          addLink: '/jylab/supsearch/index.html',
+          appType: 'outer',
+          addAppType: 'iframe',
+          openType: '_blank',
+          addOpenType: '_self'
+        },
+        {
+          name: '业主监控',
+          count: 0,
+          unit: '个',
+          background: 'linear-gradient(#FFE2DE, #FFF6F4)',
+          icon: getAssetsFile('workspace/my-customer.png'),
+          noText: '前往添加',
+          link: '/swordfish/page_big_pc/my_client',
+          addLink: '/jylab/purSearch/index.html',
+          appType: 'outer',
+          addAppType: 'iframe',
+          openType: '_blank',
+          addOpenType: '_self'
+        },
+        {
+          name: '已认领项目',
+          count: 0,
+          unit: '个',
+          background: 'linear-gradient(#FFE2DE, #FFF6F4)',
+          icon: getAssetsFile('workspace/my-claim.png'),
+          noText: '前往认领',
+          link: '/succbi/nzj/app/nzj.app/nzj_claim.spg',
+          addLink: '/succbi/nzj/app/nzj.app/nzj_search_1.spg',
+          appType: 'iframe',
+          addAppType: 'iframe',
+          addOpenType: '_self'
+        }
+      ],
+      my: {
+        claimCount: 0,
+        projectFollowCount: 0,
+        entFollowCount: 0,
+        collectCount: 0,
+        customerCount: 0
+      }
+    }
+  },
+  computed: {
+    ...mapGetters('workspace', ['hasMemberNJPower', 'hasOwnerMonitoring']),
+    calcList() {
+      const {
+        claimCount,
+        projectFollowCount,
+        entFollowCount,
+        collectCount,
+        customerCount
+      } = this.my
+      const list = this.list.map((v) => {
+        switch (v.name) {
+          case '业主监控':
+            v.show = this.hasOwnerMonitoring
+            v.count = customerCount
+            break
+          case '已认领项目':
+            v.show = this.hasMemberNJPower
+            v.count = claimCount
+            break
+          case '项目进度监控':
+            v.show = true
+            v.count = projectFollowCount
+            break
+          case '企业情报监控':
+            v.show = true
+            v.count = entFollowCount
+            break
+          case '收藏的标讯':
+            v.show = true
+            v.count = collectCount
+            break
+        }
+        return {
+          ...v
+        }
+      })
+      return list
+    }
+  },
+  created() {
+    this.getWorkspaceEquity()
+    console.log(this.hasOwnerMonitoring)
+  },
+  methods: {
+    async getWorkspaceEquity() {
+      const { data } = await getWorkspaceEquityCount()
+      if (data) {
+        this.my.claimCount = data.claimCount || 0
+        this.my.projectFollowCount = data.projectFollowCount || 0
+        this.my.entFollowCount = data.entFollowCount || 0
+        this.my.collectCount = data.collectCount || 0
+        this.my.customerCount = data.ownerMonitoring || 0
+      }
+    },
+    onClickItem(item) {
+      /*
+       * 前往认领to拟在建搜索(bi)
+       * 客户监控to采购单位搜索(iframe)
+       * 前往收藏to招标采购搜索(iframe)
+       * 企业情报监控to企业搜索(iframe)
+       * 项目进度监控to招标采购搜索(iframe)
+       */
+      tryCallHooks({
+        fn: () => {
+          this.$BRACE.methods.open({
+            route: {
+              link: item.count ? item.link : item.addLink,
+              appType: item.count ? item.appType : item.addAppType,
+              openType: item.count ? item.openType : item.addOpenType
+            }
+          })
+        },
+        spareFn: () => {
+          const url = item.count ? item.link : encodeURIComponent(item.addLink)
+          if (item.count) {
+            if (item.openType === '_blank') {
+              window.open(url)
+            } else {
+              location.href = url
+            }
+          } else {
+            if (item.addOpenType === '_blank') {
+              window.open(url)
+            } else {
+              location.href = url
+            }
+          }
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@include diy-icon('blue-more', 16, 16);
+.my-equity-list {
+  margin-top: 16px;
+  border-radius: 8px;
+  background: #fff;
+  .card-header {
+    padding: 12px 20px 8px;
+    font-size: 18px;
+    line-height: 28px;
+  }
+  .card-main {
+    display: flex;
+    align-items: center;
+    padding: 8px 20px 16px;
+  }
+}
+</style>

+ 50 - 0
apps/bigmember_pc/src/views/workspace/components/NewsList.vue

@@ -0,0 +1,50 @@
+<template>
+  <ListCard
+    class="news-list"
+    :list="list"
+    title="要闻"
+    @clickListItem="clickListItem"
+    @linkMore="linkMore"
+    :loading="loading"
+    :loaded="loaded"
+  >
+    <div slot="empty-content" class="empty-content">
+      <p>暂无数据</p>
+    </div>
+  </ListCard>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+import ListCard from '../ui/ListCard'
+
+export default {
+  name: 'NewsList',
+  components: {
+    ListCard
+  },
+  computed: {
+    ...mapState({
+      loading: (state) => state.workspace.importantNews.loading,
+      loaded: (state) => state.workspace.importantNews.loaded,
+      list: (state) => state.workspace.importantNews.list
+    })
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    ...mapActions('workspace/importantNews', ['getList']),
+    resolveLink(link) {
+      const { href } = this.$router.resolve(link)
+      return href
+    },
+    clickListItem(item) {
+      window.open(`/jySite/${item._id}.html`)
+    },
+    linkMore() {
+      window.open('/jySchool/zbjq/index.html')
+    }
+  }
+}
+</script>

+ 1 - 1
apps/bigmember_pc/src/views/workspace/components/SubscribeList.vue

@@ -2,7 +2,7 @@
   <ListCard
     class="subscribe-list"
     :list="setListData"
-    title="我的订阅信息"
+    title="订阅推送"
     @clickListItem="clickListItem"
     @linkMore="linkMore"
     :is-recommend="isRecommend"

+ 44 - 26
apps/bigmember_pc/src/views/workspace/dashboard.vue

@@ -2,10 +2,6 @@
   <el-container class="workspace-dashboard">
     <el-main>
       <CommonUse class="main-module"></CommonUse>
-      <BusinessProfile
-        v-if="businessProfileShow"
-        class="main-module"
-      ></BusinessProfile>
       <div
         class="main-module card-list-module"
         v-for="(moduleList, floor) in mainModuleList"
@@ -17,11 +13,18 @@
           :is="name"
         ></component>
       </div>
-      <div class="main-module card-list-module" v-if="dataReportShow">
+      <MyEquityList></MyEquityList>
+      <!-- <div class="main-module card-list-module" v-if="dataReportShow">
         <DataReport></DataReport>
-      </div>
+      </div> -->
+      <BusinessProfile
+        v-if="businessProfileShow"
+        class="main-module"
+      ></BusinessProfile>
     </el-main>
     <el-aside width="369px">
+      <AccountInfo></AccountInfo>
+      <ChatList></ChatList>
       <MessageTips class="aside-module"></MessageTips>
       <AsideOthers class="aside-module"></AsideOthers>
     </el-aside>
@@ -37,17 +40,23 @@ import { Container, Aside, Main } from 'element-ui'
 import MessageTips from './components/MessageTips.vue'
 import CommonUse from './components/CommonUse.vue'
 import SubscribeList from './components/SubscribeList.vue'
-import MyCollections from './components/MyCollections.vue'
-import ProjectFollow from './components/ProjectFollow.vue'
-import EntFollow from './components/EntFollow.vue'
-import DataReport from './components/DataReport.vue'
+// import MyCollections from './components/MyCollections.vue'
+// import ProjectFollow from './components/ProjectFollow.vue'
+// import EntFollow from './components/EntFollow.vue'
+// import DataReport from './components/DataReport.vue'
 import AsideOthers from './components/AsideOthers.vue'
-import ClaimList from './components/ClaimList.vue'
+// import ClaimList from './components/ClaimList.vue'
 import ActivityDialog from '@/components/ad/activity-dialog.vue'
 import GuideIntroDialog from '@/components/ad/guide-intro-dialog.vue'
+import AccountInfo from './components/AccountInfo.vue'
+import ChatList from './components/ChatList.vue'
+import BusinessToDo from './components/BusinessToDo.vue'
+import NewsList from './components/NewsList.vue'
+import AnalysisReport from './components/AnalysisReport.vue'
+import MyEquityList from './components/MyEquityList.vue'
 const BusinessProfile = () => import('./components/BusinessProfile.vue')
-const MyCustomer = () => import('./components/MyCustomer.vue')
-const CustomerWatcher = () => import('./components/CustomerWatcher.vue')
+// const MyCustomer = () => import('./components/MyCustomer.vue')
+// const CustomerWatcher = () => import('./components/CustomerWatcher.vue')
 export default {
   name: 'WorkspaceDashboard',
   components: {
@@ -59,27 +68,36 @@ export default {
     MessageTips,
     CommonUse,
     BusinessProfile,
-    MyCustomer,
-    CustomerWatcher,
+    // MyCustomer,
+    // CustomerWatcher,
     SubscribeList,
-    MyCollections,
-    ProjectFollow,
-    DataReport,
+    // MyCollections,
+    // ProjectFollow,
+    // DataReport,
     AsideOthers,
-    EntFollow,
-    ClaimList
+    // EntFollow,
+    // ClaimList,
+    AccountInfo,
+    ChatList,
+    BusinessToDo,
+    NewsList,
+    AnalysisReport,
+    MyEquityList
   },
   computed: {
     componentsPowerMap() {
       const { myCustomerShow, customerWatcherShow, hasMemberNJPower } = this
       return {
-        ClaimList: hasMemberNJPower,
-        MyCustomer: myCustomerShow,
-        CustomerWatcher: customerWatcherShow,
+        BusinessToDo: true,
+        NewsList: true,
+        // ClaimList: hasMemberNJPower,
+        // MyCustomer: myCustomerShow,
+        // CustomerWatcher: customerWatcherShow,
         SubscribeList: true,
-        MyCollections: true,
-        ProjectFollow: true,
-        EntFollow: true
+        // MyCollections: true,
+        AnalysisReport: true
+        // ProjectFollow: true,
+        // EntFollow: true
       }
     },
     mainModuleList() {

+ 4 - 2
apps/bigmember_pc/src/views/workspace/ui/ArticleItem.vue

@@ -1,6 +1,8 @@
 <template>
   <div class="article-item" :class="{ visited }" v-on="$listeners">
-    <div class="article-item-l ellipsis visited-hd">{{ title }}</div>
+    <div class="article-item-l ellipsis visited-hd" :title="title">
+      {{ title }}
+    </div>
     <div class="article-item-r">
       <span class="red-dot" v-if="read"></span>
       <span class="r-time" v-if="time">{{
@@ -21,7 +23,7 @@ export default {
       default: ''
     },
     time: {
-      type: Number,
+      type: [Number, String],
       default: 0
     },
     read: {

+ 139 - 0
apps/bigmember_pc/src/views/workspace/ui/ChatItem.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="chat-item" v-on="$listeners">
+    <div
+      class="chat-item-l"
+      :class="{ 'chat-item-l-img': img, 'chat-item-l-dot': dot }"
+    >
+      <img v-if="img" :src="img" alt="" />
+      <span v-else>{{ name }}</span>
+      <span v-if="dot" class="l-dot"></span>
+    </div>
+    <div class="chat-item-r">
+      <div class="chat-item-r-top">
+        <span class="r-title ellipsis visited-hd">{{ title }}</span>
+        <span class="r-time" v-if="time">{{ time }}</span>
+      </div>
+      <div class="chat-item-r-bottom ellipsis" :title="content">
+        {{ content }}
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ChatItem',
+  props: {
+    img: {
+      type: String,
+      default: ''
+    },
+    name: {
+      type: String,
+      default: ''
+    },
+    title: {
+      type: String,
+      default: ''
+    },
+    time: {
+      type: [String, Number],
+      default: 0
+    },
+    content: {
+      type: String,
+      default: ''
+    },
+    visited: {
+      type: Boolean,
+      default: false
+    },
+    dot: {
+      type: Boolean,
+      default: false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.chat-item {
+  padding: 8px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  cursor: pointer;
+  &-l {
+    position: relative;
+    display: flex;
+    width: 36px;
+    height: 36px;
+    justify-content: center;
+    align-items: center;
+    margin-right: 6px;
+    font-size: 13px;
+    border-radius: 50%;
+    color: #fff;
+    flex-shrink: 0;
+    background: #2cb7ca;
+    overflow: hidden;
+    .l-dot {
+      position: absolute;
+      right: 0;
+      top: 2px;
+      width: 6px;
+      height: 6px;
+      background: #fb483d;
+      border: 1px solid #ffffff;
+      border-radius: 50%;
+    }
+  }
+  &-l-img {
+    background: transparent;
+    border: 1px solid #ececec;
+  }
+  &-l-dot {
+    overflow: unset;
+  }
+  &-r {
+    flex: 1;
+    width: 0;
+    &-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+    .r-title {
+      width: 70%;
+      font-size: 14px;
+      line-height: 22px;
+      color: #1d1d1d;
+    }
+    .r-time {
+      margin-left: 6px;
+      font-size: 12px;
+      line-height: 20px;
+      color: #999;
+      text-align: right;
+      white-space: nowrap;
+    }
+    &-bottom {
+      height: 18px;
+      margin-top: 2px;
+      color: #686868;
+      font-size: 12px;
+      line-height: 18px;
+    }
+  }
+  &:hover {
+    background: #eaf8fa;
+    border-radius: 8px;
+    .r-title {
+      color: #2cb7ca;
+    }
+  }
+  &:not(:last-child) {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+  }
+}
+</style>

+ 112 - 0
apps/bigmember_pc/src/views/workspace/ui/EquityItem.vue

@@ -0,0 +1,112 @@
+<template>
+  <div
+    class="equity-item"
+    v-on="$listeners"
+    :style="{ background: background }"
+  >
+    <el-image class="equity-item-icon" :src="icon"></el-image>
+    <div class="equity-item-box">
+      <p class="item-name">{{ name }}</p>
+      <p class="item-count" v-if="count">
+        <span class="item-count-text">{{ count }}</span>
+        <span class="item-count-unit">{{ unit }}</span>
+      </p>
+      <p class="item-href" v-else>
+        <span>{{ noText }} &gt;</span>
+      </p>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Image } from 'element-ui'
+
+export default {
+  name: 'EquityItem',
+  components: {
+    [Image.name]: Image
+  },
+  props: {
+    name: {
+      type: String,
+      default: ''
+    },
+    icon: {
+      type: String,
+      default: ''
+    },
+    count: {
+      type: [Number, String],
+      default: 0
+    },
+    unit: {
+      type: String,
+      default: '个'
+    },
+    noText: {
+      type: String,
+      default: ''
+    },
+    background: {
+      type: String,
+      default: ''
+    }
+  },
+  methods: {
+    onAddClick() {
+      this.$emit('link')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.equity-item {
+  position: relative;
+  min-width: 172px;
+  max-width: 20%;
+  height: 98px;
+  border-radius: 8px;
+  flex: 1;
+  cursor: pointer;
+  &-icon {
+    position: absolute;
+    top: 12px;
+    right: 10px;
+    width: 32px;
+    height: 32px;
+  }
+  &-box {
+    padding: 16px;
+    .item-name {
+      font-size: 14px;
+      line-height: 22px;
+      color: #686868;
+    }
+    .item-count {
+      display: flex;
+      align-items: flex-end;
+      margin-top: 8px;
+      &-text {
+        font-size: 36px;
+      }
+      &-unit {
+        font-size: 14px;
+        line-height: 22px;
+      }
+    }
+    .item-href {
+      display: flex;
+      align-items: center;
+      margin-top: 16px;
+      font-size: 16px;
+      line-height: 24px;
+      color: #1d1d1d;
+      cursor: pointer;
+    }
+  }
+  &:not(:last-child) {
+    margin-right: 12px;
+  }
+}
+</style>

+ 6 - 4
apps/bigmember_pc/src/views/workspace/ui/ListCard.vue

@@ -143,13 +143,15 @@ export default {
 }
 
 .list-item {
-  padding: 12px 0;
+  // padding: 12px 0;
+  padding: 0;
+  margin-bottom: 8px;
   display: flex;
   justify-content: space-between;
   align-items: center;
-  &:not(.last) {
-    box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.05) inset;
-  }
+  // &:not(.last) {
+  //   box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.05) inset;
+  // }
   .list-item-l {
     margin-right: 12px;
     font-size: 14px;

+ 1 - 1
apps/bigmember_pc/src/views/workspace/ui/WorkspaceCard.vue

@@ -43,7 +43,7 @@ export default {
 
 .workspace-card {
   background-color: #fff;
-  border-radius: 4px;
+  border-radius: 8px;
   box-shadow: 0px 0px 18px 0px rgba(0, 0, 0, 0.02);
 }
 .card-header {

+ 12 - 0
apps/mobile/src/api/modules/bigmember.js

@@ -101,3 +101,15 @@ export function projectDetailApi (data) {
     data
   })
 }
+
+// 检查是否关注了项目
+export function projectFollowCheck (data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/bigmember/follow/project/check',
+    method: 'post',
+    data
+  })
+}
+
+

+ 19 - 0
apps/mobile/src/api/modules/business.js

@@ -0,0 +1,19 @@
+import request from '@/api'
+import qs from 'qs'
+
+export function getBusinessDetails(data) {
+  data = qs.stringify(data)
+  return request({
+    url: '/bigmember/project/businessDetails',
+    method: 'POST',
+    data
+  })
+}
+
+// 是否需要完善信息提示
+export function isNeedCompleteInfo () {
+  return request({
+    url: '/salesLeads/businessRetain',
+    method: 'POST'
+  })
+}

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

@@ -24,3 +24,4 @@ export * from './ent'
 export * from './dataSmt'
 export * from './jyPoints'
 export * from './invoice'
+export * from './business'

+ 23 - 13
apps/mobile/src/api/modules/subscribe.js

@@ -4,7 +4,7 @@ import { envs } from '@/utils/prototype/modules/platform'
 
 // 获取用户订阅信息
 // https://yapi.jydev.jianyu360.com/project/63/interface/api/191
-export function getUserSubscribeList (type = 'fType', data) {
+export function getUserSubscribeList(type = 'fType', data) {
   // fType免费用户  vType超级订阅用户  mType大会员用户  eType商机管理用户
   return request({
     url: `/jyapi/jybx/subscribe/${type}/list`,
@@ -13,10 +13,18 @@ export function getUserSubscribeList (type = 'fType', data) {
     data
   })
 }
-
+export function getRecList(data) {
+  // 订阅推荐列表
+  return request({
+    url: '/jyapi/jybx/subscribe/getRecList',
+    method: 'post',
+    noToast: true,
+    data
+  })
+}
 // 获取用户订阅信息其他额外的信息
 // https://yapi.jydev.jianyu360.com/project/63/interface/api/203
-export function getUserSubscribeSomeInfo (type = 'fType') {
+export function getUserSubscribeSomeInfo(type = 'fType') {
   return request({
     url: `/jyapi/jybx/subscribe/${type}/someInfo`,
     method: 'post',
@@ -26,7 +34,7 @@ export function getUserSubscribeSomeInfo (type = 'fType') {
 
 // 获取用户订阅筛选关键词
 // 返回内容中,合并了企业订阅的关键词
-export function getUserSubscribeKeywords (type = 'fType', data) {
+export function getUserSubscribeKeywords(type = 'fType', data) {
   data = qs.stringify(data)
   return request({
     url: `/jyapi/jybx/subscribe/${type}/getKey`,
@@ -37,7 +45,7 @@ export function getUserSubscribeKeywords (type = 'fType', data) {
 }
 
 // 推送数据浏览状态修改
-export function setUserSubscribeListVisited (type = 'fType', data) {
+export function setUserSubscribeListVisited(type = 'fType', data) {
   data = qs.stringify(data)
   return request({
     url: `/jyapi/jybx/subscribe/${type}/setRead`,
@@ -48,17 +56,19 @@ export function setUserSubscribeListVisited (type = 'fType', data) {
 }
 
 // 免费用户设置订阅关键词
-export function freeSubscribeKeys (data) {
+export function freeSubscribeKeys(data) {
   data = qs.stringify(data)
   return request({
-    url: envs.inWX ? '/member/swordfish/ajaxReq' : '/jyapp/member/swordfish/ajaxReq',
+    url: envs.inWX
+      ? '/member/swordfish/ajaxReq'
+      : '/jyapp/member/swordfish/ajaxReq',
     method: 'post',
     data
   })
 }
 
 // 企业订阅接收人员获取
-export function getDistributorPerson (type = 'fType', data) {
+export function getDistributorPerson(type = 'fType', data) {
   // data = qs.stringify(data)
   return request({
     url: `/jyapi/jybx/subscribe/${type}/distributor`,
@@ -69,7 +79,7 @@ export function getDistributorPerson (type = 'fType', data) {
 }
 
 // 订阅页面数据导出接口
-export function getPushListDataExportId (type = 'fType', data) {
+export function getPushListDataExportId(type = 'fType', data) {
   return request({
     url: `/jyapi/jybx/subscribe/${type}/byPushHistory`,
     method: 'post',
@@ -79,7 +89,7 @@ export function getPushListDataExportId (type = 'fType', data) {
 
 // 推送设置接口
 // 用户信息获取
-export function getUser (data) {
+export function getUser(data) {
   return request({
     url: '/jyapi//jybx/subscribe/getUser',
     method: 'post',
@@ -88,7 +98,7 @@ export function getUser (data) {
 }
 
 // 用户信息设置
-export function setUser (data) {
+export function setUser(data) {
   return request({
     url: '/jyapi//jybx/subscribe/setUser',
     method: 'post',
@@ -97,7 +107,7 @@ export function setUser (data) {
 }
 
 // 推送设置获取
-export function getPushSet (data) {
+export function getPushSet(data) {
   return request({
     url: '/jyapi/jybx/subscribe/getPushSet',
     method: 'post',
@@ -106,7 +116,7 @@ export function getPushSet (data) {
 }
 
 // 推送设置修改
-export function setPushSet (data) {
+export function setPushSet(data) {
   return request({
     url: '/jyapi/jybx/subscribe/setPushSet',
     method: 'post',

BIN=BIN
apps/mobile/src/assets/image/business/tip-bg.png


BIN=BIN
apps/mobile/src/assets/image/icon/icon-phone.png


BIN=BIN
apps/mobile/src/assets/image/icon/icon-right2.png


BIN=BIN
apps/mobile/src/assets/image/public/goset.png


+ 11 - 2
apps/mobile/src/components/search/Layout.vue

@@ -32,6 +32,11 @@ export default {
     event: 'input'
   },
   props: {
+    // 是否允许空值进行搜索,true表示允许,false表示不允许
+    emptySearch: {
+      type: Boolean,
+      default: false
+    },
     showButton: {
       type: Boolean,
       default: true
@@ -58,8 +63,12 @@ export default {
       if (type === 'input') {
         this.$emit('input', data)
       }
-      if (type === 'submit' && (typeof data !== 'string' || data === '')) {
-        return
+
+      // 是否允许空值进行提交
+      if (!this.emptySearch) {
+        if (type === 'submit' && (typeof data !== 'string' || data === '')) {
+          return
+        }
       }
       // 提交按钮防抖
       if (type === 'submit') {

+ 2 - 1
apps/mobile/src/components/search/bidding/filterHistoryDialog.vue

@@ -9,7 +9,7 @@
     @close="onClose(false)"
     :before-close="beforeClose"
     show-cancel-button>
-    <CellCard>
+    <CellCard scroll>
       <template #header-left>
         <div class="card-left-header">
           <div class="card-item">
@@ -84,6 +84,7 @@ export default {
 }
 .card-item {
   display: flex;
+  padding: 0 12px;
   .item-label {
     white-space: nowrap;
   }

+ 126 - 6
apps/mobile/src/components/search/bidding/filters.vue

@@ -166,6 +166,72 @@
               />
             </template>
           </JCell>
+          <JCell
+            class="more-filter-item"
+            title="采购单位"
+            @click="checkboxGroupClick({ disabled: noLoginOrFree })"
+            v-show="isLogin"
+          >
+            <template #title v-if="noLoginOrFree">
+              <span class="mr-6">采购单位</span>
+              <van-tag plain round type="danger" @click="onNoPower">
+                开通
+              </van-tag>
+            </template>
+            <template #label>
+              <KeywordsInputGroup
+                :readonly="noLoginOrFree"
+                v-model="cacheMoreFilters.buyerList"
+                inputMaxlength="30"
+                placeholder="输入采购单位名称,找其招标项目"
+                class="buyer-filter"
+              />
+            </template>
+          </JCell>
+          <JCell
+            class="more-filter-item"
+            title="中标企业"
+            @click="checkboxGroupClick({ disabled: noLoginOrFree })"
+            v-show="isLogin"
+          >
+            <template #title v-if="noLoginOrFree">
+              <span class="mr-6">中标企业</span>
+              <van-tag plain round type="danger" @click="onNoPower">
+                开通
+              </van-tag>
+            </template>
+            <template #label>
+              <KeywordsInputGroup
+                :readonly="noLoginOrFree"
+                v-model="cacheMoreFilters.winnerList"
+                inputMaxlength="30"
+                placeholder="输入中标企业名称,找其中标项目"
+                class="winner-filter"
+              />
+            </template>
+          </JCell>
+          <JCell
+            class="more-filter-item"
+            title="招标代理机构"
+            @click="checkboxGroupClick({ disabled: noLoginOrFree })"
+            v-show="isLogin"
+          >
+            <template #title v-if="noLoginOrFree">
+              <span class="mr-6">招标代理机构</span>
+              <van-tag plain round type="danger" @click="onNoPower">
+                开通
+              </van-tag>
+            </template>
+            <template #label>
+              <KeywordsInputGroup
+                :readonly="noLoginOrFree"
+                v-model="cacheMoreFilters.agencyList"
+                inputMaxlength="30"
+                placeholder="输入代理机构名称,找其代理项目"
+                class="winner-filter"
+              />
+            </template>
+          </JCell>
         </div>
       </DropdownLayout>
     </van-dropdown-item>
@@ -309,7 +375,10 @@ export default {
           fileExists: ['0'],
           winnerConcat: [''],
           buyerConcat: [''],
-          notKey: []
+          notKey: [],
+          buyerList: [],
+          winnerList: [],
+          agencyList: []
         }
       }
     },
@@ -334,7 +403,10 @@ export default {
           fileExists: ['0'],
           winnerConcat: [''],
           buyerConcat: [''],
-          notKey: []
+          notKey: [],
+          buyerList: [],
+          winnerList: [],
+          agencyList: []
         }
       }
     }
@@ -380,7 +452,10 @@ export default {
         fileExists: ['0'],
         winnerConcat: [''],
         buyerConcat: [''],
-        notKey: []
+        notKey: [],
+        buyerList: [],
+        winnerList: [],
+        agencyList: []
       }
     }
   },
@@ -543,7 +618,10 @@ export default {
             winnerConcat,
             buyerConcat,
             fileExists,
-            notKey
+            notKey,
+            buyerList,
+            winnerList,
+            agencyList
           } = this.defaultFilterState
           const sameList = []
 
@@ -584,6 +662,20 @@ export default {
           sameList.push(
             deepCompare(this.filters.notKey, notKey)
           )
+
+          // 采购单位
+          sameList.push(
+            deepCompare(this.filters.buyerList, buyerList)
+          )
+          // 中标单位
+          sameList.push(
+            deepCompare(this.filters.winnerList, winnerList)
+          )
+          // 代理机构
+          sameList.push(
+            deepCompare(this.filters.agencyList, agencyList)
+          )
+
           // 附件
           sameList.push(
             this.filters.fileExists === fileExists
@@ -638,7 +730,10 @@ export default {
             winnerConcat,
             buyerConcat,
             fileExists,
-            notKey
+            notKey,
+            buyerList,
+            winnerList,
+            agencyList
           } = this.filters
 
           // 更多关键词
@@ -671,6 +766,10 @@ export default {
 
           this.cacheMoreFilters.fileExists = fileExists
           this.cacheMoreFilters.notKey = notKey
+
+          this.cacheMoreFilters.buyerList = buyerList
+          this.cacheMoreFilters.winnerList = winnerList
+          this.cacheMoreFilters.agencyList = agencyList
           break
         }
       }
@@ -721,7 +820,10 @@ export default {
             winnerConcat,
             buyerConcat,
             fileExists,
-            notKey
+            notKey,
+            buyerList,
+            winnerList,
+            agencyList
           } = this.defaultFilterState
           // 更多关键词
           if (moreKeywordsMode) {
@@ -776,6 +878,18 @@ export default {
             this.cacheMoreFilters.notKey = notKey
             filters.notKey = notKey
           }
+          if (Array.isArray(buyerList)) {
+            filters.buyerList = buyerList
+            this.cacheMoreFilters.buyerList = buyerList
+          }
+          if (Array.isArray(winnerList)) {
+            filters.winnerList = winnerList
+            this.cacheMoreFilters.winnerList = winnerList
+          }
+          if (Array.isArray(agencyList)) {
+            filters.agencyList = agencyList
+            this.cacheMoreFilters.agencyList = agencyList
+          }
           break
         }
       }
@@ -828,6 +942,9 @@ export default {
             winnerConcat,
             buyerConcat,
             notKey,
+            buyerList,
+            winnerList,
+            agencyList,
             fileExists
           } = this.cacheMoreFilters
           const { price: defaultPrice } = this.defaultFilterState
@@ -859,6 +976,9 @@ export default {
           filters.buyerConcat = buyerConcat
           filters.fileExists = fileExists
           filters.notKey = notKey
+          filters.buyerList = buyerList
+          filters.winnerList = winnerList
+          filters.agencyList = agencyList
           break
         }
       }

+ 4 - 4
apps/mobile/src/components/selector/keyword-input-group/index.vue

@@ -15,7 +15,7 @@
       <van-form class="keywords-input-container" action="javascript:return true">
         <van-field
           class="keywords-input"
-          v-model="inputValue"
+          v-model.trim="inputValue"
           :formatter="formatter"
           :placeholder="placeholder"
           :maxlength="inputMaxlength"
@@ -137,7 +137,7 @@ export default {
   },
   methods: {
     formatter (value) {
-      return value.replace(/\s{2,}/g, ' ')
+      return value.replace(/,/g, ' ').replace(/\s{2,}/g, ' ')
     },
     /**
      * 设置页面状态
@@ -247,8 +247,8 @@ export default {
   margin-bottom: 16px;
 }
 .keywords-input-tags {
-  max-height: 160px;
-  overflow-y: scroll;
+  // max-height: 160px;
+  // overflow-y: scroll;
   ::v-deep {
     .van-tag {
       padding: 6px 10px;

+ 7 - 1
apps/mobile/src/components/treasure-box/AllUse.vue

@@ -53,7 +53,8 @@ export default {
       'tabsUseableFunctions',
       'tabsAllFunctions',
       'commonFunctions',
-      'boxFeatureType'
+      'boxFeatureType',
+      'commonMaxNum'
     ]),
     currentRenderList () {
       if (this.allNeedBadge) {
@@ -174,6 +175,11 @@ export default {
     badgeHandle (item) {
       let commonArr = JSON.parse(JSON.stringify(this.commonFunctions))
       if (item.canAdd) {
+        // 常用功能超出最大限制个提示
+        if(commonArr.length >= this.commonMaxNum) {
+          this.$toast('已添加满8个,请移除部分功能后再添加')
+          return
+        }
         item.canAdd = false
         commonArr.push(item)
         this.$toast('已添加至常用功能')

+ 6 - 3
apps/mobile/src/components/treasure-box/CommonUse.vue

@@ -6,7 +6,7 @@
           :list="commonList"
           :need-grid-bg="true"
           @openLink="openLink" >
-          <template v-if="moreGrid && !loading" #more-grid>
+          <template v-if="moreGrid && !loading && commonList.length < commonMaxNum" #more-grid>
             <div class="common-use-grid grid-bg-color clickable" @click="settingCommonFeature">
               <!-- <AppIcon name="add-01" color="#aaa" svg size="17" /> -->
               <img class="add-fun-icon" src="@/assets/image/icon/add-function.png" />
@@ -67,7 +67,8 @@ export default {
   },
   computed: {
     ...mapGetters('treasureBox', [
-      'commonFunctions'
+      'commonFunctions',
+      'commonMaxNum'
     ]),
     getList () {
       return this.commonFunctions ? JSON.parse(JSON.stringify(this.commonFunctions)) : []
@@ -84,7 +85,8 @@ export default {
       workspaceCommonUse('list', { platform: this.$env.platform?.toUpperCase() }).then(res => {
         const { data = [], error_code: code } = res
         if (code === 0 && data) {
-          _this.commonList = data
+          const maxNum = data.num || 8
+          _this.commonList = data.list || []
           _this.commonList.forEach(temp => {
             // 计算背景色
             calcImgThemeColor(temp.icon).then(({ color }) => {
@@ -99,6 +101,7 @@ export default {
           })
           // 初始化常用组件
           _this.$store.dispatch('treasureBox/setCommonFunction', _this.commonList)
+          _this.$store.dispatch('treasureBox/setCommonMaxNum', maxNum)
         }
       }).finally(() => {
         this.loading = false

+ 12 - 0
apps/mobile/src/router/modules/business.js

@@ -0,0 +1,12 @@
+// 商机情报
+export default [
+  {
+    path: '/detail/:id',
+    name: 'business-detail',
+    component: () => import('@/views/business/Detail.vue'),
+    meta: {
+      header: true,
+      title: '公告信息'
+    }
+  }
+]

+ 14 - 1
apps/mobile/src/router/modules/tabbar.js

@@ -58,7 +58,10 @@ export default [
       {
         path: 'message',
         name: 'tabbar-message',
-        alias: ['/jyapp/frontPage/messageCenter/sess/index', '/weixin/frontPage/messageCenter/sess/index'],
+        alias: [
+          '/jyapp/frontPage/messageCenter/sess/index',
+          '/weixin/frontPage/messageCenter/sess/index'
+        ],
         component: () => import('@/views/tabbar/Message.vue'),
         meta: {
           title: '消息中心',
@@ -76,5 +79,15 @@ export default [
       //   }
       // }
     ]
+  },
+  {
+    path: '/recommendedlist',
+    name: 'tabbar-recommendedlist',
+    component: () => import('@/views/tabbar/RecommendedBannerlist.vue'),
+    meta: {
+      header: true,
+      title: '推荐标讯列表'
+      // tabbar: 'subscribe'
+    }
   }
 ]

+ 13 - 1
apps/mobile/src/store/modules/treasureBox.js

@@ -5,7 +5,8 @@ export default {
     commonFunctions: [], // 常用功能模块
     tabsUseableFunctions: [], // tab菜单下可用功能模块
     tabsAllFunctions: [], // tab菜单下所有功能模块
-    boxFeatureType: localStorage.getItem('box_feature_type') || 'all' // 百宝箱功能类型
+    boxFeatureType: localStorage.getItem('box_feature_type') || 'all', // 百宝箱功能类型
+    commonMaxNum: 8 //常用功能限制数量
   }),
   mutations: {
     changeCommonFunction (state, list = []) {
@@ -20,6 +21,9 @@ export default {
     changeBoxFeatureType (state, value) {
       state.boxFeatureType = value
       localStorage.setItem('box_feature_type', value)
+    },
+    changeCommonMaxNum (state, value) {
+      state.commonMaxNum = value
     }
   },
   actions: {
@@ -34,6 +38,10 @@ export default {
     },
     setBoxFeatureType ({ commit }, value) {
       commit('changeBoxFeatureType', value)
+    },
+    // 设置常用功能最大限制数量
+    setCommonMaxNum ({ commit }, value) {
+      commit('changeCommonMaxNum', value)
     }
   },
   getters: {
@@ -51,6 +59,10 @@ export default {
     },
     boxFeatureType  (state) {
       return state.boxFeatureType || 'all'
+    },
+    // 常用功能最大限制数量
+    commonMaxNum (state) {
+      return state.commonMaxNum || 8
     }
   }
 }

+ 21 - 4
apps/mobile/src/ui/cell-card/index.vue

@@ -11,7 +11,13 @@
       </div>
     </div>
     <div class="card-content-container" @click="onClickContent">
-      <div class="card-content" :class="{ 'ellipsis inline': (!expand && overflowEllipsis) }">
+      <div
+        class="card-content"
+        :class="{
+          'ellipsis inline': !expand && overflowEllipsis,
+          'need-scroll': scroll
+        }"
+      >
         <slot name="default"></slot>
       </div>
       <button class="expand-button" v-if="showExpandButton" @click.stop="changeExpandState">
@@ -49,6 +55,11 @@ export default {
       type: Boolean,
       default: true
     },
+    // 是否需要滚动
+    scroll: {
+      type: Boolean,
+      default: false
+    },
     /**
      * 是否展示头部右侧箭头
      */
@@ -99,7 +110,7 @@ export default {
 }
 .card-content-container {
   position: relative;
-  padding: 8px 12px;
+  padding: 8px 0;
   min-height: 40px;
   line-height: 22px;
   font-size: 13px;
@@ -121,7 +132,13 @@ export default {
   border-radius: 4px;
   z-index: 2;
 }
-.card-content.inline > * {
-  display: inline-block;
+.card-content {
+  &.need-scroll {
+    max-height: 320px;
+    overflow-y: auto;
+  }
+  &.inline > * {
+    display: inline-block;
+  }
 }
 </style>

+ 22 - 1
apps/mobile/src/utils/format/modules/filter-history-formatter.js

@@ -31,7 +31,10 @@ export function filterHistoryNotEmptyFormat (formatted) {
     winnerConcatText,
     buyerConcatText,
     fileExists,
-    notKey
+    notKey,
+    buyerList,
+    winnerList,
+    agencyList
   } = formatted
   let { infoTypeText } = formatted
 
@@ -133,6 +136,24 @@ export function filterHistoryNotEmptyFormat (formatted) {
       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
 }
 

+ 310 - 0
apps/mobile/src/views/business/Detail.vue

@@ -0,0 +1,310 @@
+<template>
+  <!--  商机情报详情页-->
+  <div class="j-container business-detail-page">
+    <div class="j-main">
+      <div class="business-header">
+        <div class="title">{{ info.title }}</div>
+        <div class="business-sub-row">
+          <div class="common-time">
+            {{ dateFromNow(new Date(publishtime).getTime()) }}
+          </div>
+          <div class="business_option">
+            <div class="potential-col">
+              提前介入,把握商机,戳我查看<em class="handle-em" @click="goPotentialPage">潜在项目预测</em><i class="icon_right"></i>
+            </div>
+          </div>
+        </div>
+      </div>
+      <section class="collect-info-tip" v-if="needComplete">
+        【商机情报】想获得更精准商机情报?立即<em class="handle-em" @click="completeInfo">完善信息</em>。<br>您也可以<em class="handle-em" @click="openCustomer">联系客服</em>进行相关咨询。
+      </section>
+      <div class="business-content">
+        <section class="poten-box">
+          <div class="box-title">
+            <span class="left-line"></span>
+            <span>预测项目</span>
+          </div>
+          <div class="box-con">
+            <div class="unit_row">
+              <span class="unit_label">采购单位</span>
+              <span class="list_pur_name">{{ info.buyer }}</span>
+            </div>
+            <div class="unit_row mt8">
+              <span class="unit_label"><span class="point"></span>预测线索</span>
+              <span class="poten_name">{{ info.title && info.title.indexOf('】') ? info.title.substring(info.title.indexOf('】') + 1) : info.title }}</span>
+            </div>
+            <div class="unit_row mt8">
+              <span class="unit_label"><span class="point"></span>预测采购内容</span>
+              <span class="poten_name">
+              <span>{{ info.purchasing }}</span>
+            </span>
+            </div>
+            <div class="unit_row mt8">
+              <span class="unit_label">
+                <span class="point"></span>预测采购时间:
+                <span style="color: #1d1d1d;">{{ dateFormatter(yuceendtime, 'yyyy-MM-dd') }}</span>
+              </span>
+            </div>
+          </div>
+        </section>
+        <section class="similar-box mt8">
+          <div class="box-title">
+            <span class="left-line"></span>
+            <span>同类项目</span>
+          </div>
+          <div class="box-con">
+            <div class="unit_row">
+              <span class="unit_label">同类项目:</span>
+              <span class="unit_name similar_project clickable" @click="goProjectDetail(similarProject.p_id, similarProject.p_orther)">
+                {{ similarProject.p_orther }}
+                <van-icon style="margin-left: 16px;" size="16" name="arrow" color="#C0C4CC" v-if="similarProject.p_orther"/>
+              </span>
+            </div>
+            <div class="unit_row mt8">
+              <span class="unit_label">联系人:</span>
+              <span class="unit_name">{{ similarProject.p_person }}</span>
+            </div>
+            <div class="unit_row mt8">
+              <span class="unit_label">联系电话:</span>
+              <span class="unit_name phone">
+                {{ similarProject.p_phone }}
+                <i class="icon_phone" v-if="similarProject.p_phone" @click="telHandle(similarProject.p_phone)"></i>
+              </span>
+            </div>
+          </div>
+        </section>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { dateFromNow, callPhone, dateFormatter } from '@/utils'
+import { projectFollowCheck, getBusinessDetails, isNeedCompleteInfo } from '@/api/modules/'
+import { Icon } from 'vant'
+import { mapGetters } from 'vuex'
+export default {
+  name: 'business-detail',
+  components: {
+    [Icon.name]: Icon
+  },
+  data () {
+    return {
+      info: {},
+      // 是否需要完善信息提示
+      needComplete: false
+    }
+  },
+  computed: {
+    ...mapGetters('user', [
+      'isMember',
+    ]),
+    publishtime () {
+      return this.info.publishtime ? this.info.publishtime * 1000 : 0
+    },
+    yuceendtime () {
+      return this.info.yuceendtime ? this.info.yuceendtime * 1000 : 0
+    },
+    similarProject () {
+      return this.info.results && this.info.results[0] ? this.info.results[0] : {}
+    }
+  },
+  created() {
+    this.isNeedComplete()
+    this.getDetailInfo()
+  },
+  methods: {
+    dateFromNow,
+    dateFormatter,
+    // 是否需要完善信息
+    async isNeedComplete() {
+      const { error_code: code, data } = await isNeedCompleteInfo()
+      if(code === 0) {
+        this.needComplete = !!data
+      }
+    },
+    // 获取信息详情
+    async getDetailInfo() {
+      const id = this.$route.params?.id || ''
+      const { error_code: code, data = {} } = await getBusinessDetails({ id })
+      if(code === 0 && data) {
+        this.info = data
+      }
+    },
+    // 跳转潜在项目预测(无权限用户引导大会员留资,大会员引导跳转到潜在项目预测页面。)
+    goPotentialPage () {
+      if(this.isMember) {
+        window.location.href = '/jyapp/big/page/forecast_list'
+      } else {
+        let source = 'app_project_businessDetails'
+        const { inWX, inH5 } = this.$envs
+        if (inWX) {
+          source = 'wx_project_businessDetails'
+        } else if(inH5) {
+          source = 'h5_project_businessDetails'
+        }
+        this.$leaveInfo.toLeaveInfoPage({
+          source: source
+        })
+      }
+    },
+    // 完善信息
+    completeInfo () {
+      let source = 'app_project_businessDetails_improve'
+      const { inWX, inH5 } = this.$envs
+      if (inWX) {
+        source = 'wx_project_businessDetails_improve'
+      } else if(inH5) {
+        source = 'h5_project_businessDetails_improve'
+      }
+      this.$router.push('/uersales/newuser?source=' + source)
+    },
+    // 查看项目详情
+    async goProjectDetail (id, pName) {
+      const params = {
+        sid: id
+      }
+      const  { error_code: code, data } = await projectFollowCheck(params)
+      if(code === 0) {
+        const obj = {
+          fid: data.fig ? data.fig : '',
+          sid: id
+        }
+        sessionStorage.setItem('bigvip-fid', JSON.stringify(obj))
+        if (this.$envs.inWX) {
+          location.href = '/big/wx/page/pro_follow_detail'
+        } else {
+          location.href = "/jyapp/big/page/pro_follow_detail"
+        }
+      }
+    },
+    // 电话
+    telHandle (phone) {
+      if(!phone) return
+      callPhone(phone)
+    },
+    // 联系客服
+    openCustomer () {
+      if (this.$envs.inWX) {
+        window.location.href = '/big/wx/page/customer'
+      } else {
+        window.location.href = '/jyapp/free/customer'
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.business-detail-page{
+  .business-header{
+    background: #fff;
+    padding: 24px 16px 16px;
+    margin-bottom: 8px;
+
+    .title{
+      font-size:20px;
+      line-height:30px;
+      color: #171826;
+      margin-bottom:12px;
+    }
+    .business-sub-row{
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      .common-time{
+        font-size:12px;
+        color: #9B9CA3;
+      }
+      .potential-col {
+        display: flex;
+        align-items: center;
+        padding: 4px 8px;
+        border-radius: 4px;
+        border: 0.5px solid #87DFEA;
+        background: #EAF8FA;
+        font-size:12px;
+        height:26px;
+      }
+      .icon_right {
+        display: inline-block;
+        width: 10px;
+        height: 10px;
+        background: url('@/assets/image/icon/icon-right2.png') no-repeat center;
+        background-size: contain;
+        margin-left: 4px;
+      }
+    }
+  }
+  .collect-info-tip {
+    font-size:13px;
+    color: #1D1D1D;
+    line-height:20px;
+    padding: 8px 16px 8px 16px;
+    background: #fff url('@/assets/image/business/tip-bg.png')  no-repeat center right;
+    background-size:contain;
+    margin-bottom:8px;
+  }
+  .business-content{
+    .poten-box, .similar-box{
+      background: #fff;
+    }
+    .box-title{
+      padding: 16px 0 6px 16px;
+      font-size:18px;
+      line-height: 20px;
+      color: #171826;
+      display: flex;
+      align-items: center;
+      .left-line{
+        display: inline-block;
+        background: #2ABED1;
+        width: 3px;
+        height:16px;
+        border-radius: 11px;
+        margin-right: 8px;
+      }
+    }
+    .box-con{
+      padding: 16px;
+      font-size:14px;
+      color: #171826;
+      line-height:20px;
+      .unit_row {
+        display: flex;
+        flex-direction: column;
+      }
+      .unit_label{
+        font-size: 12px;
+        line-height: 18px;
+        color: #9B9CA3;
+      }
+      .similar_project{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+      }
+      .phone{
+        display: flex;
+        align-items: center;
+      }
+      .icon_phone{
+        display: inline-block;
+        width: 24px;
+        height: 24px;
+        background: url('@/assets/image/icon/icon-phone.png') no-repeat center;
+        background-size: contain;
+        margin-left: 12px;
+      }
+    }
+  }
+  .handle-em{
+    color:#2ABED1 !important;
+  }
+  .mt8{
+    margin-top:8px;
+  }
+}
+
+</style>

+ 3 - 3
apps/mobile/src/views/push/PushSetting.vue

@@ -37,7 +37,7 @@
           </template>
           <div class="noMessage" v-if="item.title === '我的订阅'">
             <div class="left_">
-              <div class="title">消息提醒</div>
+              <div class="title">推荐消息提醒</div>
               <div class="warm" @click.stop="PopupBox"></div>
               <div class="states">{{ item.i_nomsgtip ? '已开启' : '已关闭' }}</div>
             </div>
@@ -748,8 +748,8 @@ export default {
     },
     PopupBox () {
       this.$dialog.confirm({
-        title: '消息提醒',
-        message: '根据订阅条件,未匹配到新的招投标公告信息时,将为您发送提醒通知。',
+        title: '推荐消息提醒',
+        message: '根据您在平台的订阅词/搜索词/浏览标讯进行联想推荐,剑鱼标讯智能推荐为您推送新的标讯信息。',
         className: 'j-confirm-dialog',
         messageAlign: 'left',
         showCancelButton: false,

+ 17 - 0
apps/mobile/src/views/search/filter-history/bidding.vue

@@ -84,6 +84,12 @@
                 <span class="item-text highlight-text">{{ history.winner }}</span>
               </template>
             </div>
+            <div class="card-item" v-if="history.agency">
+              <template>
+                <span class="item-label">招标代理机构:</span>
+                <span class="item-text highlight-text">{{ history.agency }}</span>
+              </template>
+            </div>
           </template>
           <template v-else>
             {{ desc_dispose(history) }}
@@ -238,6 +244,9 @@ export default {
       const formattedItem = {
         ...formatted,
         notKey: item.notkey,
+        buyerList: item.buyer,
+        winnerList: item.winner,
+        agencyList: item.agency,
         isPay: item.isPay,
         fileExists: [item.fileExists || '0'],
         price: {
@@ -501,6 +510,9 @@ export default {
       if (val.winner) {
         localStorage.setItem('winner', val.winner)
       }
+      if (val.agency) {
+        localStorage.setItem('agency', val.agency)
+      }
       localStorage.setItem('index','-1') 
       if (this.$envs.inWX) {
         window.location.replace(`${location.origin}/front/wx_dataExport/toSieve?from=filterList`)
@@ -523,6 +535,7 @@ export default {
       localStorage.removeItem('subTypes')
       localStorage.removeItem('buyer')
       localStorage.removeItem('winner')
+      localStorage.removeItem('agency')
     },
     selectType_dispose(selectType) { // 匹配类型转化
       let select = [];
@@ -593,6 +606,9 @@ export default {
         if (val.winner) {
           str += val.winner
         }
+        if (val.agency) {
+          str += val.agency
+        }
       }
       return str
     },
@@ -755,6 +771,7 @@ export default {
 
 .card-item {
   display: flex;
+  padding: 0 12px;
 
   &.close {
     margin-right: 4px;

+ 13 - 1
apps/mobile/src/views/search/middle/bidding/index.vue

@@ -167,6 +167,9 @@ export default {
         ...formatted,
         isPay: item.isPay,
         notKey: item.notkey,
+        buyerList: item.buyer,
+        winnerList: item.winner,
+        agencyList: item.agency,
         fileExists: [item.fileExists || '0'],
         price: {
           start: item.minprice,
@@ -183,6 +186,14 @@ export default {
       const allKeywordsArr = [keywordsText].concat(additionalWords).filter(k => !!k)
       const allKeywordsText = allKeywordsArr.join(' ')
       const additionalWordsText = additionalWords.join(' ')
+
+      // 关键词为空时兼容
+      let label = allKeywordsArr.join(',')
+      if (label) {
+        label += ':'
+      }
+      label += labelList.join(',')
+
       return {
         id: item.id,
         ...formattedItem,
@@ -191,7 +202,8 @@ export default {
         allKeywordsText,
         allKeywordsArr,
         wordsModeText: additionalWordsText ? wordsModeText : '',
-        label: `${allKeywordsArr.join(',')}:${labelList.join(',')}`
+        // label: `${allKeywordsArr.join(',')}:${labelList.join(',')}`
+        label
       }
     },
     /**

+ 43 - 3
apps/mobile/src/views/search/result/bidding/index.vue

@@ -403,7 +403,10 @@ export default {
           fileExists: ['0'],
           winnerConcat: [''],
           buyerConcat: [''],
-          notKey: []
+          notKey: [],
+          buyerList: [],
+          winnerList: [],
+          agencyList: []
         }
       },
       // 发送请求整理的数据
@@ -425,7 +428,10 @@ export default {
         winnerConcat: [''],
         buyerConcat: [''],
         fileExists: ['0'],
-        notKey: []
+        notKey: [],
+        winnerList: [],
+        buyerList: [],
+        agencyList: []
       },
       listState: {
         limit: 0,
@@ -894,6 +900,7 @@ export default {
     restoreStateFromRouteParams () {
       const { searchFilters = {} } = this.$refs
       const { filters } = this.$route.params
+      console.log(filters)
       if (!filters) return
       const { getScopeOptions = [], getTimeOptions = [] } = searchFilters
       this.topSearch.input = filters.keywords.join(' ')
@@ -967,6 +974,30 @@ export default {
           this.filters.notKey = filters.notKey.split(',')
         }
       }
+      if (filters.buyerList && !this.isFree) {
+        if (Array.isArray(filters.buyerList)) {
+          this.filters.buyerList = filters.buyerList
+        } else {
+          this.filters.buyerList = filters.buyerList.split(',')
+        }
+      }
+      if (filters.winnerList && !this.isFree) {
+        if (Array.isArray(filters.winnerList)) {
+          this.filters.winnerList = filters.winnerList
+        } else {
+          this.filters.winnerList = filters.winnerList.split(',')
+        }
+      }
+      if (filters.agencyList && !this.isFree) {
+        if (Array.isArray(filters.agencyList)) {
+          this.filters.agencyList = filters.agencyList
+        } else {
+          this.filters.agencyList = filters.agencyList.split(',')
+        }
+      }
+
+      console.log(filters.agencyList)
+
       // 信息类型
       if (filters.infoType) {
         const key = 'infoType'
@@ -1093,6 +1124,9 @@ export default {
         winnerTel: this.filters.winnerConcat.join(','),
         buyerTel: this.filters.buyerConcat.join(','),
         exclusionWords: this.filters.notKey.join(','),
+        buyer: this.filters.buyerList.join(','),
+        winner: this.filters.winnerList.join(','),
+        agency: this.filters.agencyList.join(','),
         fileExists: this.filters.fileExists.join(','),
         splitKeywords: this.pageState.splitKeywords
       }
@@ -1594,6 +1628,9 @@ export default {
         buyertel: this.filters.buyerConcat.join(','),
         fileExists: fileExists || '0',
         notkey: this.filters.notKey.join(','),
+        buyer: this.filters.buyerList.join(','),
+        winner: this.filters.winnerList.join(','),
+        agency: this.filters.agencyList.join(','),
         searchGroup,
         searchMode: this.filters.searchMode.join('') - 0,
         wordsMode,
@@ -1853,13 +1890,16 @@ export default {
       }
     },
     formatFilterItems (item) {
-      console.log('flag!!!!!!!!!!!!!')
+      console.log('flag!!!!!!!!!!!!!', item)
       item.scope = item.selectType
       item.infotype = item.subtype
       const formatted = FilterHistoryAjaxModel2ViewModel.formatAll(item)
       const formattedItem = {
         ...formatted,
         notKey: item.notkey,
+        buyerList: item.buyer,
+        winnerList: item.winner,
+        agencyList: item.agency,
         fileExists: [item.fileExists || '0'],
         price: {
           start: item.minprice,

+ 1 - 1
apps/mobile/src/views/tabbar/Box.vue

@@ -12,7 +12,7 @@
       </div>
       <div class="work-common-box">
         <p class="mr-lr12 title-box">
-          <span class="content-title">常用功能</span>
+          <span class="content-title">我的常用</span>
           <span class="title-handle-span" @click="settingCommonFeature">
             <AppIcon name="Setting" />
             <span class="setting-text">设置</span>

+ 1 - 1
apps/mobile/src/views/tabbar/Home.vue

@@ -337,7 +337,7 @@ export default {
       //   this.toLogin()
       // }
     },
-    toSearch () {
+    toSearch () { 
       this.$router.push({
         name: 'search-middle-bidding',
         params: {

+ 288 - 0
apps/mobile/src/views/tabbar/RecommendedBannerlist.vue

@@ -0,0 +1,288 @@
+<template>
+  <div class="RecommendedBannerlist">
+    <div class="listContent">
+      <div @click="toSubManagePage">
+        <img src="@/assets/image/public/goset.png" alt="" class="topimg" />
+      </div>
+      <ProjectCell
+        v-for="item in list"
+        class="list-item"
+        cardType="simplify"
+        :class="item.className"
+        :detailList="item.detailList"
+        @click="onClickCell(item)"
+        :title="item.title"
+        :time="item.dateTime"
+        :isFile="item.isFile"
+        :keys="item.matchKeys"
+        :leftTopBadgeText="item.leftTopBadgeText"
+        :tags="item.tagList"
+        :pushSource="item.pushSource"
+        :key="item.id"
+        v-visited:subscribe="item._id"
+      >
+      </ProjectCell>
+      <AppEmpty
+        class="center"
+        state="sleep"
+        v-show="list.length === 0"
+      ></AppEmpty>
+    </div>
+  </div>
+</template>
+<script>
+import { mapGetters, mapState } from 'vuex'
+import qs from 'qs'
+import { AppEmpty, ProjectCell } from '@/ui'
+import {
+  getRecList,
+  getUserSubscribeSomeInfo,
+  getUserSubscribeKeywords
+} from '@/api/modules'
+import { LINKS } from '@/data'
+import {
+  openAppOrWxPage,
+  openLinkOfOther,
+  getRandomString,
+  formatMoney
+} from '@/utils'
+export default {
+  name: 'RecommendedBannerlist',
+  components: {
+    [ProjectCell.name]: ProjectCell,
+    [AppEmpty.name]: AppEmpty
+  },
+  computed: {
+    ...mapState('user', ['power', 'mySelectEntInfo']),
+    ...mapGetters('user', [
+      'isLogin',
+      'restfulApiUserTypeWitchVSwitch',
+      'vSwitch'
+    ]),
+    isHasKey() {
+      return this.mergedKeywords.length > 0
+    }
+  },
+  data() {
+    return {
+      isEntSubscribe: '',
+      mergedKeywords: [],
+      list: [],
+      // 用户其他信息
+      someInfo: {
+        userId: '',
+        hasKey: false, // 是否订阅关键词
+        isInTSguide: false, // 是否进入向导
+        isExpire: 0, // 超级订阅到期提醒
+        isOnTail: false, // 超级订阅试用状态
+        isPassCount: false, // 推送数量达到上限校验
+        otherFlag: false, // 首次用户推送查询“其他”
+        isread: false, // 某个通知??是否已读
+        industry: [] // 会员订阅的行业
+      }
+    }
+  },
+
+  created() {
+    this.getUserSubscribeSomeInfo()
+    this.getMergedEntSubscribeKeywords()
+    this.getList()
+  },
+  methods: {
+    formatMoney,
+    async onClickCell(item) {
+      const { _id, industry } = item
+      const { inWX } = this.$envs
+      const query = {}
+      if (industry) {
+        query.industry = industry
+      }
+      if (item.matchKeys) {
+        query.keywords = item.matchKeys.join(' ')
+      }
+      const targetMap = {
+        wx: `/article/content/${_id}.html?${qs.stringify(query)}`,
+        h5: `/jyapp/article/content/${_id}.html?${qs.stringify(query)}`,
+        app: `/jyapp/article/content/${_id}.html?${qs.stringify(query)}`
+      }
+      if (!this.isLogin) {
+        return openLinkOfOther(LINKS.APP登录页.app, {
+          query: {
+            url: inWX ? targetMap.wx : targetMap.app
+          }
+        })
+      }
+      openAppOrWxPage(targetMap)
+    },
+    // 去订阅页面
+    toSubManagePage() {
+      const { isUpgrade } = this.power
+      const { isInTSguide } = this.someInfo
+      // 打开订阅页面
+      // 商机管理跳转商机管理页面
+      if (this.vSwitch === 's') {
+        openLinkOfOther('/page_entniche_new/page/subsetting/sub_entrance.html')
+      } else if (this.vSwitch === 'v' || this.vSwitch === 'm') {
+        // 超级订阅/大会员
+        openAppOrWxPage(LINKS.订阅管理页面, {
+          query: {
+            vSwitch: this.vSwitch
+          }
+        })
+      } else {
+        // vSwitch === 'f' || vSwitch === ''
+        // 是否是免费用户订阅升级用户 默认true
+        if (isInTSguide) {
+          openAppOrWxPage(LINKS.老关键词列表)
+        } else {
+          if (isUpgrade || !this.isHasKey) {
+            // 无关键词时候,也要跳转订阅管理
+            openAppOrWxPage(LINKS.订阅管理页面)
+          } else {
+            // 老用户订阅用户跳转老订阅管理
+            openAppOrWxPage(LINKS.老关键词列表)
+          }
+        }
+      }
+
+      // openLinkOfOther(
+      //   '/page_entniche_new/page/sub_management/sub_management_system.html?pagesource=enterprise'
+      // )
+    },
+    // 获取用户订阅信息
+    async getUserSubscribeSomeInfo() {
+      try {
+        const {
+          data = {},
+          error_code: code = 0,
+          // eslint-disable-next-line no-unused-vars
+          error_msg: msg
+        } = await getUserSubscribeSomeInfo(this.restfulApiUserTypeWitchVSwitch)
+        if (code === 0 && data) {
+          Object.assign(this.someInfo, data)
+        }
+      } catch (error) {
+        // eslint-disable-next-line no-empty
+      } finally {
+      }
+    },
+    async getMergedEntSubscribeKeywords() {
+      const {
+        ent_buy_vip: entBuyVip,
+        ent_buy_member: entBuyMember,
+        power_source: powerSource,
+        user_power: userPower
+      } = this.mySelectEntInfo
+      const params = {
+        ent_buy_vip: entBuyVip,
+        ent_buy_member: entBuyMember,
+        powerSource,
+        userPower,
+        isEnt: false
+      }
+      const {
+        data,
+        error_code: code,
+        error_msg: msg
+      } = await getUserSubscribeKeywords(
+        this.restfulApiUserTypeWitchVSwitch,
+        params
+      )
+      if (code === 0) {
+        this.mergedKeywords = data?.items || []
+      } else {
+        // eslint-disable-next-line no-console
+        console.warn(msg)
+      }
+    },
+    async getList() {
+      const loading = this.$toast.loading({
+        duration: 0,
+        message: 'loading...'
+      })
+      const {
+        data = {},
+        error_code: code = 0,
+        error_msg: msg
+      } = await getRecList({ spath: '2' }) // 1订阅更多 2模版消息
+      loading.clear()
+      if (code === 0 && data) {
+        const list = data.list
+        if (Array.isArray(list)) {
+          this.preSortList(list)
+          this.list = list
+        }
+      } else {
+        this.$toast(msg || '请求失败')
+      }
+    },
+    preSortList(list = []) {
+      if (!Array.isArray(list)) return
+      list.forEach(this.preSortItem)
+    },
+    // 格式化查询后的每一项数据
+    preSortItem(item) {
+      if (!item) return
+      const { area, collection, projectInfo } = item
+      const {
+        vip_power: vipPower,
+        member_power: memberPower,
+        user_power: userPower
+      } = this.mySelectEntInfo
+      item.star = !!collection
+      // 参标状态
+      item.isCB = {
+        id: '',
+        value: 0
+      }
+      // 是否有附件
+      item.isFile = item?.ca_fileExists || false
+      item.leftTopBadgeText = item.site === '剑鱼信息发布平台' ? '用户发布' : ''
+      // 拟建项目独有参数
+      if (projectInfo) {
+        Object.assign(item, projectInfo)
+      }
+      if (!Array.isArray(item.matchKeys)) {
+        item.matchKeys = []
+      }
+
+      // 个人订阅下, 来自企业分发的信息(企业超级订阅/企业大会员/企业商机管理), 都要添加标签
+      const powerFromEnt =
+        vipPower === 1 || memberPower === 1 || userPower === 1
+      if (powerFromEnt) {
+        item.pushSource = item.source
+      }
+      const buyerClass =
+        item?.buyerClass && item?.buyerClass !== '其它'
+          ? item?.buyerClass
+          : undefined
+      // 标签
+      item.tagList = [
+        area || '全国',
+        buyerClass,
+        item?.type || item?.subtype,
+        // 有中标金额取中标金额,没有取预算,预算没有置空
+        // eslint-disable-next-line prettier/prettier
+        item?.bidAmount ? formatMoney(item?.bidAmount - 0) : item?.budget ? formatMoney(item?.budget - 0)  : ''
+      ].filter((v) => v)
+
+      // 添加随机id
+      item.id = `${item._id}--${getRandomString(8).toLowerCase()}`
+      item.dateTime = item.publishTime ? item.publishTime * 1000 : ''
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.topimg {
+  width: 100%;
+  display: block;
+  // position: fixed;
+  // top: 0;
+  // left: 0;
+}
+.listContent {
+  // padding-top: 0.96rem;
+}
+</style>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 442 - 154
apps/mobile/src/views/tabbar/Subscribe.vue


+ 15 - 4
apps/mobile/src/views/treasurebox/FeatureSettings.vue

@@ -3,7 +3,8 @@
     <div class="j-main page-content">
       <div class="work-common-box common-use-module">
         <p class="pd-lr12 title-box">
-          <span class="content-title">常用功能</span>
+          <span class="content-title">我的常用</span>
+          <span class="content-count">(<em>{{ commonCount }}</em>/{{commonMaxNum}})</span>
         </p>
         <common-use  :is-settings="true" :no-data="true"/>
       </div>
@@ -40,8 +41,12 @@ export default {
   },
   computed: {
     ...mapGetters('treasureBox', [
-      'commonFunctions'
-    ])
+      'commonFunctions',
+      'commonMaxNum'
+    ]),
+    commonCount () {
+      return this.commonFunctions?.length || 0
+    }
   },
   created () {
   },
@@ -96,9 +101,15 @@ $content-title-font-size: 18px;
     }
     .title-box{
       display: flex;
-      justify-content: space-between;
+      //justify-content: space-between;
       align-items: center;
       margin-bottom:10px;
+      .content-count{
+        font-size:14px;
+        em {
+          color:#2ABED1;
+        }
+      }
     }
     .work-common-box{
       background-color:#fff;

+ 31 - 10
apps/mobile/src/views/uersales/newuser/index.vue

@@ -5,7 +5,7 @@
       <div class="title-box">
         <div class="title_top">
           <h5 class="page-title">选择您的业务标签</h5>
-          <div class="jumpto_btn" @click.stop="postNewUerData(false)">
+          <div v-if="!urlSource" class="jumpto_btn" @click.stop="postNewUerData(false)">
             跳过
           </div>
         </div>
@@ -70,9 +70,8 @@
       </div>
     </div>
     <div class="j-button-group j-footer">
-      <button tag="button" class="j-button-confirm" @click.stop="postNewUerData(true)">
-        开启剑鱼之旅
-      </button>
+      <button v-if="!urlSource" tag="button" class="j-button-confirm" @click.stop="postNewUerData(true)">开启剑鱼之旅</button>
+      <button v-else tag="button" class="j-button-confirm" @click.stop="submitNewUerData">提交</button>
     </div>
     <Dialog :title="dparam.setTitle" :show-dialog="dparam.setDialog" @cancel="cancel" @confirm="confirm"
       :confirmButtonText="dparam.confirmButtonText">
@@ -166,7 +165,8 @@ export default {
       value: '去开启',
       label: '根据设置的推送时间发送APP消息,APP提醒设置请前往APP操作'
     },
-    timer: null
+    timer: null,
+    urlSource: '', // 路由来源
   }),
   computed: {
     showApp () {
@@ -179,6 +179,7 @@ export default {
   created () {
     callHideTab(0)
     this.getUserInfoApi()
+    this.urlSource = this.$route.query?.source || ''
   },
   mounted () {
     callHideTab(0)
@@ -477,7 +478,10 @@ export default {
      * @param datas
      */
     getNewUerSales () {
-      ajaxGetIsNewUerSales({})
+      const data = {
+        source: this.urlSource || ''
+      }
+      ajaxGetIsNewUerSales(data)
         .then((res) => {
           if (res && res.data && res?.error_code === 0) {
             this.subtitle = res.data.intro
@@ -525,8 +529,14 @@ export default {
         }
       })
     },
+    // 提交(带有source,需要callback)
+    submitNewUerData () {
+      this.postNewUerData(true, () => {
+        history.back()
+      })
+    },
     // 提交跳过
-    postNewUerData (type) {
+    postNewUerData (type, callback) {
       this.setPageTrack(`点击-${type ? '开启剑鱼之旅' : '跳过'}`)
       const msgSet = {}
       this.msgTypeContent.forEach((v) => {
@@ -555,14 +565,25 @@ export default {
       // Object.keys(this.selectPopInfo).forEach(v => {
       //   codesArr[v] = this.selectPopInfo[v].select
       // })
-
+      // 添加source
+      if(this.urlSource) {
+        this.$set(codesArr, 'source', this.urlSource)
+      }
       ajaxGetappNewUerSales(codesArr)
         .then((res) => {
           if (res?.error_code === 0) {
-            this.goHomePage()
+            if(typeof callback === 'function') {
+              callback()
+            } else {
+              this.goHomePage()
+            }
           }
         }).catch((e) => {
-          this.goHomePage()
+          if(typeof callback === 'function') {
+            callback()
+          } else {
+            this.goHomePage()
+          }
           console.warn('请求跳过信息异常:', e)
         })
     },

+ 1 - 0
apps/mobile/vite.config.js

@@ -35,6 +35,7 @@ export default defineConfig(({command}) => {
     base: '/jy_mobile/',
     build: {
       outDir: '../../dist/jy_mobile',
+      emptyOutDir: true,
       // terserOptions: {
       //   compress: {
       //     drop_console: false, // 默认为true

+ 1 - 0
apps/work-bench/.env.development

@@ -6,3 +6,4 @@ VUE_APP_BASE_URL='/home'
 VUE_APP_BASE_PUBLIC='/'
 VUE_APP_BASE_SITE='https://jybx3-webtest.jydev.jianyu360.com'
 VUE_APP_BASE_LOGO='https://cdn-ali.jianyu360.com/images/swordfish/sf_01_new.png?v=0928'
+VUE_APP_SIMPLE_LOGO='https://cdn-ali.jianyu360.com/common-module/public/image/logo_new.png?v=0928'

+ 1 - 0
apps/work-bench/.env.production

@@ -6,3 +6,4 @@ VUE_APP_BASE_URL='/page_workDesktop'
 VUE_APP_BASE_PUBLIC='/page_workDesktop/'
 VUE_APP_BASE_SITE=''
 VUE_APP_BASE_LOGO='https://cdn-ali.jianyu360.com/images/swordfish/sf_01_new.png?v=0928'
+VUE_APP_SIMPLE_LOGO='https://cdn-ali.jianyu360.com/common-module/public/image/logo_new.png?v=0928'

+ 2 - 2
apps/work-bench/public/index.html

@@ -4,7 +4,7 @@
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width,initial-scale=1.0">
-    <link rel="stylesheet" href="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/23.10.26/iconfont.css">
+    <link rel="stylesheet" href="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/24.2.21/iconfont.css">
     <title>工作台</title>
     <% if (process.env.NODE_ENV === 'production') { %>
         <script src="/common-module/public/head.js?v=<%= htmlWebpackPlugin.options.assets.version %>"></script>
@@ -15,7 +15,7 @@
       <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
     </noscript>
     <div id="app"></div>
-    <script src="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/23.10.26/iconfont.js"></script>
+    <script src="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/24.2.21/iconfont.js"></script>
     <script src="//cdn-common.jianyu360.com/cdn/lib/jquery/3.5.1/jquery.min.js"></script>
 
     <% if (process.env.NODE_ENV === 'production') { %>

+ 0 - 49
apps/work-bench/public/index.html.bak

@@ -1,49 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
-<<<<<<< HEAD
-    <link rel="stylesheet" href="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/23.8.30/iconfont.css">
-=======
-    <link rel="stylesheet" href="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/23.9.28/iconfont.css">
->>>>>>> main
-    <title>工作台</title>
-    <% if (process.env.NODE_ENV === 'production') { %>
-        <script src="/common-module/public/head.js?v=<%= htmlWebpackPlugin.options.assets.version %>"></script>
-    <% } %>
-  </head>
-  <body>
-    <noscript>
-      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
-    </noscript>
-    <div id="app"></div>
-<<<<<<< HEAD
-    <script src="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/23.8.30/iconfont.js"></script>
-=======
-    <script src="https://cdn-common.jianyu360.com/cdn/assets/iconfont/pc/23.9.28/iconfont.js"></script>
->>>>>>> main
-    <script src="//cdn-common.jianyu360.com/cdn/lib/jquery/3.5.1/jquery.min.js"></script>
-
-    <% if (process.env.NODE_ENV === 'production') { %>
-    <!--  临时处理 fotter 中覆盖 appendChild 导致的 qiankun 加载资源异常问题  -->
-    <!--  (后续可尝试从 qiankun 自定义资源拦截排查处理该问题)  -->
-    <script>
-        window._needInjectionTrackStatus = false
-    </script>
-    <script src="/common-module/public/fotter.js?v=<%= htmlWebpackPlugin.options.assets.version %>"></script>
-    <script src="/js/jyWebScoket.js?v=<%= htmlWebpackPlugin.options.assets.version %>"></script>
-    <script src="/common-module/ad/js/msgbuoy.js?v=<%= htmlWebpackPlugin.options.assets.version %>"></script>
-<!--    智能助手-->
-<!--    <script src="/common-module/chat/jy-chat.umd.js?auto=true&v=<%= htmlWebpackPlugin.options.assets.version %>"></script>-->
-    <% } %>
-
-    <% if (process.env.NODE_ENV === 'development') { %>
-    <!-- test--- msg加载 公共js方法 -->
-    <script src="/common-module/ad/js/msgbuoy.js?v=<%= htmlWebpackPlugin.options.assets.version %>"></script>
-    <% } %>
-
-    <!-- built files will be auto injected -->
-  </body>
-</html>

+ 1 - 1
apps/work-bench/src/components/NavUserInfo.vue

@@ -255,7 +255,7 @@ export default {
 </style>
 <style scoped lang="scss">
 .nav-user-info-module {
-  margin-left: 48px;
+  margin-left: 20px;
   cursor: pointer;
 }
 

+ 1 - 1
apps/work-bench/src/register-app.js

@@ -106,7 +106,7 @@ export const subApps = {
     describe: '该应用包含大会员画像、推送、周报月报、个人桌面等功能',
     qiankun: {
       name: 'bigMemberSubApp',
-      entry: 'http://192.168.40.158:8081/page_big_pc/index.html',
+      entry: '/page_big_pc/index.html',
       rule: '/big',
       props: {
         inject: ({ Vue, router }) => {

+ 1 - 1
packages/work-bench-frame/packages/components/Navbar/components/item.vue

@@ -64,7 +64,7 @@ export default {
 }
 
 ::v-deep .el-badge__content.is-fixed {
-  top: -2px;
+  top: 0;
   right: 16px;
   line-height: 16px;
   background-color: #ff3a20;

+ 174 - 69
packages/work-bench-frame/packages/components/Navbar/index.vue

@@ -2,73 +2,87 @@
   <div class="navbar-group">
     <div class="logo">
       <img :src="logo" alt="logo" @click="goSiteHome">
+      <h1>剑鱼工作台</h1>
     </div>
     <div class="navbar-content-group">
-      <!--   头部搜索模块   -->
-      <navbar-search class="search-module"></navbar-search>
-      <div class="nav-group">
+      <div class="website-group">
         <div
-          class="nav-item"
-          :class="nav.class"
-          v-for="(nav, index) in navs"
+          class="website-item"
+          v-for="(site, index) in websites"
           :key="index"
-          @click="onSelectNav(nav)"
+          @click="onWebsiteNav(site)"
         >
-          <template v-if="nav.plugin">
-            <navbar-item :badge="nav.badge" :svg="nav.svg" :nav="nav" v-popover:[nav.plugin]></navbar-item>
-          </template>
-          <navbar-item :badge="nav.badge" :svg="nav.svg" :nav="nav" v-else></navbar-item>
+         <i class="iconfont" :class="site.icon"></i>
+         <span>{{site.label}}</span>
         </div>
-        <!--    提供自定义用户信息插槽    -->
-        <slot name="nav-user-info" v-bind:info="userInfo"></slot>
-        <el-popover
-          popper-class="nav-popover"
-          ref="navInfo"
-          placement="bottom"
-          trigger="hover"
-          :visible-arrow="false"
-        >
-          <div class="nav-user-info-group">
-            <navbar-item :nav="{ icon: userInfo.avatar }" mode="vertical">
-              <div class="info-group">
-                <span v-if="userInfo.name">{{ userInfo.name }}</span>
-                <span v-if="userInfo.phone">{{ userInfo.phone }}</span>
-              </div>
-            </navbar-item>
-            <button @click="onClickOut">退出登录</button>
+      </div>
+      <section class="navbar-content-group--right">
+        <!--   头部搜索模块   -->
+        <navbar-search class="search-module"></navbar-search>
+        <div class="nav-group">
+          <div
+            class="nav-item"
+            :class="nav.class"
+            v-for="(nav, index) in navs"
+            :key="index"
+            @click="onSelectNav(nav)"
+          >
+            <template v-if="nav.plugin">
+              <navbar-item :badge="nav.badge" :svg="nav.svg" :nav="nav" v-popover:[nav.plugin]></navbar-item>
+            </template>
+            <navbar-item :badge="nav.badge" :svg="nav.svg" :nav="nav" v-else></navbar-item>
           </div>
-        </el-popover>
-        <el-popover
-          popper-class="nav-popover"
-          ref="navCustomer"
-          placement="bottom"
-          trigger="hover"
-          :visible-arrow="false"
-        >
-          <div class="nav-custom-info-group">
-            <div class="info-item" v-if="hasExclusiveCustomer">
-              <div class="after-tag-box recommend-tag">推荐</div>
-              <navbar-item
-                :nav="{ label: exclusiveCustomerInfo.name, icon: 'icon-weixin_line' }"></navbar-item>
-              <navbar-item
-                class="qrcode-group"
-                :nav="{
-                label: '微信扫一扫',
-                icon: exclusiveCustomerInfo.qrcode
-              }"
-                mode="vertical"
-              >
+          <!--    提供自定义用户信息插槽    -->
+          <slot name="nav-user-info" v-bind:info="userInfo"></slot>
+          <el-popover
+            popper-class="nav-popover"
+            ref="navInfo"
+            placement="bottom"
+            trigger="hover"
+            :visible-arrow="false"
+          >
+            <div class="nav-user-info-group">
+              <navbar-item :nav="{ icon: userInfo.avatar }" mode="vertical">
+                <div class="info-group">
+                  <span v-if="userInfo.name">{{ userInfo.name }}</span>
+                  <span v-if="userInfo.phone">{{ userInfo.phone }}</span>
+                </div>
               </navbar-item>
+              <button @click="onClickOut">退出登录</button>
             </div>
-            <div class="info-item" @click="onClickCustomer('在线咨询')">
-              <navbar-item :nav="{ label: '在线咨询', icon: 'icon-kefu_xian' }" :svg="true"></navbar-item>
-            </div>
-            <div class="info-item" @click="onClickCustomer('客服热线')">
-              <navbar-item :nav="{ label: '客服热线:400-108-6670', icon: 'icon-telphone_line' }"></navbar-item>
+          </el-popover>
+          <el-popover
+            popper-class="nav-popover"
+            ref="navCustomer"
+            placement="bottom"
+            trigger="hover"
+            :visible-arrow="false"
+          >
+            <div class="nav-custom-info-group">
+              <div class="info-item" v-if="hasExclusiveCustomer">
+                <div class="after-tag-box recommend-tag">推荐</div>
+                <navbar-item
+                  :nav="{ label: exclusiveCustomerInfo.name, icon: 'icon-weixin_line' }"></navbar-item>
+                <navbar-item
+                  class="qrcode-group"
+                  :nav="{
+                  label: '微信扫一扫',
+                  icon: exclusiveCustomerInfo.qrcode
+                }"
+                  mode="vertical"
+                >
+                </navbar-item>
+              </div>
+              <div class="info-item" @click="onClickCustomer('在线咨询')">
+                <navbar-item :nav="{ label: '在线咨询', icon: 'icon-kefu_xian' }" :svg="true"></navbar-item>
+              </div>
+              <div class="info-item" @click="onClickCustomer('客服热线')">
+                <navbar-item :nav="{ label: '客服热线:400-108-6670', icon: 'icon-telphone_line' }"></navbar-item>
+              </div>
             </div>
-          </div>
-        </el-popover>
-      </div>
+          </el-popover>
+        </div>
+      </section>
     </div>
     <div v-if="showCustomer" class="customer-container">
       <iframe :src="customerUrl" name="_blank" frameborder="0" width="100%" height="100%"></iframe>
@@ -94,15 +108,15 @@ export default {
   data () {
     return {
       home: process.env.VUE_APP_BASE_SITE,
-      logo: process.env.VUE_APP_BASE_LOGO,
+      logo: process.env.VUE_APP_SIMPLE_LOGO,
       navs: [
-        {
-          label: '前往官网',
-          icon: 'icon-houtui'
-        },
+        // {
+        //   label: '前往官网',
+        //   icon: 'icon-houtui'
+        // },
         {
           label: '消息中心',
-          icon: 'icon-a-Property1gongzuozhuomianProperty2xiaoxizhongxinProperty3grey',
+          icon: 'icon-nav-message',
           badge: 0
         },
         {
@@ -111,6 +125,20 @@ export default {
           svg: true,
           plugin: 'navCustomer'
         }
+      ],
+      websites: [
+        {
+          label: '标讯主站',
+          icon: 'icon-a-Property1gray1'
+        },
+        {
+          label: '剑鱼官网',
+          icon: 'icon-a-Property1gray2'
+        },
+        {
+          label: '商情门户',
+          icon: 'icon-a-Property1gray'
+        }
       ]
     }
   },
@@ -248,6 +276,44 @@ export default {
      */
     onClickCustomer (type) {
       this.$BRACE.$emit('click-nav-customer', type)
+    },
+    /**
+     * 前往商情门户(资讯站群)
+     */
+    goSiteCms () {
+      this.$BRACE.$emit({
+        fKey: 'goSiteCms',
+        spareFn: (link) => this.$BRACE.methods.open({
+          route: { link }
+        })
+      }, '/jycms')
+    },
+    /**
+     * 前往剑鱼官网(brand)
+     */
+    goSiteBrand () {
+      this.$BRACE.$emit({
+        fKey: 'goSiteBrand',
+        spareFn: (link) => this.$BRACE.methods.open({
+          route: { link }
+        })
+      }, '/brand')
+    },
+    onWebsiteNav (nav) {
+      switch (nav.label) {
+        case '标讯主站': {
+          this.goSiteHome()
+          break
+        }
+        case '剑鱼官网': {
+          this.goSiteBrand()
+          break
+        }
+        case '商情门户': {
+          this.goSiteCms()
+          break
+        }
+      }
     }
   }
 }
@@ -529,7 +595,7 @@ export default {
   justify-content: space-between;
   min-height: 64px;
   min-width: 800px;
-  padding: 0 36px;
+  padding: 0 24px;
   box-sizing: border-box;
   background-color: #ffffff;
   box-shadow: 0px 2px 8px -1px rgba(0, 0, 0, 0.0800);
@@ -544,7 +610,8 @@ export default {
   }
 
   .logo {
-    display: inline-block;
+    display: flex;
+    align-items: center;
     width: auto;
     height: 32px;
     cursor: pointer;
@@ -552,6 +619,12 @@ export default {
     img {
       height: 100%;
     }
+    h1{
+      margin-left: 8px;
+      font-size: 18px;
+      line-height: 24px;
+      color: #2ABED1;
+    }
   }
 
   .navbar-content-group {
@@ -560,11 +633,16 @@ export default {
     justify-content: space-between;
 
     .search-module {
-      margin-left: 82px;
-      margin-right: 16px;
+      margin-left: 32px;
+      margin-right: 20px;
       flex: 1;
       max-width: 440px;
     }
+    &--right{
+      flex: 1;
+      @extend .flex-horizontal;
+      justify-content: flex-end;
+    }
   }
 
   .nav-group {
@@ -579,13 +657,40 @@ export default {
     }
 
     .nav-item + .nav-item {
-      margin-left: 48px;
+      margin-left: 20px;
+    }
+  }
+  .website-group{
+    @extend .flex-horizontal;
+    margin-left: 16px;
+    .website-item{
+      @extend .flex-horizontal;
+      padding: 6px 8px;
+      margin-right: 10px;
+      font-size: 14px;
+      border-radius: 6px;
+      color: #1D1D1D;
+      border: 1px solid rgba(0, 0, 0, 0.08);
+      cursor: pointer;
+      .iconfont{
+        margin-right: 4px;
+        color: #686868;
+        font-size: 20px;
+      }
+      &:hover{
+        color: #fff;
+        background: #2ABED1;
+        border-color: #2ABED1;
+        i{
+          color: #fff;
+        }
+      }
     }
   }
   .customer-container{
     // display: none;
     position: fixed;
-     right: 84px;
+    right: 84px;
     bottom: 80px;
     max-height: 674px;
     min-height: 400px;

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio