search-library.vue 20 KB

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