search-library.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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="#F3B574"
  30. selectedColor="#F3B574"
  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"></tui-loadmore>
  57. <tui-nomore
  58. :text="hasMore ? '上拉加载更多' : '没有更多了'"
  59. :visible="!isLoading"
  60. backgroundColor="#f5f5f5"
  61. ></tui-nomore>
  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. },
  150. brandListQuery: {
  151. keyword: '',
  152. id: '',
  153. idType: '',
  154. identity: 1
  155. },
  156. // 美业资料
  157. materialList: [],
  158. materialTotal: 0,
  159. materialHasMore: true,
  160. materialListQuery: {
  161. keyword: '', //查询关键词
  162. productType: 0, //商品类型 0 全部 1 仪器 2 产品
  163. pageNum: 1,
  164. pageSize: 10,
  165. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  166. linkageFlag: 1 // 关键词来源是否为用户搜索 0 是 1 不是
  167. },
  168. // 采美文章
  169. articleList: [],
  170. articleTotal: 0,
  171. articleHasMore: true,
  172. articleListQuery: {
  173. keyword: '',
  174. pageSize: 10,
  175. pageNum: 1,
  176. status: 1,
  177. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  178. linkageFlag: 1 // 关键词来源是否为用户搜索 0 是 1 不是
  179. },
  180. // 采美百科
  181. encyclopediaList: [],
  182. encyclopediaTotal: 0,
  183. encyclopediaHasMore: true,
  184. encyclopediaListQuery: {
  185. keyword: '',
  186. pageSize: 10,
  187. pageNum: 1,
  188. productFlag: 1, // 是否统计关键词 1 统计 0 不统计
  189. linkageFlag: 1 // 关键词来源是否为用户搜索 0 是 1 不是
  190. }
  191. }
  192. },
  193. computed: {
  194. ...mapGetters(['identity']),
  195. // 当前选中tab
  196. currentTabItem() {
  197. return this.tabs[this.currentTab]
  198. },
  199. // 列表是否为空
  200. isListEmpty() {
  201. return (
  202. (this.currentTab === 0 && this.productList.length === 0) ||
  203. (this.currentTab === 1 && this.articleList.length === 0) ||
  204. (this.currentTab === 2 && this.materialList.length === 0) ||
  205. (this.currentTab === 3 && this.encyclopediaList.length === 0)
  206. )
  207. },
  208. // 是否还有更多
  209. hasMore() {
  210. return (
  211. (this.currentTab === 0 && this.productHasMore) ||
  212. (this.currentTab === 1 && this.articleHasMore) ||
  213. (this.currentTab === 2 && this.materialHasMore) ||
  214. (this.currentTab === 3 && this.encyclopediaHasMore)
  215. )
  216. }
  217. },
  218. onLoad(option) {
  219. this.keyword = option.keyword || ''
  220. this.initStorage()
  221. this.initList()
  222. },
  223. onReachBottom() {
  224. this.getList()
  225. },
  226. methods: {
  227. // 获取用户信息
  228. async initStorage(option) {
  229. const userInfo = await this.$api.getStorage()
  230. if (!userInfo) return
  231. this.userInfo = { ...this.userInfo, ...userInfo }
  232. },
  233. // 初始化
  234. initList() {
  235. const action = {
  236. 0: this.initProductList,
  237. 1: this.initArticleList,
  238. 2: this.initMaterialList,
  239. 3: this.initEncyclopediaList
  240. }
  241. this.isLoading = true
  242. action[this.currentTab] && action[this.currentTab]()
  243. },
  244. // 获取列表
  245. getList() {
  246. const action = {
  247. 0: this.fetchProductList,
  248. 1: this.fetchArticleList,
  249. 2: this.fetchMaterialList,
  250. 3: this.fetchEncyclopediaList
  251. }
  252. if (!this.hasMore) return
  253. this.isLoading = true
  254. action[this.currentTab] && action[this.currentTab]()
  255. },
  256. // 初始化采美百科文章列表
  257. initEncyclopediaList() {
  258. this.encyclopediaList = []
  259. this.encyclopediaListQuery.keyword = this.keyword
  260. this.encyclopediaListQuery.pageNum = 1
  261. this.encyclopediaListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  262. this.fetchEncyclopediaList()
  263. },
  264. // 获取采美百科文章列表
  265. fetchEncyclopediaList: myDebounce(async function() {
  266. try {
  267. this.isLoading = true
  268. const res = await this.LibraryService.GetEncyclopediaList(this.encyclopediaListQuery)
  269. this.encyclopediaList = [...this.encyclopediaList, ...res.data.results]
  270. this.encyclopediaHasMore = res.data.hasNextPage
  271. this.encyclopediaTotal = res.data.totalRecord
  272. this.encyclopediaListQuery.pageNum++
  273. } catch (e) {
  274. console.log(e)
  275. } finally {
  276. this.isLoading = false
  277. }
  278. }),
  279. // 初始化采美文章列表
  280. initArticleList() {
  281. this.articleList = []
  282. this.articleListQuery.keyword = this.keyword
  283. this.articleListQuery.pageNum = 1
  284. this.articleListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  285. this.fetchArticleList()
  286. },
  287. // 获取采美文章列表
  288. fetchArticleList: myDebounce(async function() {
  289. try {
  290. this.isLoading = true
  291. const res = await this.LibraryService.GetArticleList(this.articleListQuery)
  292. if (!res.data) return
  293. const result = JSON.parse(res.data)
  294. this.articleList = [...this.articleList, ...result.items]
  295. this.articleTotal = result.articleTotal
  296. this.articleHasMore = result.articleTotal > this.articleList.length
  297. this.articleListQuery.pageNum++
  298. } catch (e) {
  299. console.log(e)
  300. } finally {
  301. this.isLoading = false
  302. }
  303. }),
  304. // 采美文章筛选
  305. onArticleFilterItemChange(filterData) {
  306. this.isLoading = true
  307. this.articleList = []
  308. this.articleListQuery.keyword = this.keyword
  309. this.articleListQuery.pageNum = 1
  310. this.articleListQuery.status = filterData.status
  311. this.articleListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  312. this.fetchArticleList()
  313. },
  314. // 初始化美业资料列表
  315. initMaterialList() {
  316. this.materialList = []
  317. this.materialListQuery.keyword = this.keyword
  318. this.materialListQuery.pageNum = 1
  319. this.materialListQuery.productType = 0
  320. this.fetchMaterialList()
  321. },
  322. // 获取美业资料列表
  323. fetchMaterialList: myDebounce(async function() {
  324. try {
  325. this.isLoading = true
  326. const res = await this.LibraryService.GetProductArchive(this.materialListQuery)
  327. this.materialList = [...this.materialList, ...res.data.results]
  328. this.materialHasMore = res.data.hasNextPage
  329. this.materialTotal = res.data.totalRecord
  330. this.materialListQuery.pageNum++
  331. } catch (e) {
  332. console.log(e)
  333. } finally {
  334. this.isLoading = false
  335. }
  336. }),
  337. // 初始化产品列表
  338. initProductList() {
  339. this.productList = []
  340. this.productListQuery.pageNum = 1
  341. this.productListQuery.keyword = this.keyword
  342. this.productListQuery.identity = this.userInfo.userIdentity
  343. this.productListQuery.sortType = 1
  344. this.productListQuery.sortField = ''
  345. this.productListQuery.linkageFlag = this.searchType === 'library' ? 1 : 0
  346. this.fetchProductList()
  347. this.fetchBrandList()
  348. },
  349. // 获取产品列表
  350. fetchProductList: myDebounce(async function() {
  351. try {
  352. this.isLoading = true
  353. const { data } = await this.ProductService.GetProductSearchList(this.productListQuery)
  354. if (!data) return
  355. const result = JSON.parse(data)
  356. this.productTotal = result.total
  357. this.fetchProductPrice(result.items)
  358. this.productListQuery.pageNum++
  359. } catch (e) {
  360. this.isLoading = false
  361. }
  362. }),
  363. // 获取产品价格
  364. async fetchProductPrice(productItems = []) {
  365. try {
  366. //获取价格
  367. const productIds = productItems.map(item => item.productId).join(',')
  368. const { data } = await this.ProductService.querySearchProductPrice({
  369. userId: this.userInfo.userId,
  370. productIds: productIds,
  371. source: 2 // 来源 1 WWW 2 小程序
  372. })
  373. const productList = productItems.map(productItem => {
  374. const product = data.find(item => item.productId === productItem.productId)
  375. return { ...productItem, ...product }
  376. })
  377. this.productList = [...this.productList, ...productList]
  378. this.productHasMore = this.productTotal > this.productList.length
  379. console.log(this.productList)
  380. } catch (e) {
  381. console.log(e)
  382. } finally {
  383. this.isLoading = false
  384. }
  385. },
  386. // 产品筛选
  387. productFilter() {
  388. this.isLoading = true
  389. this.productList = []
  390. this.productListQuery.pageNum = 1
  391. const sortType = { asc: 0, desc: 1 }
  392. const currentSortItem = this.currentSortItem
  393. switch (currentSortItem.id) {
  394. case 1:
  395. this.productListQuery.sortField = ''
  396. this.productListQuery.sortType = 1
  397. break
  398. case 2:
  399. this.productListQuery.sortField = 'sales'
  400. this.productListQuery.sortType = sortType[currentSortItem.sortType]
  401. break
  402. case 3:
  403. this.productListQuery.sortField = 'favorite'
  404. this.productListQuery.sortType = sortType[currentSortItem.sortType]
  405. break
  406. case 4:
  407. this.productListQuery.sortField = 'price'
  408. this.productListQuery.sortType = sortType[currentSortItem.sortType]
  409. break
  410. }
  411. this.fetchProductList()
  412. },
  413. // 获取品牌列表
  414. async fetchBrandList() {
  415. try {
  416. this.brandListQuery.keyword = this.keyword
  417. const { data } = await this.ProductService.getCommoditySearchQUeryBrand(this.brandListQuery)
  418. if (!data) return
  419. this.brandList = data.map(brand => {
  420. brand.checked = false
  421. return brand
  422. })
  423. } catch (e) {
  424. console.log(e)
  425. }
  426. },
  427. // drawer reset or confirm
  428. onDrawerConfirm(e) {
  429. this.showDrawer = false
  430. this.productListQuery.brandIds = e.brandList.map(brand => brand.id).join(',')
  431. this.productListQuery.promotionFlag = e.promotionFlag
  432. this.productListQuery.newFlag = e.newFlag
  433. this.productFilter()
  434. },
  435. // 产品筛选切换
  436. onProductFilterItemChange(item) {
  437. if (item.id === 5) {
  438. this.showDrawer = true
  439. return
  440. }
  441. this.currentSortItem = item
  442. this.productFilter()
  443. },
  444. // 搜素关键词库
  445. fetchLibraryWordList: myDebounce(async function() {
  446. try {
  447. const keyword = this.keyword
  448. const res = await this.LibraryService.GetSearchKeywordList({ keyword })
  449. if (!res.data) {
  450. this.libraryWordList = []
  451. }
  452. this.libraryWordList = res.data.map(item => {
  453. item.text = item.keyword.replace(keyword, `<span style="color:#F3B574;">${keyword}</span>`)
  454. return item
  455. })
  456. if (this.libraryWordList.length <= 0) return
  457. this.showLibaray = true
  458. } catch (e) {
  459. console.log(e)
  460. }
  461. }),
  462. // 关键词点击
  463. onLibraryClick(item) {
  464. this.keyword = item.keyword
  465. this.searchType = 'library'
  466. this.initList()
  467. },
  468. // menu-item 点击事件
  469. onMenuItemClick(index) {
  470. this.currentMenu = index
  471. },
  472. // tab 切换
  473. onTabChange(current) {
  474. this.currentTab = current.index
  475. this.searchType = 'search'
  476. this.initList()
  477. },
  478. // 搜索
  479. onSearch: myDebounce(function() {
  480. if (this.currentMenu === 0) {
  481. this.searchType = 'keyword'
  482. return this.initList()
  483. }
  484. this.$api.navigateTo(`/pages/search/search-supplier?keyWord=${this.keyword}`)
  485. }),
  486. // 清空搜索框
  487. onClear() {
  488. this.keyword = ''
  489. },
  490. // 用户输入
  491. onInput() {
  492. this.fetchLibraryWordList()
  493. },
  494. // 隐藏library list
  495. hideLibrary() {
  496. this.showLibaray = false
  497. }
  498. }
  499. }
  500. </script>
  501. <style lang="scss" scoped>
  502. .search-library {
  503. min-height: 100vh;
  504. background: #f5f5f5;
  505. &.unScorll {
  506. height: 100vh;
  507. overflow: hidden;
  508. }
  509. .sticky-container {
  510. position: sticky;
  511. width: 100%;
  512. left: 0;
  513. top: 0;
  514. z-index: 99;
  515. background: #f5f5f5;
  516. }
  517. .search-category {
  518. padding: 20rpx 0;
  519. background: #fff;
  520. margin-bottom: 24rpx;
  521. }
  522. .search-wrapper {
  523. background: #fff;
  524. }
  525. .empty-box {
  526. width: 100%;
  527. display: flex;
  528. align-items: center;
  529. flex-direction: column;
  530. margin-top: 140rpx;
  531. .empty-image {
  532. width: 500rpx;
  533. height: 395rpx;
  534. }
  535. .empty-text {
  536. font-size: 24rpx;
  537. color: #999;
  538. margin-top: 32rpx;
  539. }
  540. }
  541. .search-library-container {
  542. position: fixed;
  543. top: 86rpx;
  544. left: 0;
  545. z-index: 1000;
  546. width: 100vw;
  547. height: calc(100vh - 88rpx);
  548. background: #f5f5f5;
  549. .search-library-scroll {
  550. height: 95%;
  551. }
  552. .search-library-wrapper {
  553. background: #fff;
  554. padding-top: 40rpx;
  555. box-sizing: border-box;
  556. max-height: calc(100vh - 88rpx);
  557. overflow-y: auto;
  558. .keyword-item {
  559. width: 100%;
  560. display: flex;
  561. justify-content: space-between;
  562. height: 90rpx;
  563. line-height: 90rpx;
  564. box-sizing: border-box;
  565. padding: 0 48rpx 0 32rpx;
  566. border-top: 1rpx solid #e1e1e1;
  567. &:last-child {
  568. border-bottom: 1rpx solid #e1e1e1;
  569. }
  570. .iconfont {
  571. display: block;
  572. width: 34rpx;
  573. margin-right: 26rpx;
  574. flex-shrink: 0;
  575. font-size: 34rpx;
  576. color: #999;
  577. }
  578. .content {
  579. flex: 1;
  580. flex-shrink: 0;
  581. overflow: hidden;
  582. text-overflow: ellipsis;
  583. display: -webkit-box;
  584. -webkit-line-clamp: 1; // 这里控制几行显示省略号
  585. -webkit-box-orient: vertical;
  586. font-size: 28rpx;
  587. color: #333;
  588. text {
  589. color: #F3B574;
  590. }
  591. }
  592. }
  593. }
  594. }
  595. }
  596. </style>