searchBox.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <script>
  2. import {handle} from "../../until/handle";
  3. import {rCode} from "../../map/rcodeMap_esm";
  4. import Loading from "../public/loading.vue";
  5. import HideScroll from "../public/hideScroll.vue";
  6. export default {
  7. name: "searchBox",
  8. components: {Loading, HideScroll},
  9. props: {
  10. searchPlaceholder: {
  11. type: String,
  12. default: '输入搜索关键字'
  13. },
  14. loadTip: {
  15. type: String,
  16. default: '加载中'
  17. },
  18. loadMsg: {
  19. type: String,
  20. default: '加载失败'
  21. },
  22. limit: {
  23. type: Number,
  24. default: 20
  25. },
  26. loadDataApi: {
  27. type: Function,
  28. default: () => {}
  29. },
  30. },
  31. data(){
  32. return {
  33. searchLoading:false,// 搜索加载框
  34. searchTimer: null,// 防抖搜索框
  35. searchKey: '',// 关键字
  36. page: 1,// 页数
  37. count: 20,// 每页数量
  38. total: 0,// 当前数据总量
  39. loadState: 0,// 加载状态
  40. loadLock: false,// 加载锁
  41. domPlaceholder: [],//当前的数据列表
  42. }
  43. },
  44. mounted() {
  45. this.loadData(1,true)
  46. },
  47. methods: {
  48. async loadData(page,isPush){
  49. if(isPush){
  50. page = 1;
  51. this.loadState = 0;
  52. }
  53. this.searchLoading = true;
  54. // console.log('---------')
  55. // console.log(page);
  56. let searchParam = {
  57. }
  58. // 1.搜索相关设备
  59. let [err,res] = await this.loadDataApi({
  60. key: this.searchKey,//
  61. page: page,// 当前页数
  62. });
  63. this.loadLock = false;
  64. this.searchLoading = false;
  65. // 2.检查返回值是否正常
  66. if (err){
  67. console.log(err);
  68. this.msg = err.message;
  69. isPush?this.loadState=2:this.$message.error(this.msg);
  70. return false;
  71. }
  72. this.loadState = 1;
  73. // 只有在第一页时会需要加载总数
  74. if(res.page <= 1){
  75. this.total = res.total;
  76. }
  77. // 3.根据是否为新设备创建新数据集合
  78. if(isPush || page===1){
  79. this.domPlaceholder = new Array(res.total)
  80. .fill({
  81. loaded: false,
  82. val:{}
  83. });
  84. }
  85. console.log('total' + this.domPlaceholder.length)
  86. // 4.将当前的分块请求结果填充至指定位置
  87. let startIndex = (res.page - 1) * res.limit;
  88. let key = res.key;
  89. console.log(`startIndex:${startIndex} , endIndex:${res.count+startIndex}`);
  90. for (let i = startIndex,n=0;n<res.count;n++,i++){
  91. let item = res.data[n];
  92. if(item){
  93. // 时间字段转换
  94. // info.subTitle = time.dateFormat(time.timeStamp_to_Date(info.loginTime),'YY-MM-DD H:m:s');
  95. // 展示字段转换
  96. item['showText'] = item.name || item.title;
  97. item['innerText'] = item.showText.replace(key,`<span style="color:red;">${key}</span>`);
  98. }
  99. this.$set(this.domPlaceholder,i,{
  100. loaded:!!item,
  101. val:item?item:{}
  102. });
  103. }
  104. // 判断当前位置
  105. },
  106. onSearch(v){
  107. console.log('search');
  108. console.log(v);
  109. if(this.loadLock){
  110. this.$message.info('加载设备列表中')
  111. return
  112. }
  113. this.loadLock = true;
  114. this.loadData(1);
  115. },
  116. async onScrollChange(data){
  117. let scrollTop = data.scrollTop;
  118. let index = data.index;
  119. console.log('onScrollChange'+scrollTop + 'index:' + index);
  120. //获取
  121. let page = Math.floor(scrollTop/50/this.limit);
  122. console.log(page);
  123. // 加载数据
  124. if(page < this.page){
  125. console.log(`不加载数据`);
  126. }else{
  127. await this.loadData(page + 1);
  128. }
  129. console.log(`检测是否加载数据 total: ${this.total} <=
  130. limit: ${this.limit} + index: ${index} ===> ${this.total <= this.limit + index}`)
  131. if(this.total <= this.limit + index){
  132. console.log(`同时获取下一页数据`);
  133. await this.loadData(page + 2);
  134. }
  135. },
  136. onSelectedItem(item){
  137. this.$emit('onSelectedItem',item);
  138. }
  139. }
  140. }
  141. </script>
  142. <template>
  143. <div class="w-full h-full rounded bg-yellow-50 flex flex-col">
  144. <div class="w-full h-auto">
  145. <!-- 其他搜索项 -->
  146. <slot name="otherSearchItem"></slot>
  147. </div>
  148. <div class="serarch mt-1 box-border px-1">
  149. <a-input-search
  150. :placeholder="searchPlaceholder"
  151. enter-button
  152. :loading="searchLoading"
  153. v-model="searchKey"
  154. @search="onSearch"
  155. allow-clear
  156. style="border-radius: 0;"
  157. />
  158. </div>
  159. <div class="devices w-full h-48 md:h-full bg-white mt-2 relative">
  160. <loading
  161. :loading-state="loadState"
  162. :tip="loadTip"
  163. >
  164. <hide-scroll
  165. @onScroll="onScrollChange"
  166. :itemHeight="50"
  167. >
  168. <div
  169. v-for="(item,i) in domPlaceholder"
  170. :key="'search-key:'+i"
  171. class="item cursor-default" >
  172. <div
  173. v-if="item.loaded"
  174. class="text text-base">
  175. <span v-html="item.val.innerText"></span>
  176. <div class="subText text-gray-300 text-xs">{{ item.val.subTitle }}</div>
  177. </div>
  178. <div
  179. v-if="item.loaded"
  180. class="action">
  181. <a-button class="mx-1" @click="onSelectedItem(item.val)">选中</a-button>
  182. </div>
  183. <div class="w-full h-full bg-black text-red-500 opacity-30" v-else-if="!item.loaded">
  184. {{i}}.....loading
  185. </div>
  186. </div>
  187. </hide-scroll>
  188. <template v-slot:loadFail>
  189. <div class="w-full h-full flex justify-center items-center">
  190. <h2 class="text-2xl text-red-700">{{loadMsg}}</h2>
  191. <a-button primary @click="loadData(1)">重新加载</a-button>
  192. </div>
  193. </template>
  194. </loading>
  195. </div>
  196. </div>
  197. </template>
  198. <style scoped>
  199. .item{
  200. width: 100%;
  201. height: 50px;
  202. display: flex;
  203. align-items: center;
  204. border-bottom: 1px solid #66ccff;
  205. padding: 0 0.25em;
  206. box-sizing: border-box;
  207. }
  208. .text{
  209. width: 100%;
  210. height: 100%;
  211. }
  212. .subText{
  213. width: 100%;
  214. color: #afa7a7;
  215. }
  216. .action{
  217. display: flex;
  218. }
  219. </style>