Search.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <template>
  2. <div class="pages--search">
  3. <div class="j-header">
  4. <search id="mySearch" key="search-page" :defalultValue="listState.value" @input="onInput" @submit="doSearch"></search>
  5. <van-tabs v-model="docsTypeConf.active"
  6. v-if="docsTypeConf.list.length"
  7. title-active-color="#2ABED1"
  8. title-inactive-color="#5F5E64"
  9. color="#2ABED1"
  10. @change="docTypeChange"
  11. >
  12. <van-tab v-for="(item, index) in docsTypeConf.list" :key="index" :title="item.label" :name="item.type"></van-tab>
  13. </van-tabs>
  14. <div class="sort-list flex-r-c center" v-if="listState.loaded">
  15. <div
  16. class="sort-list-item flex-r-c center flex"
  17. :class="{
  18. active: item.active,
  19. reverse: item.sort
  20. }"
  21. v-for="(item, index) in sortTypeList"
  22. :key="index"
  23. @click="sortAndSearch(item, index)"
  24. >
  25. <span class="s-i-label">{{ item.label }}</span>
  26. <van-icon name="down" class="s-i-icon" />
  27. </div>
  28. </div>
  29. </div>
  30. <div class="j-main" ref="scrollWrap">
  31. <van-list
  32. v-model="listState.loading"
  33. :finished="listState.finished"
  34. :offset="listState.offset"
  35. @load="getList"
  36. class="more-list calc-height-1px"
  37. ref="vanList"
  38. >
  39. <div>
  40. <Card
  41. v-for="(item, index) in listState.list"
  42. :key="index"
  43. :title="item.docName"
  44. :desc="item.docSummary"
  45. :docType="item.docFileType"
  46. :highlightKey="highlightKey"
  47. :price="item.price"
  48. :subInfo="calcSubInfo(item)"
  49. @onClick="toDocDetail(item)"
  50. />
  51. </div>
  52. <Empty v-if="listState.list.length === 0 && listState.loaded">暂无数据</Empty>
  53. </van-list>
  54. </div>
  55. </div>
  56. </template>
  57. <script lang="ts">
  58. import { Component, Vue } from 'vue-property-decorator'
  59. import { Tabs, Tab, Icon, List } from 'vant'
  60. import Search from '@/components/Search.vue'
  61. import Card from '@/components/docs-card/Card.vue'
  62. import Empty from '@/components/common/Empty.vue'
  63. import { mapState, mapMutations, mapActions } from 'vuex'
  64. import { dateFormatter } from '@/utils/globalFunctions'
  65. @Component({
  66. name: 'search-page',
  67. components: {
  68. [Tab.name]: Tab,
  69. [Tabs.name]: Tabs,
  70. [List.name]: List,
  71. [Icon.name]: Icon,
  72. Search,
  73. Card,
  74. Empty
  75. },
  76. computed: {
  77. ...mapState('main', {
  78. searchState: (state: any) => state.searchPageData
  79. })
  80. },
  81. methods: {
  82. ...mapMutations({
  83. saveSearchState: 'main/saveSearchPageState',
  84. clearSearchState: 'main/clearSearchPageState'
  85. }),
  86. ...mapActions({
  87. doSearchRquesst: 'main/doSearchDocs',
  88. getIndexTags: 'main/getIndexTags'
  89. })
  90. }
  91. })
  92. export default class extends Vue {
  93. protected searchState: any
  94. protected saveSearchState: any
  95. protected clearSearchState: any
  96. protected doSearchRquesst: any
  97. protected getIndexTags: any
  98. docsTypeConf = {
  99. active: '',
  100. list: []
  101. }
  102. sortTypeList = [
  103. {
  104. type: 'tSort',
  105. label: '上传时间',
  106. sort: 0,
  107. active: true
  108. },
  109. {
  110. type: 'dSort',
  111. label: '下载次数',
  112. sort: 0,
  113. active: false
  114. },
  115. {
  116. type: 'vSort',
  117. label: '浏览人数',
  118. sort: 0,
  119. active: false
  120. }
  121. ]
  122. listState ={
  123. value: '',
  124. loaded: false, // 是否首次加载完成
  125. loading: false,
  126. finished: true,
  127. pageNum: 1,
  128. pageSize: 10,
  129. offset: 80,
  130. scrollTop: 0,
  131. total: 0,
  132. list: []
  133. }
  134. restored = false // 当前数据是否走过缓存
  135. get activeSortType () {
  136. return this.sortTypeList.find(item => {
  137. return item.active
  138. })
  139. }
  140. get highlightKey () {
  141. return this.listState.value.split(/\s+/)
  142. }
  143. created () {
  144. this.restored = this.reStoreState()
  145. if (!this.restored) {
  146. this.getTags()
  147. }
  148. }
  149. mounted () {
  150. if (!this.restored) {
  151. this.onFocus()
  152. }
  153. }
  154. onFocus () {
  155. console.log('fffff')
  156. const dom = document.querySelector('#mySearch input') as HTMLInputElement
  157. if (dom) {
  158. this.$nextTick(() => {
  159. setTimeout(() => {
  160. dom.focus()
  161. }, 200)
  162. })
  163. }
  164. }
  165. // 恢复数据至第一次请求的状态(页码等)
  166. resetListState () {
  167. const state = {
  168. loading: false,
  169. finished: true,
  170. pageNum: 1,
  171. total: 0,
  172. scrollTop: 0,
  173. list: []
  174. }
  175. Object.assign(this.listState, state)
  176. }
  177. onInput (search: string) {
  178. this.listState.value = search
  179. }
  180. docTypeChange () {
  181. if (!this.listState.value) return
  182. this.resetListState()
  183. this.setScrollTop()
  184. this.listState.finished = false
  185. this.getList()
  186. }
  187. sortAndSearch (item: any) {
  188. if (item.active) {
  189. // 改变sort
  190. // item.sort = item.sort ? 0 : 1
  191. } else {
  192. this.sortTypeList.forEach(s => {
  193. s.active = false
  194. })
  195. item.active = true
  196. }
  197. if (!this.listState.value) return
  198. this.resetListState()
  199. this.setScrollTop()
  200. this.listState.finished = false
  201. this.getList()
  202. }
  203. doSearch () {
  204. if (!this.listState.value) return
  205. this.resetListState()
  206. this.setScrollTop()
  207. this.listState.finished = false
  208. this.getList()
  209. }
  210. toDocDetail (item: any) {
  211. const { docId: id } = item
  212. this.saveState()
  213. this.$router.push({
  214. name: 'details',
  215. params: { id }
  216. })
  217. }
  218. async getTags () {
  219. const { data } = await this.getIndexTags()
  220. if (Array.isArray(data)) {
  221. const list: any = data.map(item => {
  222. return {
  223. type: item,
  224. label: item
  225. }
  226. })
  227. this.docsTypeConf.list = list
  228. if (data.length) {
  229. const i: any = this.docsTypeConf.list[0]
  230. this.docsTypeConf.active = i.type
  231. }
  232. }
  233. return data
  234. }
  235. async getList () {
  236. if (!this.listState.value) return
  237. const query = {
  238. keyWord: this.listState.value,
  239. tag: this.docsTypeConf.active === '全部' ? '' : this.docsTypeConf.active,
  240. sort: this.activeSortType?.type,
  241. num: this.listState.pageNum,
  242. size: this.listState.pageSize
  243. }
  244. console.log('搜索参数:', query)
  245. this.listState.loading = true
  246. const { data } = await this.doSearchRquesst(query)
  247. this.listState.loading = false
  248. this.listState.loaded = true
  249. this.$toast.clear()
  250. if (data && Array.isArray(data.list)) {
  251. this.listState.pageNum += 1
  252. this.listState.total = data.total
  253. this.listState.list = this.listState.list.concat(data.list)
  254. } else {
  255. this.listState.finished = true
  256. }
  257. // 数据请求完成(根据页码计算,当前页是否是最后一页)
  258. // 请求完成后,页码就变为了下一页的页面,所以这里要-1
  259. const isLastPage = (this.listState.pageNum - 1) * this.listState.pageSize >= this.listState.total
  260. if (isLastPage) {
  261. this.listState.finished = true
  262. }
  263. }
  264. calcSubInfo (item: any) {
  265. const { uploadDate, downTimes } = item
  266. const subInfoArr = []
  267. if (uploadDate !== undefined) {
  268. subInfoArr.push(dateFormatter(uploadDate, 'yyyy/MM/dd'))
  269. }
  270. if (downTimes !== undefined) {
  271. subInfoArr.push(`${downTimes}次下载`)
  272. }
  273. return subInfoArr
  274. }
  275. setScrollTop () {
  276. this.$nextTick(() => {
  277. const wrapper: any = this.$refs.scrollWrap
  278. wrapper.scrollTop = this.listState.scrollTop
  279. })
  280. }
  281. reStoreState () {
  282. const listInfo = this.searchState
  283. if (!listInfo || Object.keys(listInfo).length === 0) {
  284. return false
  285. } else {
  286. for (const key in listInfo) {
  287. this.$data[key] = listInfo[key]
  288. }
  289. setTimeout(() => {
  290. this.setScrollTop()
  291. this.clearSearchState()
  292. }, 50)
  293. return true
  294. }
  295. }
  296. saveState () {
  297. const wrapper: any = this.$refs.scrollWrap
  298. this.listState.scrollTop = wrapper.scrollTop
  299. const d = {
  300. docsTypeConf: this.docsTypeConf,
  301. sortTypeList: this.sortTypeList,
  302. listState: this.listState
  303. }
  304. this.saveSearchState(d)
  305. }
  306. }
  307. </script>
  308. <style scoped lang="scss">
  309. .pages--search {
  310. background-color: #F5F6F7;
  311. box-sizing: border-box;
  312. ::v-deep .van-tabs {
  313. width: 100%;
  314. font-size: 14px;
  315. line-height: 20px;
  316. }
  317. .j-header {
  318. flex-direction: column;
  319. }
  320. .sort-list {
  321. width: 100%;
  322. font-size: 14px;
  323. line-height: 20px;
  324. .sort-list-item {
  325. padding: 12px 16px;
  326. &.active {
  327. color: #2ABED1;
  328. }
  329. &.reverse {
  330. .s-i-icon {
  331. transform: rotate(180deg);
  332. }
  333. }
  334. .s-i-label {
  335. margin-right: 4px;
  336. }
  337. .s-i-icon {
  338. font-weight: bold;
  339. transition: transform .2 ease;
  340. }
  341. }
  342. }
  343. }
  344. </style>