wyb-pagination.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <template>
  2. <view
  3. class="wyb-pagination-box"
  4. :style="{
  5. paddingLeft: padding + 'rpx',
  6. paddingRight: padding + 'rpx',
  7. '--hover': autoHover
  8. }"
  9. >
  10. <view class="wyb-pagination-left" :style="{ opacity: currentPage === 1 ? 0.5 : 1 }">
  11. <view
  12. v-if="showFirst"
  13. :class="'wyb-pagination-first-page-' + (showIcon ? 'i' : 't')"
  14. :style="btnStyleStr"
  15. :hover-class="currentPage === 1 ? '' : 'wyb-pagination-hover'"
  16. @tap="onPageBtnTap('first-page')"
  17. >
  18. <view v-if="showIcon" class="iconfont icon-shuangjiantou left-arrow" />
  19. <text v-else>{{ firstText }}</text>
  20. </view>
  21. <view
  22. :class="'wyb-pagination-prev-page-' + (showIcon ? 'i' : 't')"
  23. :style="btnStyleStr"
  24. :hover-class="currentPage === 1 ? '' : 'wyb-pagination-hover'"
  25. @tap="onPageBtnTap('prev-page')"
  26. >
  27. <view v-if="showIcon" class="iconfont icon-danjiantou left-arrow" />
  28. <text v-else>{{ prevText }}</text>
  29. </view>
  30. </view>
  31. <view class="wyb-pagination-info" @tap.stop="onInfoTap">
  32. <view class="wyb-pagination-num" v-if="!infoClick">
  33. <text :style="{ color: currentColor }">{{ currentPage }}</text>
  34. <text class="wyb-pagination-span" :style="{ color: pageInfoColor }">/</text>
  35. <text :style="{ color: pageInfoColor }">{{ totalPage }}</text>
  36. <text v-if="showTotalItem" class="wyb-pagination-info-total" :style="{ color: RGBChange(pageInfoColor, 0.5, 'light') }">
  37. ({{ totalItems }})
  38. </text>
  39. </view>
  40. <!-- #ifndef MP-WEIXIN || APP-VUE || APP-NVUE || APP-PLUS || APP-PLUS-NVUE -->
  41. <view class="wyb-pagination-input" v-else>
  42. <input
  43. type="number"
  44. v-model="inputPage"
  45. :onpaste="false"
  46. :focus="infoFocus"
  47. :value="currentPage"
  48. :style="{ color: currentColor }"
  49. :cursor-spacing="cursorSpacing"
  50. @confirm="onInfoConfirm"
  51. @blur="onInfoBlur"
  52. />
  53. </view>
  54. <!-- #endif -->
  55. <!-- #ifdef MP-WEIXIN || APP-VUE || APP-NVUE || APP-PLUS || APP-PLUS-NVUE -->
  56. <view class="wyb-pagination-input" v-else>
  57. <input
  58. type="number"
  59. v-model="inputPage"
  60. :focus="infoFocus"
  61. :name="currentPage"
  62. :style="{ color: currentColor }"
  63. :cursor-spacing="cursorSpacing"
  64. @confirm="onInfoConfirm"
  65. @blur="onInfoBlur"
  66. />
  67. </view>
  68. <!-- #endif -->
  69. </view>
  70. <view class="wyb-pagination-right" :style="{ opacity: currentPage === totalPage ? 0.5 : 1 }">
  71. <view
  72. :class="'wyb-pagination-next-page-' + (showIcon ? 'i' : 't')"
  73. :style="btnStyleStr"
  74. :hover-class="currentPage === totalPage ? '' : 'wyb-pagination-hover'"
  75. @tap="onPageBtnTap('next-page')"
  76. >
  77. <view v-if="showIcon" class="iconfont icon-danjiantou right-arrow" />
  78. <text v-else>{{ nextText }}</text>
  79. </view>
  80. <view
  81. v-if="showLast"
  82. :class="'wyb-pagination-last-page-' + (showIcon ? 'i' : 't')"
  83. :style="btnStyleStr"
  84. :hover-class="currentPage === totalPage ? '' : 'wyb-pagination-hover'"
  85. @tap="onPageBtnTap('last-page')"
  86. >
  87. <view v-if="showIcon" class="iconfont icon-shuangjiantou right-arrow" />
  88. <text v-else>{{ lastText }}</text>
  89. </view>
  90. </view>
  91. </view>
  92. </template>
  93. <script>
  94. export default {
  95. data() {
  96. return {
  97. currentPage: this.current || 1,
  98. inputPage: '',
  99. infoClick: false,
  100. infoFocus: false
  101. };
  102. },
  103. computed: {
  104. totalPage() {
  105. return Math.ceil(parseFloat(this.totalItems) / parseFloat(this.pageItems));
  106. },
  107. autoHover() {
  108. if (this.btnStyle.backgroundColor) {
  109. return this.RGBChange(this.btnStyle.backgroundColor, 0.1, 'dark');
  110. } else {
  111. return this.RGBChange('#f8f8f8', 0.05, 'dark');
  112. }
  113. },
  114. btnStyleStr() {
  115. let styleStr = '';
  116. for (let key in this.btnStyle) {
  117. styleStr += `${this.sortFieldMatch(key)}: ${this.btnStyle[key]}; `;
  118. }
  119. return styleStr;
  120. }
  121. },
  122. watch: {
  123. current(val) {
  124. const oPage = this.currentPage;
  125. if (!Object.is(oPage, val)) {
  126. this.currentPage = val;
  127. this.$emit('change', {
  128. type: 'prop-page',
  129. current: this.currentPage
  130. });
  131. }
  132. }
  133. },
  134. props: {
  135. totalItems: {
  136. type: [String, Number],
  137. default: 20
  138. },
  139. pageItems: {
  140. type: [String, Number],
  141. default: 5
  142. },
  143. current: {
  144. type: Number,
  145. default: 1
  146. },
  147. prevText: {
  148. type: String,
  149. default: '上一页'
  150. },
  151. nextText: {
  152. type: String,
  153. default: '下一页'
  154. },
  155. firstText: {
  156. type: String,
  157. default: '首页'
  158. },
  159. lastText: {
  160. type: String,
  161. default: '尾页'
  162. },
  163. pageInfoColor: {
  164. type: String,
  165. default: '#494949'
  166. },
  167. currentColor: {
  168. type: String,
  169. default: '#007aff'
  170. },
  171. padding: {
  172. type: [String, Number],
  173. default: 15
  174. },
  175. btnStyle: {
  176. type: Object,
  177. default() {
  178. return {};
  179. }
  180. },
  181. showIcon: {
  182. type: Boolean,
  183. default: false
  184. },
  185. showTotalItem: {
  186. type: Boolean,
  187. default: false
  188. },
  189. showFirst: {
  190. type: Boolean,
  191. default: true
  192. },
  193. showLast: {
  194. type: Boolean,
  195. default: true
  196. },
  197. couldInput: {
  198. type: Boolean,
  199. default: true
  200. },
  201. cursorSpacing: {
  202. type: Number,
  203. default: 0
  204. }
  205. },
  206. methods: {
  207. onPageBtnTap(type) {
  208. switch (type) {
  209. case 'first-page':
  210. if (!Object.is(this.currentPage, 1)) {
  211. this.currentPage = 1;
  212. this.$emit('change', { type, current: this.currentPage });
  213. }
  214. break;
  215. case 'prev-page':
  216. if (!Object.is(this.currentPage, 1)) {
  217. this.currentPage--;
  218. this.$emit('change', { type, current: this.currentPage });
  219. }
  220. break;
  221. case 'next-page':
  222. if (!Object.is(this.currentPage, this.totalPage)) {
  223. this.currentPage++;
  224. this.$emit('change', { type, current: this.currentPage });
  225. }
  226. break;
  227. case 'last-page':
  228. if (!Object.is(this.currentPage, this.totalPage)) {
  229. this.currentPage = this.totalPage;
  230. this.$emit('change', { type, current: this.currentPage });
  231. }
  232. break;
  233. }
  234. },
  235. onInfoTap() {
  236. if (this.couldInput) {
  237. this.infoClick = true;
  238. this.inputPage = this.currentPage;
  239. setTimeout(() => {
  240. this.infoFocus = true;
  241. }, 10);
  242. }
  243. },
  244. onInfoConfirm(e) {
  245. let input = e.detail.value;
  246. const oPage = this.currentPage;
  247. if (parseFloat(input) > this.totalPage) {
  248. this.currentPage = this.totalPage;
  249. } else if (parseFloat(input) < 1) {
  250. this.currentPage = 1;
  251. } else if (input === '') {
  252. this.currentPage = oPage;
  253. } else {
  254. this.currentPage = parseFloat(input);
  255. }
  256. if (!Object.is(oPage, this.currentPage)) {
  257. this.$emit('change', {
  258. type: 'input-page',
  259. current: this.currentPage
  260. });
  261. }
  262. this.infoClick = false;
  263. this.$nextTick(() => {
  264. this.infoFocus = false;
  265. });
  266. },
  267. onInfoBlur(e) {
  268. let input = e.detail.value;
  269. const oPage = this.currentPage;
  270. if (parseFloat(input) > this.totalPage) {
  271. this.currentPage = this.totalPage;
  272. } else if (parseFloat(input) < 1) {
  273. this.currentPage = 1;
  274. } else if (input === '') {
  275. this.currentPage = oPage;
  276. } else {
  277. this.currentPage = parseFloat(input);
  278. }
  279. if (!Object.is(oPage, this.currentPage)) {
  280. this.$emit('change', {
  281. type: 'input-page',
  282. current: this.currentPage
  283. });
  284. }
  285. this.infoClick = false;
  286. this.$nextTick(() => {
  287. this.infoFocus = false;
  288. });
  289. },
  290. RGBChange(color, level, type) {
  291. // 判断颜色类型
  292. let r = 0,
  293. g = 0,
  294. b = 0,
  295. hasAlpha = false,
  296. alpha = 1;
  297. if (color.indexOf('#') !== -1) {
  298. // hex转rgb
  299. if (color.length === 4) {
  300. let arr = color.split('');
  301. color = '#' + arr[1] + arr[1] + arr[2] + arr[2] + arr[3] + arr[3];
  302. }
  303. let color16List = [color.substring(1, 3), color.substring(3, 5), color.substring(5, 7)];
  304. r = parseInt(color16List[0], 16);
  305. g = parseInt(color16List[1], 16);
  306. b = parseInt(color16List[2], 16);
  307. } else {
  308. hasAlpha = color.indexOf('a') !== -1;
  309. let root = color.slice();
  310. let idx = root.indexOf('(') + 1;
  311. root = root.substring(idx);
  312. let firstDotIdx = root.indexOf(',');
  313. r = parseFloat(root.substring(0, firstDotIdx));
  314. root = root.substring(firstDotIdx + 1);
  315. let secondDotIdx = root.indexOf(',');
  316. g = parseFloat(root.substring(0, secondDotIdx));
  317. root = root.substring(secondDotIdx + 1);
  318. if (hasAlpha) {
  319. let thirdDotIdx = root.indexOf(',');
  320. b = parseFloat(root.substring(0, thirdDotIdx));
  321. alpha = parseFloat(root.substring(thirdDotIdx + 1));
  322. } else {
  323. b = parseFloat(root);
  324. }
  325. }
  326. let rgbc = [r, g, b];
  327. // 减淡或加深
  328. for (var i = 0; i < 3; i++)
  329. type === 'light' ? (rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i])) : (rgbc[i] = Math.floor(rgbc[i] * (1 - level)));
  330. if (hasAlpha) {
  331. return `rgba(${rgbc[0]}, ${rgbc[1]}, ${rgbc[2]}, ${alpha})`;
  332. } else {
  333. return `rgb(${rgbc[0]}, ${rgbc[1]}, ${rgbc[2]})`;
  334. }
  335. },
  336. sortFieldMatch(field) {
  337. const stringArray = field.split('');
  338. let newField = field;
  339. stringArray.forEach((t) => {
  340. if (/[A-Z]/.test(t)) {
  341. newField = field.replace(t, `-${t.toLowerCase()}`);
  342. }
  343. });
  344. return newField;
  345. }
  346. }
  347. };
  348. </script>
  349. <style>
  350. @import 'iconfont.css';
  351. .wyb-pagination-box {
  352. width: 100%;
  353. display: flex;
  354. flex-direction: row;
  355. align-items: center;
  356. box-sizing: border-box;
  357. justify-content: space-between;
  358. flex-wrap: nowrap;
  359. }
  360. .wyb-pagination-left {
  361. flex: 1;
  362. display: flex;
  363. flex-direction: row;
  364. align-items: center;
  365. flex-wrap: nowrap;
  366. justify-content: flex-start;
  367. }
  368. .wyb-pagination-right {
  369. flex: 1;
  370. display: flex;
  371. flex-direction: row;
  372. align-items: center;
  373. flex-wrap: nowrap;
  374. justify-content: flex-end;
  375. }
  376. .wyb-pagination-first-page-t,
  377. .wyb-pagination-prev-page-t,
  378. .wyb-pagination-next-page-t,
  379. .wyb-pagination-last-page-t {
  380. font-size: 27rpx;
  381. padding: 14rpx 25rpx;
  382. box-sizing: border-box;
  383. background-color: #f8f8f8;
  384. border: 1px solid #e5e5e5;
  385. white-space: nowrap;
  386. }
  387. .wyb-pagination-first-page-i,
  388. .wyb-pagination-prev-page-i,
  389. .wyb-pagination-next-page-i,
  390. .wyb-pagination-last-page-i {
  391. font-size: 27rpx;
  392. padding: 14rpx 33rpx;
  393. box-sizing: border-box;
  394. background-color: #f8f8f8;
  395. border: 1px solid #e5e5e5;
  396. white-space: nowrap;
  397. }
  398. .wyb-pagination-first-page-t,
  399. .wyb-pagination-first-page-i {
  400. margin-right: 15rpx;
  401. }
  402. .wyb-pagination-last-page-t,
  403. .wyb-pagination-last-page-i {
  404. margin-left: 15rpx;
  405. }
  406. .wyb-pagination-info {
  407. font-size: 33rpx;
  408. white-space: nowrap;
  409. display: flex;
  410. flex-direction: row;
  411. align-items: center;
  412. justify-content: center;
  413. flex: 1;
  414. }
  415. .wyb-pagination-input input {
  416. text-align: center;
  417. }
  418. .wyb-pagination-span {
  419. margin: 0 2rpx;
  420. }
  421. .wyb-pagination-info-total {
  422. margin-left: 10rpx;
  423. }
  424. .wyb-pagination-first-page-t:active,
  425. .wyb-pagination-prev-page-t:active,
  426. .wyb-pagination-next-page-t:active,
  427. .wyb-pagination-last-page-t:active,
  428. .wyb-pagination-first-page-i:active,
  429. .wyb-pagination-prev-page-i:active,
  430. .wyb-pagination-next-page-i:active,
  431. .wyb-pagination-last-page-i:active {
  432. background-color: var(--hover) !important;
  433. }
  434. .left-arrow {
  435. transform: scale(0.9);
  436. margin-right: 5rpx;
  437. }
  438. .right-arrow {
  439. margin-left: 5rpx;
  440. transform: scale(0.9) rotate(180deg);
  441. -webkit-transform: scale(0.8) rotate(180deg);
  442. }
  443. </style>