search-library.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. <template>
  2. <view class="search-library" :class="{ unScorll: showLibaray }">
  3. <!-- 搜索框区域 -->
  4. <view class="sticky-container">
  5. <search-header
  6. :menuList="menuItemList"
  7. :currentMenu="currentMenu"
  8. v-model="keyword"
  9. @clear="
  10. onClear()
  11. hideLibrary()
  12. "
  13. @search="
  14. onSearch()
  15. hideLibrary()
  16. "
  17. @input="onInput"
  18. @menuClick="onMenuItemClick"
  19. ></search-header>
  20. <!-- 搜索分类区域 -->
  21. <view class="search-category">
  22. <tui-tabs
  23. :tabs="tabs"
  24. :currentTab="currentTab"
  25. :sliderWidth="40"
  26. :bold="true"
  27. :unlined="true"
  28. :height="60"
  29. sliderBgColor="#FF5B00"
  30. selectedColor="#FF5B00"
  31. @change="onTabChange"
  32. ></tui-tabs>
  33. </view>
  34. </view>
  35. <!-- 搜索结果区域 -->
  36. <view class="search-wrapper">
  37. <!-- 产品列表 -->
  38. <product-list v-if="currentTab === 0" :list="productList" @change="onProductFilterItemChange" />
  39. <!-- 相关文章 -->
  40. <article-list v-if="currentTab === 1" :list="articleList" @change="onArticleFilterItemChange" />
  41. <!-- 相关资料 -->
  42. <material-list v-if="currentTab === 2" :list="materialList" />
  43. <!-- 相关百科 -->
  44. <encyclopedia-list v-if="currentTab === 3" :list="encyclopediaList" />
  45. </view>
  46. <!-- 列表为空 -->
  47. <view class="empty-box" v-if="!isLoading && isListEmpty">
  48. <image
  49. class="empty-image"
  50. src="https://img.caimei365.com/group1/M00/03/8D/Cmis215XHXWAHCoqAAELHadZ9Xg365.png"
  51. ></image>
  52. <text class="empty-text">抱歉,没有{{ currentTabItem.name }}!</text>
  53. </view>
  54. <!-- 加载 loading -->
  55. <template v-if="!isListEmpty">
  56. <tui-loadmore :index="2" :visible="isLoading" />
  57. <tui-nomore
  58. :text="hasMore ? '上拉加载更多' : '没有更多了'"
  59. :visible="!isLoading"
  60. backgroundColor="#f5f5f5"
  61. />
  62. </template>
  63. <!-- 搜索关键词库提示 -->
  64. <view class="search-library-container" v-show="showLibaray">
  65. <scroll-view scroll-y="true" class="search-library-scroll">
  66. <div class="search-library-wrapper">
  67. <view
  68. class="keyword-item"
  69. v-for="item in libraryWordList"
  70. :key="item"
  71. @click="
  72. onLibraryClick(item)
  73. hideLibrary()
  74. "
  75. >
  76. <text class="iconfont icon-sousuo"></text>
  77. <view class="content" v-html="item.text"></view>
  78. </view>
  79. </div>
  80. </scroll-view>
  81. </view>
  82. <!-- 商品筛选框 -->
  83. <tui-drawer mode="right" :visible="showDrawer" @close="showDrawer = false">
  84. <product-filter :brandList="brandList" @confirm="onDrawerConfirm" @reset="onDrawerConfirm"></product-filter>
  85. </tui-drawer>
  86. </view>
  87. </template>
  88. <script>
  89. import ProductList from './components/product-list.vue'
  90. import ArticleList from './components/article-list.vue'
  91. import MaterialList from './components/material-list.vue'
  92. import EncyclopediaList from './components/encyclopedia-list.vue'
  93. import ProductFilter from './components/product-filter.vue'
  94. import SearchHeader from './components/search-header.vue'
  95. import { debounce } from '@/common/config/common'
  96. import { mapGetters } from 'vuex'
  97. const myDebounce = fn => debounce(fn, 200, false)
  98. export default {
  99. components: {
  100. ProductList,
  101. ArticleList,
  102. MaterialList,
  103. EncyclopediaList,
  104. ProductFilter,
  105. SearchHeader
  106. },
  107. data() {
  108. const menuItemList = [{ id: 1, name: '产品' }, { id: 2, name: '供应商' }]
  109. const tabs = [{ name: '相关产品' }, { name: '相关文章' }, { name: '相关资料' }, { name: '相关百科' }]
  110. return {
  111. // 容器相关
  112. tabs: tabs,
  113. currentTab: 0,
  114. menuItemList: menuItemList,
  115. currentMenu: 0,
  116. keyword: '',
  117. showLibaray: false,
  118. libraryWordList: [],
  119. isLoading: true,
  120. searchType: 'library',
  121. // 用户信息
  122. userInfo: {
  123. userId: 0,
  124. userIdentity: 1,
  125. vipFlag: 0,
  126. firstClubType: 0
  127. },
  128. // 产品相关
  129. currentSortItem: {},
  130. showDrawer: false,
  131. brandList: [],
  132. productList: [],
  133. productTotal: 0,
  134. productHasMore: true,
  135. productListQuery: {
  136. identity: 1,
  137. keyword: '',
  138. pageNum: 1,
  139. pageSize: 10,
  140. sortField: '',
  141. sortType: 1,
  142. newType: 1,
  143. actiType: 1,
  144. brandIds: '', // 品牌Id
  145. newFlag: 0, // 查询新品标记,默认0,新品1
  146. promotionFlag: 0, // 查询促销标记,默认0,促销1
  147. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  148. linkageFlag: 1 ,// 关键词来源是否为用户搜索 0 是 1 不是
  149. serviceProviderId:''
  150. },
  151. brandListQuery: {
  152. keyword: '',
  153. id: '',
  154. idType: '',
  155. identity: 1
  156. },
  157. // 美业资料
  158. materialList: [],
  159. materialTotal: 0,
  160. materialHasMore: true,
  161. materialListQuery: {
  162. keyword: '', //查询关键词
  163. productType: 0, //商品类型 0 全部 1 仪器 2 产品
  164. pageNum: 1,
  165. pageSize: 10,
  166. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  167. linkageFlag: 1 // 关键词来源是否为用户搜索 0 是 1 不是
  168. },
  169. // 采美文章
  170. articleList: [],
  171. articleTotal: 0,
  172. articleHasMore: true,
  173. articleListQuery: {
  174. keyword: '',
  175. pageSize: 10,
  176. pageNum: 1,
  177. status: 1,
  178. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  179. linkageFlag: 1 // 关键词来源是否为用户搜索 0 是 1 不是
  180. },
  181. // 采美百科
  182. encyclopediaList: [],
  183. encyclopediaTotal: 0,
  184. encyclopediaHasMore: true,
  185. encyclopediaListQuery: {
  186. keyword: '',
  187. pageSize: 10,
  188. pageNum: 1,
  189. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  190. linkageFlag: 1 // 关键词来源是否为用户搜索 0 是 1 不是
  191. }
  192. }
  193. },
  194. computed: {
  195. ...mapGetters(['identity']),
  196. // 当前选中tab
  197. currentTabItem() {
  198. return this.tabs[this.currentTab]
  199. },
  200. // 列表是否为空
  201. isListEmpty() {
  202. return (
  203. (this.currentTab === 0 && this.productList.length === 0) ||
  204. (this.currentTab === 1 && this.articleList.length === 0) ||
  205. (this.currentTab === 2 && this.materialList.length === 0) ||
  206. (this.currentTab === 3 && this.encyclopediaList.length === 0)
  207. )
  208. },
  209. // 是否还有更多
  210. hasMore() {
  211. return (
  212. (this.currentTab === 0 && this.productHasMore) ||
  213. (this.currentTab === 1 && this.articleHasMore) ||
  214. (this.currentTab === 2 && this.materialHasMore) ||
  215. (this.currentTab === 3 && this.encyclopediaHasMore)
  216. )
  217. }
  218. },
  219. onLoad(option) {
  220. this.keyword = option.keyword || ''
  221. this.initStorage()
  222. this.initList()
  223. },
  224. onReachBottom() {
  225. this.getList()
  226. },
  227. methods: {
  228. // 获取用户信息
  229. async initStorage(option) {
  230. const userInfo = await this.$api.getStorage()
  231. if (!userInfo) return
  232. this.userInfo = { ...this.userInfo, ...userInfo }
  233. },
  234. // 初始化
  235. initList() {
  236. const action = {
  237. 0: this.initProductList,
  238. 1: this.initArticleList,
  239. 2: this.initMaterialList,
  240. 3: this.initEncyclopediaList
  241. }
  242. this.isLoading = true
  243. action[this.currentTab] && action[this.currentTab]()
  244. },
  245. // 获取列表
  246. getList() {
  247. const action = {
  248. 0: this.fetchProductList,
  249. 1: this.fetchArticleList,
  250. 2: this.fetchMaterialList,
  251. 3: this.fetchEncyclopediaList
  252. }
  253. if (!this.hasMore) return
  254. this.isLoading = true
  255. action[this.currentTab] && action[this.currentTab]()
  256. },
  257. // 初始化采美百科文章列表
  258. initEncyclopediaList() {
  259. this.encyclopediaList = []
  260. this.encyclopediaListQuery.keyword = this.keyword
  261. this.encyclopediaListQuery.pageNum = 1
  262. this.encyclopediaListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  263. this.fetchEncyclopediaList()
  264. },
  265. // 获取采美百科文章列表
  266. fetchEncyclopediaList: myDebounce(async function() {
  267. try {
  268. this.isLoading = true
  269. const res = await this.LibraryService.GetEncyclopediaList(this.encyclopediaListQuery)
  270. this.encyclopediaList = [...this.encyclopediaList, ...res.data.results]
  271. this.encyclopediaHasMore = res.data.hasNextPage
  272. this.encyclopediaTotal = res.data.totalRecord
  273. this.encyclopediaListQuery.pageNum++
  274. } catch (e) {
  275. console.log(e)
  276. } finally {
  277. this.isLoading = false
  278. }
  279. }),
  280. // 初始化采美文章列表
  281. initArticleList() {
  282. this.articleList = []
  283. this.articleListQuery.keyword = this.keyword
  284. this.articleListQuery.pageNum = 1
  285. this.articleListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  286. this.fetchArticleList()
  287. },
  288. // 获取采美文章列表
  289. fetchArticleList: myDebounce(async function() {
  290. try {
  291. this.isLoading = true
  292. const res = await this.LibraryService.GetArticleList(this.articleListQuery)
  293. if (!res.data) return
  294. const result = JSON.parse(res.data)
  295. this.articleList = [...this.articleList, ...result.items]
  296. this.articleTotal = result.articleTotal
  297. this.articleHasMore = result.articleTotal > this.articleList.length
  298. this.articleListQuery.pageNum++
  299. } catch (e) {
  300. console.log(e)
  301. } finally {
  302. this.isLoading = false
  303. }
  304. }),
  305. // 采美文章筛选
  306. onArticleFilterItemChange(filterData) {
  307. this.isLoading = true
  308. this.articleList = []
  309. this.articleListQuery.keyword = this.keyword
  310. this.articleListQuery.pageNum = 1
  311. this.articleListQuery.status = filterData.status
  312. this.articleListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  313. this.fetchArticleList()
  314. },
  315. // 初始化美业资料列表
  316. initMaterialList() {
  317. this.materialList = []
  318. this.materialListQuery.keyword = this.keyword
  319. this.materialListQuery.pageNum = 1
  320. this.materialListQuery.productType = 0
  321. this.fetchMaterialList()
  322. },
  323. // 获取美业资料列表
  324. fetchMaterialList: myDebounce(async function() {
  325. try {
  326. this.isLoading = true
  327. const res = await this.LibraryService.GetProductArchive(this.materialListQuery)
  328. this.materialList = [...this.materialList, ...res.data.results]
  329. this.materialHasMore = res.data.hasNextPage
  330. this.materialTotal = res.data.totalRecord
  331. this.materialListQuery.pageNum++
  332. } catch (e) {
  333. console.log(e)
  334. } finally {
  335. this.isLoading = false
  336. }
  337. }),
  338. // 初始化产品列表
  339. initProductList() {
  340. this.productList = []
  341. this.productListQuery.pageNum = 1
  342. this.productListQuery.keyword = this.keyword
  343. this.productListQuery.identity = this.userInfo.userIdentity
  344. this.productListQuery.sortType = 1
  345. this.productListQuery.sortField = ''
  346. this.productListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  347. this.fetchProductList()
  348. this.fetchBrandList()
  349. },
  350. // 获取产品列表
  351. fetchProductList: myDebounce(async function() {
  352. try {
  353. this.isLoading = true
  354. const { data } = await this.ProductService.GetProductSearchList(this.productListQuery)
  355. if (!data) return
  356. const result = JSON.parse(data)
  357. this.productTotal = result.total
  358. this.fetchProductPrice(result.items)
  359. this.productListQuery.pageNum++
  360. } catch (e) {
  361. this.isLoading = false
  362. }
  363. }),
  364. // 获取产品价格
  365. async fetchProductPrice(productItems = []) {
  366. try {
  367. //获取价格
  368. const productIds = productItems.map(item => item.productId).join(',')
  369. const { data } = await this.ProductService.querySearchProductPrice({
  370. userId: this.userInfo.userId,
  371. productIds: productIds,
  372. source: 2 // 来源 1 WWW 2 小程序
  373. })
  374. const productList = productItems.map(productItem => {
  375. const product = data.find(item => item.productId === productItem.productId)
  376. return { ...productItem, ...product }
  377. })
  378. this.productList = [...this.productList, ...productList]
  379. this.productHasMore = this.productTotal > this.productList.length
  380. console.log(this.productList)
  381. } catch (e) {
  382. console.log(e)
  383. } finally {
  384. this.isLoading = false
  385. }
  386. },
  387. // 产品筛选
  388. productFilter() {
  389. this.isLoading = true
  390. this.productList = []
  391. this.productListQuery.pageNum = 1
  392. const sortType = { asc: 0, desc: 1 }
  393. const currentSortItem = this.currentSortItem
  394. switch (currentSortItem.id) {
  395. case 1:
  396. this.productListQuery.sortField = ''
  397. this.productListQuery.sortType = 1
  398. break
  399. case 2:
  400. this.productListQuery.sortField = 'sales'
  401. this.productListQuery.sortType = sortType[currentSortItem.sortType]
  402. break
  403. case 3:
  404. this.productListQuery.sortField = 'favorite'
  405. this.productListQuery.sortType = sortType[currentSortItem.sortType]
  406. break
  407. case 4:
  408. this.productListQuery.sortField = 'price'
  409. this.productListQuery.sortType = sortType[currentSortItem.sortType]
  410. break
  411. }
  412. this.fetchProductList()
  413. },
  414. // 获取品牌列表
  415. async fetchBrandList() {
  416. try {
  417. this.brandListQuery.keyword = this.keyword
  418. const { data } = await this.ProductService.getCommoditySearchQUeryBrand(this.brandListQuery)
  419. if (!data) return
  420. this.brandList = data.map(brand => {
  421. brand.checked = false
  422. return brand
  423. })
  424. } catch (e) {
  425. console.log(e)
  426. }
  427. },
  428. // drawer reset or confirm
  429. onDrawerConfirm(e) {
  430. this.showDrawer = false
  431. this.productListQuery.brandIds = e.brandList.map(brand => brand.id).join(',')
  432. this.productListQuery.promotionFlag = e.promotionFlag
  433. this.productListQuery.newFlag = e.newFlag
  434. this.productFilter()
  435. },
  436. // 产品筛选切换
  437. onProductFilterItemChange(item) {
  438. if (item.id === 5) {
  439. this.showDrawer = true
  440. return
  441. }
  442. this.currentSortItem = item
  443. this.productFilter()
  444. },
  445. // 搜素关键词库
  446. fetchLibraryWordList: myDebounce(async function() {
  447. try {
  448. const keyword = this.keyword
  449. const res = await this.LibraryService.GetSearchKeywordList({ keyword })
  450. if (!res.data) {
  451. this.libraryWordList = []
  452. }
  453. this.libraryWordList = res.data.map(item => {
  454. item.text = item.keyword.replace(keyword, `<span style="color:#FF5B00;">${keyword}</span>`)
  455. return item
  456. })
  457. if (this.libraryWordList.length <= 0) return
  458. this.showLibaray = true
  459. } catch (e) {
  460. console.log(e)
  461. }
  462. }),
  463. // 关键词点击
  464. onLibraryClick(item) {
  465. this.keyword = item.keyword
  466. this.searchType = 'library'
  467. this.initList()
  468. },
  469. // menu-item 点击事件
  470. onMenuItemClick(index) {
  471. this.currentMenu = index
  472. },
  473. // tab 切换
  474. onTabChange(current) {
  475. this.currentTab = current.index
  476. this.searchType = 'search'
  477. this.initList()
  478. },
  479. // 搜索
  480. onSearch: myDebounce(function() {
  481. if (this.currentMenu === 0) {
  482. this.searchType = 'keyword'
  483. return this.initList()
  484. }
  485. this.$api.navigateTo(`/pages/search/search-supplier?keyWord=${this.keyword}`)
  486. }),
  487. // 清空搜索框
  488. onClear() {
  489. this.keyword = ''
  490. },
  491. // 用户输入
  492. onInput() {
  493. this.fetchLibraryWordList()
  494. },
  495. // 隐藏library list
  496. hideLibrary() {
  497. this.showLibaray = false
  498. }
  499. }
  500. }
  501. </script>
  502. <style lang="scss" scoped>
  503. .search-library {
  504. min-height: 100vh;
  505. background: #f5f5f5;
  506. &.unScorll {
  507. height: 100vh;
  508. overflow: hidden;
  509. }
  510. .sticky-container {
  511. position: sticky;
  512. width: 100%;
  513. left: 0;
  514. top: 0;
  515. z-index: 99;
  516. background: #f5f5f5;
  517. }
  518. .search-category {
  519. padding: 20rpx 0;
  520. background: #fff;
  521. margin-bottom: 24rpx;
  522. }
  523. .search-wrapper {
  524. background: #fff;
  525. }
  526. .empty-box {
  527. width: 100%;
  528. display: flex;
  529. align-items: center;
  530. flex-direction: column;
  531. margin-top: 140rpx;
  532. .empty-image {
  533. width: 500rpx;
  534. height: 395rpx;
  535. }
  536. .empty-text {
  537. font-size: 24rpx;
  538. color: #999;
  539. margin-top: 32rpx;
  540. }
  541. }
  542. .search-library-container {
  543. position: fixed;
  544. top: 86rpx;
  545. left: 0;
  546. z-index: 1000;
  547. width: 100vw;
  548. height: calc(100vh - 88rpx);
  549. background: #f5f5f5;
  550. .search-library-scroll {
  551. height: 95%;
  552. }
  553. .search-library-wrapper {
  554. background: #fff;
  555. padding-top: 40rpx;
  556. box-sizing: border-box;
  557. max-height: calc(100vh - 88rpx);
  558. overflow-y: auto;
  559. .keyword-item {
  560. width: 100%;
  561. display: flex;
  562. justify-content: space-between;
  563. height: 90rpx;
  564. line-height: 90rpx;
  565. box-sizing: border-box;
  566. padding: 0 48rpx 0 32rpx;
  567. border-top: 1rpx solid #e1e1e1;
  568. &:last-child {
  569. border-bottom: 1rpx solid #e1e1e1;
  570. }
  571. .iconfont {
  572. display: block;
  573. width: 34rpx;
  574. margin-right: 26rpx;
  575. flex-shrink: 0;
  576. font-size: 34rpx;
  577. color: #999;
  578. }
  579. .content {
  580. flex: 1;
  581. flex-shrink: 0;
  582. overflow: hidden;
  583. text-overflow: ellipsis;
  584. display: -webkit-box;
  585. -webkit-line-clamp: 1; // 这里控制几行显示省略号
  586. -webkit-box-orient: vertical;
  587. font-size: 28rpx;
  588. color: #333;
  589. text {
  590. color: #FF5B00;
  591. }
  592. }
  593. }
  594. }
  595. }
  596. }
  597. </style>