wyb-pagination.vue 11 KB

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