From 7ad48ddf4ca20d0f1f894cbfe4946b2a8d7041ab Mon Sep 17 00:00:00 2001
From: lin <1005713861@163.com>
Date: Fri, 17 Apr 2026 15:05:01 +0800
Subject: [PATCH] feat: 星享购提交
---
static/img/quanshou/BG1.png | 0
static/img/quanshou/LX3.png | 0
static/css/collect.css | 2
static/imgs/ad5.png | 0
uni_modules/uv-drop-down/changelog.md | 13
static/imgs/index-top.png | 0
static/img/quanshou/C15.png | 0
uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js | 29
uni_modules/uv-avatar/components/uv-avatar-group/props.js | 53
uni_modules/uv-collapse/components/uv-collapse-item/props.js | 60
pages/ginfo/ginfo.vue | 3
uni_modules/uni-popup/changelog.md | 32
uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue | 52
pages/profile/profile.vue | 5
uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue | 222
uni_modules/uv-skeleton/components/uv-skeleton/props.js | 65
main.js | 14
uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue | 265
uni_modules/uv-keyboard/components/uv-keyboard-car/uv-keyboard-car.vue | 347
uni_modules/uv-back-top/readme.md | 11
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js | 96
uni_modules/uv-text/components/uv-text/props.js | 113
uni_modules/uv-upload/changelog.md | 17
uni_modules/uv-slider/readme.md | 15
uni_modules/uv-back-top/components/uv-back-top/uv-back-top.vue | 116
uni_modules/uv-toolbar/components/uv-toolbar/props.js | 2
static/imgs/right.png | 0
uni_modules/uv-scroll-list/components/uv-scroll-list/props.js | 35
uni_modules/uv-album/readme.md | 21
static/img/qsLeft.png | 0
static/img/quanshou/H5.png | 0
static/img/quanshou/DC3.png | 0
uni_modules/uv-notice-bar/readme.md | 19
uni_modules/uv-index-list/components/uv-index-anchor/props.js | 30
uni_modules/uv-drop-down/readme.md | 23
uni_modules/uv-tabbar/components/uv-tabbar-item/props.js | 40
uni_modules/uv-steps/changelog.md | 10
uni_modules/uv-toast/readme.md | 11
uni_modules/uv-read-more/components/uv-read-more/props.js | 62
static/img/quanshou/VIPLVL2.png | 0
static/imgs/xxhf.png | 0
static/img/smsBg.png | 0
uni_modules/uv-tooltip/readme.md | 11
static/img/quanshou/3.png | 0
uni_modules/uv-back-top/package.json | 89
uni_modules/uv-qrcode/package.json | 87
static/imgs/mf1.png | 0
uni_modules/uv-collapse/components/uv-collapse/props.js | 20
uni_modules/uv-keyboard/components/uv-keyboard-car/props.js | 24
uni_modules/uv-radio/components/uv-radio/uv-radio.vue | 348
uni_modules/uv-empty/changelog.md | 13
static/img/quanshou/3A.png | 0
uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue | 263
uni_modules/uv-navbar/readme.md | 19
uni_modules/uv-scroll-list/readme.md | 19
pages/carts/carts.vue | 2
uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue | 347
uni_modules/uv-switch/changelog.md | 14
static/css/ginfo.css | 4
uni_modules/uv-avatar/package.json | 89
static/img/quanshou/10.png | 0
uni_modules/uv-modal/package.json | 90
uni_modules/uv-pick-color/components/uv-pick-color/colors.js | 217
uni_modules/uv-code/package.json | 87
uni_modules/uv-line-progress/components/uv-line-progress/uv-line-progress.vue | 146
static/img/quanshou/VIPBOX3.png | 0
uni_modules/uv-line-progress/changelog.md | 7
pages/vip/index.vue | 403
static/img/quanshou/VIPTXT2.png | 0
uni_modules/uv-waterfall/readme.md | 19
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js | 11
uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js | 15
uni_modules/uv-picker/components/uv-picker/props.js | 2
static/imgs/tb3.png | 0
uni_modules/uv-calendar/components/uv-calendar/header.vue | 104
static/img/quanshou/DC2.png | 0
uni_modules/uv-upload/components/uv-upload/uv-upload.vue | 488
uni_modules/uv-badge/changelog.md | 7
uni_modules/uv-radio/package.json | 88
uni_modules/uv-swiper/components/uv-swiper-indicator/props.js | 30
pages/quanyi/components/agreement/index.vue | 16
static/img/quanshou/LX2.png | 0
uni_modules/uv-list/components/uv-list-item/uv-list-item.vue | 535
uni_modules/uv-parse/components/uv-parse/uv-parse.vue | 498
pages/quanshou/components/xianlianghaojjuab.vue | 14
uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue | 110
uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs | 51
uni_modules/uv-no-network/readme.md | 11
uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js | 40
uni_modules/uv-collapse/components/uv-collapse/uv-collapse.vue | 86
uni_modules/uv-list/package.json | 87
uni_modules/uv-read-more/readme.md | 11
pages/order/order.vue | 6
uni_modules/uv-skeleton/changelog.md | 9
uni_modules/uv-subsection/components/uv-subsection/props.js | 54
uni_modules/uv-count-down/components/uv-count-down/uv-count-down.vue | 169
uni_modules/uv-scroll-list/changelog.md | 12
uni_modules/uv-tags/readme.md | 11
pages/quanyi/components/code/set.vue | 323
uni_modules/uv-calendars/components/uv-calendars/calendar.js | 546
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js | 18
static/img/quanshou/H6.png | 0
uni_modules/uv-badge/package.json | 87
static/img/quanshou/ZQ3.png | 0
uni_modules/uv-calendars/components/uv-calendars/calendar-item.vue | 248
uni_modules/uv-skeleton/package.json | 87
pages/web/index.vue | 46
static/img/quanshou/TBBG3.png | 0
uni_modules/uv-vtabs/components/uv-vtabs/props.js | 79
uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hans.json | 12
static/img/quanshou/C7.png | 0
static/imgs/mf2.png | 0
uni_modules/uv-search/components/uv-search/props.js | 127
uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js | 19
uni_modules/uv-count-to/components/uv-count-to/uv-count-to.vue | 187
static/img/quanshou/4.png | 0
static/img/quanshou/VIPTXT3.png | 0
uni_modules/uv-scroll-list/package.json | 87
uni_modules/uni-popup/package.json | 190
uni_modules/uv-badge/components/uv-badge/uv-badge.vue | 176
static/img/quanshou/C8.png | 0
static/img/yifa.png | 0
uni_modules/uv-link/package.json | 87
uni_modules/uv-calendars/components/uv-calendars/calendar-body.vue | 376
uni_modules/uv-ui/package.json | 162
uni_modules/uv-load-more/package.json | 89
static/img/quanshou/Y1.png | 0
uni_modules/uv-calendar/components/uv-calendar/month.vue | 616
pages/quanshou/components/lanyaerji.vue | 45
uni_modules/uv-pick-color/readme.md | 19
static/img/quanshou/VIPLVL1.png | 0
uni_modules/uv-link/changelog.md | 7
static/img/quanshou/CY5.png | 0
uni_modules/uv-search/changelog.md | 25
uni_modules/uv-list/changelog.md | 25
uni_modules/uv-rate/readme.md | 19
uni_modules/uni-transition/components/uni-transition/uni-transition.vue | 523
uni_modules/uv-grid/components/uv-grid/props.js | 20
uni_modules/uv-notice-bar/components/uv-column-notice/props.js | 61
uni_modules/uv-text/changelog.md | 11
static/img/A0.png | 0
static/imgs/tb2.png | 0
static/img/back.png | 0
uni_modules/uv-read-more/package.json | 89
uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue | 4
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js | 23
uni_modules/uv-parse/components/uv-parse/node/node.vue | 576
uni_modules/uv-text/package.json | 89
pages/web/ceshixz.vue | 41
static/img/quanshou/9.png | 0
uni_modules/uv-calendar/package.json | 89
uni_modules/uv-swiper/changelog.md | 16
uni_modules/uv-index-list/components/uv-index-list/props.js | 30
uni_modules/uv-drop-down/components/uv-drop-down-item/uv-drop-down-item.vue | 169
uni_modules/uv-divider/changelog.md | 11
pages/quanyi/components/addAddress/index.vue | 14
static/css/carts.css | 3
static/imgs/c1.png | 0
uni_modules/uv-count-to/changelog.md | 13
static/img/quanshou/TBBG2.png | 0
uni_modules/uv-text/components/uv-text/value.js | 87
uni_modules/uv-grid/components/uv-grid-item/props.js | 15
static/img/quanshou/TY1.png | 0
uni_modules/uv-keyboard/components/uv-keyboard/uv-keyboard.vue | 180
uni_modules/uv-drop-down/package.json | 91
uni_modules/uv-sticky/components/uv-sticky/props.js | 41
static/img/left.png | 0
uni_modules/uv-divider/components/uv-divider/uv-divider.vue | 113
static/img/quanshou/CY1.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js | 24
uni_modules/uv-code-input/components/uv-code-input/props.js | 83
uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue | 269
uni_modules/uv-swipe-action/readme.md | 11
uni_modules/uv-collapse/readme.md | 11
uni_modules/uv-radio/readme.md | 19
static/img/quanshou/LX1.png | 0
uni_modules/uv-load-more/changelog.md | 7
uni_modules/uv-index-list/changelog.md | 18
static/imgs/ad3.png | 0
static/imgs/nv5.png | 0
uni_modules/uv-upload/readme.md | 11
uni_modules/uv-avatar/changelog.md | 13
uni_modules/uv-swipe-action/changelog.md | 11
uni_modules/uv-line-progress/readme.md | 11
uni_modules/uv-calendars/changelog.md | 40
static/imgs/nv2.png | 0
uni_modules/uv-calendar/components/uv-calendar/props.js | 145
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js | 1191 +
static/img/quanshou/C9.png | 0
uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue | 250
uni_modules/uv-link/components/uv-link/uv-link.vue | 83
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue | 94
static/img/quanshou/CY4.png | 0
uni_modules/uv-count-down/components/uv-count-down/utils.js | 62
static/imgs/tb1.png | 0
uni_modules/uv-tooltip/components/uv-tooltip/props.js | 60
uni_modules/uv-navbar/package.json | 89
uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue | 106
uni_modules/uv-list/components/uv-list/uv-list.vue | 147
pages/web/authorization.vue | 106
uni_modules/uv-tabbar/components/uv-tabbar/props.js | 50
uni_modules/uv-line-progress/package.json | 87
uni_modules/uv-number-box/components/uv-number-box/props.js | 113
static/img/quanshou/1.png | 0
uni_modules/uv-divider/components/uv-divider/props.js | 45
pages/quanshou/components/tehuichongzhi.vue | 263
static/img/quanshou/C2.png | 0
pages/quanyi/index.vue | 349
static/img/qsLifeIcon.png | 0
uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue | 162
uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue | 1038 +
uni_modules/uv-count-down/components/uv-count-down/props.js | 25
uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue | 200
uni_modules/uv-badge/readme.md | 11
static/img/quanshou/LS1.png | 0
uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hant.json | 12
uni_modules/uv-qrcode/changelog.md | 13
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue | 90
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js | 666
uni_modules/uv-list/readme.md | 27
uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue | 176
uni_modules/uv-upload/components/uv-upload/utils.js | 151
uni_modules/uv-vtabs/readme.md | 19
uni_modules/uv-alert/package.json | 88
uni_modules/uv-avatar/components/uv-avatar/props.js | 80
uni_modules/uv-notify/changelog.md | 9
pages/web/alipay.vue | 42
uni_modules/uv-drop-down/components/uv-drop-down/uv-drop-down.vue | 135
uni_modules/uv-drop-down/components/uv-drop-down-popup/uv-drop-down-popup.vue | 242
static/img/quanshou/C1.png | 0
uni_modules/uv-count-down/changelog.md | 9
uni_modules/uv-keyboard/components/uv-keyboard/props.js | 95
uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue | 223
uni_modules/uv-grid/changelog.md | 21
static/imgs/nv4.png | 0
uni_modules/uv-calendars/components/uv-calendars/i18n/en.json | 12
static/imgs/ad4.png | 0
uni_modules/uni-transition/changelog.md | 11
uni_modules/uv-grid/components/uv-grid/uv-grid.vue | 100
uni_modules/uv-swiper/readme.md | 19
uni_modules/uv-tabbar/package.json | 90
static/img/quanshou/CY2.png | 0
uni_modules/uv-notify/components/uv-notify/uv-notify.vue | 220
uni_modules/uv-load-more/components/uv-load-more/props.js | 95
uni_modules/uv-no-network/package.json | 90
static/img/quanshou/CY3.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js | 21
uni_modules/uv-swiper/components/uv-swiper/props.js | 136
static/imgs/nv3.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js | 21
uni_modules/uv-album/changelog.md | 10
static/img/qyBg2.png | 0
pages/my/my.vue | 293
uni_modules/uv-code-input/changelog.md | 13
uni_modules/uv-code-input/components/uv-code-input/uv-code-input.vue | 272
uni_modules/uv-waterfall/components/uv-waterfall/props.js | 69
uni_modules/uv-collapse/components/uv-collapse-item/uv-collapse-item.vue | 229
static/img/quanshou/TY99.png | 0
uni_modules/uv-image/readme.md | 15
uni_modules/uv-toast/changelog.md | 10
static/img/quanshou/2.png | 0
uni_modules/uv-code/readme.md | 11
uni_modules/uv-skeletons/changelog.md | 4
uni_modules/uv-upload/components/uv-upload/props.js | 130
uni_modules/uv-modal/components/uv-modal/uv-modal.vue | 245
uni_modules/uv-slider/package.json | 87
uni_modules/uv-slider/components/uv-slider/uv-slider.vue | 58
static/img/quanshou/VIPTXT1.png | 0
uni_modules/uv-tags/changelog.md | 10
static/img/qsBg.png | 0
static/img/quanshou/C10.png | 0
uni_modules/uv-notify/readme.md | 11
uni_modules/uv-parse/package.json | 87
uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue | 341
uni_modules/uv-row/package.json | 87
uni_modules/uv-tabs/components/uv-tabs/props.js | 71
uni_modules/uv-gap/changelog.md | 2
uni_modules/uv-tags/components/uv-tags/props.js | 95
uni_modules/uv-datetime-picker/components/uv-datetime-picker/uv-datetime-picker.vue | 2
uni_modules/uv-navbar/changelog.md | 17
static/img/quanshou/C4.png | 0
static/imgs/mf5.png | 0
static/css/search.css | 4
uni_modules/uv-calendar/components/uv-calendar/calendar.js | 546
uni_modules/uv-sticky/package.json | 87
uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue | 152
static/img/GG2.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js | 142
static/img/qsBg2.png | 0
static/img/quanshou/7.png | 0
uni_modules/uv-divider/package.json | 88
uni_modules/uv-no-network/changelog.md | 5
uni_modules/uv-collapse/changelog.md | 5
uni_modules/uv-toast/components/uv-toast/uv-toast.vue | 333
static/imgs/more.png | 0
uni_modules/uv-navbar/components/uv-navbar/props.js | 89
uni_modules/uv-parse/static/app-plus/uv-parse/local.html | 1
uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs | 256
static/img/quanshou/Y4.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js | 21
uni_modules/uv-link/components/uv-link/props.js | 40
static/img/quanshou/C11.png | 0
static/img/off.png | 0
static/img/quanshou/TY3.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js | 17
uni_modules/uv-tabs/package.json | 90
uni_modules/uv-qrcode/components/uv-qrcode/props.js | 85
uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js | 264
uni_modules/uv-index-list/package.json | 88
uni_modules/uv-subsection/package.json | 87
uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js | 224
uni_modules/uv-empty/readme.md | 19
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js | 21
uni_modules/uv-alert/components/uv-alert/props.js | 45
static/img/quanshou/TBBG1.png | 0
uni_modules/uv-vtabs/changelog.md | 20
uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue | 438
uni_modules/uv-tooltip/package.json | 90
static/imgs/input-bg.png | 0
uni_modules/uv-radio/components/uv-radio/props.js | 65
uni_modules/uv-grid/components/uv-grid-item/uv-grid-item.vue | 226
pages/index/index.vue | 679
uni_modules/uv-waterfall/package.json | 89
uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue | 115
uni_modules/uv-qrcode/components/uv-qrcode/cache.js | 1
uni_modules/uv-alert/components/uv-alert/uv-alert.vue | 246
uni_modules/uv-notice-bar/package.json | 88
uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue | 146
uni_modules/uv-count-to/package.json | 87
static/img/qsLife.png | 0
static/img/quanshou/ZQ1.png | 0
uni_modules/uv-alert/changelog.md | 7
pages/pays/pays.vue | 14
uni_modules/uv-swipe-action/package.json | 88
uni_modules/uv-notice-bar/changelog.md | 19
static/img/quanshou/H1.png | 0
uni_modules/uv-calendar/readme.md | 21
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js | 39
static/img/quanshou/LX6.png | 0
static/img/kk.png | 0
uni_modules/uv-keyboard/changelog.md | 17
uni_modules/uv-vtabs/package.json | 87
uni_modules/uv-ui/changelog.md | 353
static/img/quanshou/C3.png | 0
uni_modules/uv-read-more/changelog.md | 7
static/img/quanshou/QL.png | 0
uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue | 390
uni_modules/uv-parse/components/uv-parse/parser.js | 1335 +
uni_modules/uv-row/components/uv-row/uv-row.vue | 95
uni_modules/uv-search/package.json | 89
uni_modules/uv-image/components/uv-image/props.js | 95
static/img/quanshou/8.png | 0
uni_modules/uv-calendars/components/uv-calendars/uv-calendars.vue | 452
uni_modules/uv-image/changelog.md | 36
static/imgs/mf6.png | 0
uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js | 2
uni_modules/uv-tabs/changelog.md | 22
uni_modules/uv-code/components/uv-code/uv-code.vue | 136
uni_modules/uv-sticky/readme.md | 11
uni_modules/uv-subsection/changelog.md | 10
uni_modules/uv-code-input/readme.md | 19
uni_modules/uv-image/package.json | 89
pages/quanshou/components/list.vue | 46
uni_modules/uv-calendar/components/uv-calendar/uv-calendar.vue | 390
pages/quanyi/components/code/index.vue | 323
uni_modules/uv-index-list/components/uv-index-item/uv-index-item.vue | 87
static/imgs/ad2.png | 0
static/img/GG1.png | 0
uni_modules/uv-keyboard/package.json | 89
uni_modules/uv-load-more/readme.md | 11
static/img/quanshou/Star.png | 0
uni_modules/uv-subsection/readme.md | 11
static/img/quanshou/8A.png | 0
uni_modules/uv-rate/changelog.md | 17
static/imgs/notice.png | 0
uni_modules/uv-badge/components/uv-badge/props.js | 73
uni_modules/uv-index-list/readme.md | 19
uni_modules/uv-switch/components/uv-switch/uv-switch.vue | 192
pages/my/my_bak.vue | 246
uni_modules/uv-toolbar/readme.md | 2
static/img/qyBg.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/queue.js | 41
uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue | 112
uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue | 175
uni_modules/uv-pick-color/changelog.md | 18
static/img/quanshou/C12.png | 0
uni_modules/uv-swiper/package.json | 88
static/img/quanshou/TY2.png | 0
uni_modules/uv-steps/components/uv-steps-item/props.js | 25
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js | 22
uni_modules/uv-rate/package.json | 88
static/img/MKL.png | 0
uni_modules/uv-swipe-action/components/uv-swipe-action/props.js | 10
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue | 11
static/imgs/ad1.png | 0
uni_modules/uv-number-box/package.json | 88
uni_modules/uv-row/readme.md | 11
uni_modules/uv-tags/components/uv-tags/uv-tags.vue | 379
uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue | 65
static/img/quanshou/H2.png | 0
uni_modules/uv-line-progress/components/uv-line-progress/props.js | 29
static/imgs/c2.png | 0
uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js | 182
static/css/index.css | 961
uni_modules/uv-number-box/changelog.md | 7
uni_modules/uv-row/components/uv-col/props.js | 30
uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js | 34
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js | 241
uni_modules/uv-radio/changelog.md | 31
manifest.json | 32
uni_modules/uv-parse/readme.md | 21
uni_modules/uv-keyboard/components/uv-keyboard-number/uv-keyboard-number.vue | 201
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js | 22
static/imgs/nv1.png | 0
uni_modules/uv-index-list/components/uv-index-anchor/uv-index-anchor.vue | 98
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js | 8
pages/quanshou/components/heijing.vue | 28
uni_modules/uv-text/readme.md | 17
uni_modules/uv-row/components/uv-row/props.js | 20
uni_modules/uv-empty/components/uv-empty/uv-empty.vue | 126
uni_modules/uv-empty/package.json | 88
uni_modules/uv-slider/components/uv-slider/props.js | 58
static/img/quanshou/L6.png | 0
static/img/quanshou/H3.png | 0
uni_modules/uv-tabbar/readme.md | 21
uni_modules/uv-keyboard/readme.md | 19
static/img/quanshou/DC1.png | 0
static/img/quanshou/VIPBOX1.png | 0
uni_modules/uv-code/changelog.md | 9
uni_modules/uv-count-down/readme.md | 11
uni_modules/uv-number-box/readme.md | 11
static/img/quanshou/C13.png | 0
uni_modules/uv-pick-color/package.json | 89
static/img/quanshou/ZQ2.png | 0
static/img/quanshou/C6.png | 0
static/imgs/index-logo.png | 0
uni_modules/uv-sticky/changelog.md | 11
static/imgs/deb.png | 0
static/imgs/mf3.png | 0
static/imgs/logo-icon.png | 0
static/imgs/xxhh.png | 0
uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue | 247
uni_modules/uv-modal/changelog.md | 28
static/img/quanshou/5.png | 0
uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue | 146
uni_modules/uv-calendars/readme.md | 23
uni_modules/uv-image/components/uv-image/uv-image.vue | 287
uni_modules/uv-button/components/uv-button/uv-button.vue | 2
uni_modules/uv-switch/components/uv-switch/props.js | 58
static/imgs/my-bg.png | 0
static/img/quanshou/Y2.png | 0
uni_modules/uni-popup/components/uni-popup/uni-popup.vue | 81
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js | 3
uni_modules/uv-grid/readme.md | 17
static/img/quanshou/6A.png | 0
static/img/quanshou/TY5.png | 0
uni_modules/uv-index-list/components/uv-index-list/uv-index-list.vue | 461
uni_modules/uv-no-network/components/uv-no-network/props.js | 20
static/img/quanshou/12.png | 0
uni_modules/uv-modal/components/uv-modal/props.js | 90
uni_modules/uv-waterfall/changelog.md | 24
uni_modules/uv-rate/components/uv-rate/props.js | 73
uni_modules/uv-calendars/components/uv-calendars/util.js | 435
uni_modules/uv-upload/components/uv-upload/mixin.js | 22
uni_modules/uv-code/components/uv-code/props.js | 35
uni_modules/uv-keyboard/components/uv-keyboard-number/props.js | 19
uni_modules/uv-ui/readme.md | 164
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js | 11
uni_modules/uv-switch/package.json | 88
uni_modules/uv-ui/components/uv-ui/uv-ui.vue | 7
uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue | 68
uni_modules/uv-steps/package.json | 89
uni_modules/uv-search/components/uv-search/uv-search.vue | 311
uni_modules/uv-row/components/uv-col/uv-col.vue | 165
uni_modules/uv-album/package.json | 88
uni_modules/uv-code-input/package.json | 87
static/img/quanshou/H4.png | 0
uni_modules/uni-transition/package.json | 105
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js | 298
uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue | 372
static/imgs/tb4.png | 0
uni_modules/uv-back-top/changelog.md | 8
uni_modules/uv-calendars/package.json | 89
uni_modules/uv-count-to/components/uv-count-to/props.js | 60
uni_modules/uv-count-to/readme.md | 11
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js | 22
uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue | 551
uni_modules/uv-pick-color/components/uv-pick-color/props.js | 47
uni_modules/uv-tooltip/changelog.md | 13
uni_modules/uv-qrcode/readme.md | 21
static/img/quanshou/LX4.png | 0
uni_modules/uv-grid/package.json | 88
util.js | 77
pages/web/xieyi.vue | 5
uni_modules/uv-modal/readme.md | 23
uni_modules/uv-notice-bar/components/uv-notice-bar/props.js | 76
static/img/quanshou/C5.png | 0
uni_modules/uv-steps/components/uv-steps/props.js | 40
pages/quanyi/add.vue | 1094
static/css/my2.css | 171
uni_modules/uv-calendars/components/uv-calendars/i18n/index.js | 8
static/img/quanshou/Y3.png | 0
pages.json | 29
static/img/nion1.png | 0
uni_modules/uv-link/readme.md | 11
static/imgs/mf4.png | 0
static/img/qsRight.png | 0
uni_modules/uni-transition/components/uni-transition/createAnimation.js | 5
static/img/quanshou/6.png | 0
uni_modules/uv-switch/readme.md | 19
uni_modules/uv-notice-bar/components/uv-row-notice/props.js | 40
uni_modules/uv-rate/components/uv-rate/uv-rate.vue | 274
uni_modules/uv-back-top/components/uv-back-top/props.js | 58
static/img/quanshou/C14.png | 0
uni_modules/uv-collapse/package.json | 89
uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue | 218
uni_modules/uv-search/readme.md | 19
uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue | 245
uni_modules/uv-tabs/readme.md | 19
uni_modules/uv-tabbar/changelog.md | 14
uni_modules/uv-alert/readme.md | 15
uni_modules/uv-empty/components/uv-empty/props.js | 60
uni_modules/uv-steps/components/uv-steps/uv-steps.vue | 83
uni_modules/uv-steps/readme.md | 11
uni_modules/uv-parse/changelog.md | 13
App.vue | 14
uni_modules/uv-avatar/readme.md | 11
uni_modules/uv-calendar/changelog.md | 16
uni_modules/uv-notify/components/uv-notify/props.js | 45
uni_modules/uv-skeleton/readme.md | 11
uni_modules/uv-toast/package.json | 90
uni_modules/uv-radio/components/uv-radio-group/props.js | 89
static/img/quanshou/VIPLVL3.png | 0
uni_modules/uv-tags/package.json | 89
uni_modules/uv-upload/package.json | 90
uni_modules/uv-divider/readme.md | 11
uni_modules/uv-skeletons/package.json | 88
static/img/BNM.png | 0
static/img/nion.png | 0
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js | 74
pages/login/login.vue | 223
static/img/quanshou/LX5.png | 0
uni_modules/uv-skeletons/readme.md | 23
uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue | 395
static/img/GG3.png | 0
static/img/quanshou/VIPBOX2.png | 0
uni_modules/uv-slider/changelog.md | 9
androidPrivacy.json | 6
pages/quanshou/index.vue | 315
uni_modules/uv-picker/components/uv-picker/uv-picker.vue | 2
uni_modules/uv-row/changelog.md | 5
uni_modules/uv-album/components/uv-album/uv-album.vue | 312
static/img/quanshou/TY4.png | 0
uni_modules/uv-count-down/package.json | 87
uni_modules/uv-notify/package.json | 90
uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs | 225
uni_modules/uv-text/components/uv-text/uv-text.vue | 218
static/imgs/quanshou.png | 0
static/img/qsPhysical.png | 0
static/img/quanshou/11.png | 0
560 files changed, 43,824 insertions(+), 2,603 deletions(-)
diff --git a/App.vue b/App.vue
index df3e9ab..09f34d6 100644
--- a/App.vue
+++ b/App.vue
@@ -6,8 +6,8 @@
if(args){
// 处理args参数,如直达到某新页面等
console.log('args', args)
- if(args.indexOf('lzzzsc-app') >= 0) {
- args = args.replace("lzzzsc-app://", "/")
+ if(args.indexOf('pig-app') >= 0) {
+ args = args.replace("pig-app://", "/")
return uni.redirectTo({
url: args
})
@@ -21,7 +21,7 @@
console.log('getConfig', res.data)
if(res.code == 1){
try {
- const token = uni.getStorageSync('lzsc-token');
+ const token = uni.getStorageSync('pig-token');
if (!token) {
/*
uni.redirectTo({
@@ -82,6 +82,14 @@
</script>
<style>
+ uni-view {
+ box-sizing: border-box;
+ }
+ checkbox .uni-checkbox-input {
+ border-radius: 50%;
+ width: 38rpx;
+ height: 38rpx
+}
/*每个页面公共css */
.startBar {
width: 100%;
diff --git a/androidPrivacy.json b/androidPrivacy.json
index 95a0812..70f49c9 100644
--- a/androidPrivacy.json
+++ b/androidPrivacy.json
@@ -1,15 +1,15 @@
{
"version" : "1",
"prompt" : "template",
- "title" : "齐享购商城注册协议和隐私政策",
- "message" : " 请你务必审慎阅读、充分理解“注册协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/> 你可阅读<a href=\"https://www.ncmiruo.com/index/?gp=1 \">《注册协议》</a>和<a href=\"https://www.ncmiruo.com/?gp=2 \">《隐私政策》</a>了解详细信息。并且我们会申请读取您的设备信息,以向您提供安全风控服务。同时您应特别注意前述协议中免除或者限制我们责任的条款、对您权利进行限制的条款。如您已详细阅读并同意金喜易购服务协议和隐私政策,请点击【同意并继续】开始使用我们的服务。",
+ "title" : "星享购城注册协议和隐私政策",
+ "message" : " 请你务必审慎阅读、充分理解“注册协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/> 你可阅读<a href=\"https://xxg1996.xjjyxh.com/?gp=1 \">《注册协议》</a>和<a href=\"https://xxg1996.xjjyxh.com/?gp=2 \">《隐私政策》</a>了解详细信息。并且我们会申请读取您的设备信息,以向您提供安全风控服务。同时您应特别注意前述协议中免除或者限制我们责任的条款、对您权利进行限制的条款。如您已详细阅读并同意星享购服务协议和隐私政策,请点击【同意并继续】开始使用我们的服务。",
"buttonAccept" : "同意并继续",
"buttonRefuse" : "暂不同意",
"hrefLoader" : "system",
"backToExit" : "false",
"second" : {
"title" : "确认提示",
- "message" : " 进入应用前,你需先同意<a href=\"https://www.ncmiruo.com/?gp=1\">《注册协议》</a>和<a href=\"https://www.ncmiruo.com/?gp=2\">《隐私政策》</a>,否则将退出应用。",
+ "message" : " 进入应用前,你需先同意<a href=\"https://xxg1996.xjjyxh.com/?gp=1\">《注册协议》</a>和<a href=\"https://xxg1996.xjjyxh.com/?gp=2\">《隐私政策》</a>,否则将退出应用。",
"buttonAccept" : "同意并继续",
"buttonRefuse" : "退出应用"
},
diff --git a/main.js b/main.js
index 404f9a3..add9c89 100644
--- a/main.js
+++ b/main.js
@@ -4,17 +4,19 @@
import Vue from 'vue'
import './uni.promisify.adaptor'
import util from './util.js'
+
Vue.config.productionTip = false
App.mpType = 'app'
+
Vue.prototype.$webHost = 'https://rbb98765.zrgy-bbg.com'
Vue.prototype.$util = util
-Vue.prototype.$baseUrl = 'https://tmy.xjzbh.com'
+Vue.prototype.$baseUrl = 'https://xxg1996.xjjyxh.com'
Vue.prototype.$bank = [
'中国工商银行',
'中国建设银行',
'中国银行',
'中国农业银行',
- '中国邮政储蓄银行',
+ '中国邮政储蓄银行',
'中国交通银行',
'浦东发展银行',
'中信银行',
@@ -34,7 +36,7 @@
'公司企业',
'政府机关',
'事业单位',
- '社会组织',
+ '社会组织',
'灵活就业',
]
Vue.prototype.$relation = [
@@ -42,7 +44,7 @@
'配偶',
'同事',
'好友',
- '同学',
+ '同学',
'其他',
]
Vue.prototype.$income = [
@@ -50,7 +52,7 @@
'5000-10000',
'10000-20000',
'20000-30000',
- '30000以上',
+ '30000以上',
]
// 全局跳转方法
Vue.prototype.$goWebUrl = function(url) {
@@ -75,4 +77,4 @@
app
}
}
-// #endif
+// #endif
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index 4cb53e3..c48ceb6 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,14 +1,14 @@
{
- "name" : "齐享购",
- "appid" : "__UNI__3333B1D",
+ "name" : "星享购",
+ "appid" : "__UNI__6FA73A5",
"description" : "",
- "versionName" : "1.4.0",
- "versionCode" : 140,
+ "versionName" : "1.3.5",
+ "versionCode" : 135,
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"statusbar" : {
- "immersed" : false
+ "immersed" : true
},
"compatible" : {
"ignoreVersion" : true
@@ -65,7 +65,7 @@
"<uses-permission android:name=\"android.permission.WRITE_SMS\"/>"
],
"minSdkVersion" : 29,
- "schemes" : "miruo-app",
+ "schemes" : "xxg-app",
"targetSdkVersion" : 30,
"abiFilters" : [ "arm64-v8a" ]
},
@@ -82,7 +82,7 @@
"com.apple.developer.associated-domains" : [ "applinks:www.ncmiruo.com" ]
}
},
- "urltypes" : "miruo-app"
+ "urltypes" : "xxg-app"
},
/* SDK配置 */
"sdkConfigs" : {
@@ -148,13 +148,27 @@
"description" : "核身服务",
"platforms" : "Android,iOS",
"url" : "https://ext.dcloud.net.cn/plugin?id=10378",
- "android_package_name" : "",
- "ios_bundle_id" : "com.qxg.app",
+ "android_package_name" : "com.jiangyu.xxg",
+ "ios_bundle_id" : "com.jiangyu.xxg",
"isCloud" : true,
"bought" : 1,
"pid" : "10378",
"parameters" : {}
}
+ },
+ "DC-WBNormal" : {
+ "__plugin_info__" : {
+ "name" : "DC-WBNormal",
+ "description" : "WB Normal SDK uni-app 插件",
+ "platforms" : "Android",
+ "url" : "https://ext.dcloud.net.cn/plugin?id=1491",
+ "android_package_name" : "com.jiangyu.xxg",
+ "ios_bundle_id" : "com.jiangyu.xxg",
+ "isCloud" : true,
+ "bought" : 1,
+ "pid" : "1491",
+ "parameters" : {}
+ }
}
}
},
diff --git a/pages.json b/pages.json
index 804a26d..7b32c95 100644
--- a/pages.json
+++ b/pages.json
@@ -4,13 +4,14 @@
"path": "pages/index/index",
"style": {
"navigationStyle":"custom",
- "navigationBarTitleText": "齐享购"
+ "navigationBarTitleText": "星享购"
}
},
{
"path" : "pages/cates/cates",
"style" :
{
+ "navigationStyle":"custom",
"navigationBarTitleText": "分类"
}
},
@@ -213,18 +214,20 @@
{
"path": "pages/quanyi/index",
"style": {
+ "navigationStyle":"custom",
"navigationBarTitleText": "会员权益"
}
},
{
"path": "pages/quanyi/add",
"style": {
- "navigationBarTitleText": "绑卡"
+ "navigationBarTitleText": "添加银行卡"
}
},
{
"path": "pages/quanshou/index",
"style": {
+ "navigationStyle":"custom",
"navigationBarTitleText": "会员权收"
}
},
@@ -238,6 +241,12 @@
"path": "pages/web/xieyi",
"style": {
"navigationBarTitleText": "协议"
+ }
+ },
+ {
+ "path": "pages/vip/index",
+ "style": {
+ "navigationBarTitleText": "会员"
}
}
],
@@ -254,11 +263,17 @@
"text": "首页"
},
{
- "pagePath": "pages/cates/cates",
- "iconPath": "static/imgs/tab-2.png",
- "selectedIconPath": "static/imgs/tab-s-2.png",
- "text": "分类"
+ "pagePath": "pages/vip/index",
+ "iconPath": "/static/imgs/tab-2.png",
+ "selectedIconPath": "/static/imgs/tab-s-2.png",
+ "text": "会员"
},
+ // {
+ // "pagePath": "pages/cates/cates",
+ // "iconPath": "/static/imgs/tb2.png",
+ // "selectedIconPath": "/static/imgs/tb2.png",
+ // "text": "分类"
+ // },
{
"pagePath": "pages/carts/carts",
"iconPath": "static/imgs/tab-3.png",
@@ -275,7 +290,7 @@
},
"globalStyle": {
"navigationBarTextStyle": "black",
- "navigationBarTitleText": "齐享购",
+ "navigationBarTitleText": "星享购",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
diff --git a/pages/carts/carts.vue b/pages/carts/carts.vue
index 4185416..5674ad3 100644
--- a/pages/carts/carts.vue
+++ b/pages/carts/carts.vue
@@ -47,7 +47,7 @@
<view class="noData" v-else>
<image src="/static/imgs/noData.png" mode="" style="margin-top: 200upx;"></image>
<view class="">没有购物车信息</view>
- <view class="noDataBtn" @tap="$util.goSwitch('/pages/cates/cates')">去看看</view>
+ <view class="noDataBtn" @tap="$util.goSwitch('/pages/index/index')">去看看</view>
</view>
</view>
<view class="cartsControll" v-if="cartsData.length > 0">
diff --git a/pages/ginfo/ginfo.vue b/pages/ginfo/ginfo.vue
index 60442c2..e8beb8c 100644
--- a/pages/ginfo/ginfo.vue
+++ b/pages/ginfo/ginfo.vue
@@ -14,7 +14,8 @@
<view class="">
<text>¥</text>
<text>{{goodsData.price}}</text>
- <text>积分:{{goodsData.points}}</text>
+ <!-- <text>积分:{{goodsData.points}}</text> -->
+ <!-- <text>原价:¥{{goodsData.price}}</text> -->
</view>
<view class="collect" @tap="collectGoods">
<image :src="goodsData.collect == 1 ? '/static/imgs/colls.png' : '/static/imgs/coll.png'" mode=""></image>
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 6270525..2205b53 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,99 +1,88 @@
<template>
- <view class="index">
- <view class="status_bar"></view>
- <view class="index-top">
- <view class="index-title">
- <view class="index-title-view index-title-view1">
- <view class="index-title-view-lf">
- <image src="@/static/imgs/index-logo.png" mode=""></image>
- </view>
- <view class="index-title-view-lf2">趋势好货放心购</view>
+ <view class="page-container index">
+ <image class="page-bg" src="@/static/imgs/index-top.png" mode="scaleToFill"></image>
+ <view class="content">
+ <!-- #ifdef APP-PLUS -->
+ <view class="uni-popup" :style="{top:isNativeHead?'':StatusBar}" v-if="contactsFlag">
+ <view class="ani uni-custom uni-popup__wrapper">
+ <view class="uni-popup__wrapper-box">
+ <view class="title">通讯权限</view>
+ <view class="content">便于实现好友推荐、联系人同步等功能</view>
+ <view class="btn-group">
+ <button class="cancel-btn" @click="handleReject">拒绝</button>
+ <button class="allow-btn" @click="handleAuthorize">允许</button>
+ </view>
+ </view>
+ </view>
+ </view>
+ <!-- #endif -->
+ <view class="hLogo">
+ <view class="hLogoView">
+ <view class="hLogo-1" style="display: flex;align-items: center;gap: 20rpx;">
+ <image class="h-logo" src="/static/imgs/index-logo.png" mode=""></image>
+ <view style="color: #fff;font-size: 24rpx;width: 184rpx;height: 48rpx;text-align: center;line-height: 48rpx;background: #FF822F;border-radius: 16rpx 8rpx 16rpx 4rpx;">品质严选放心购</view>
</view>
- <view class="index-title-view index-title-view2">
- <view class="index-title-view-rf">
- <view class="">
- <image src="@/static/imgs/qiandao.png" mode=""></image>
- <text>签到打卡</text>
- </view>
- </view>
- <view class="index-title-view-rf">
- <view class="">
- <image src="@/static/imgs/kefu.png" mode=""></image>
- <text>联系客服</text>
- </view>
- </view>
+ <view class="hLogo-2" @tap="$util.goPages('../search/search')">
+ <image class="s-icon" src="/static/imgs/search.png" mode=""></image>
+ <input type="text" placeholder="请搜索您想要的商品" />
+ <view class="s-btn">搜索</view>
</view>
</view>
- <view class="search">
- <view class="search-1">
- <image src="@/static/imgs/s2.png" mode=""></image>
- <input type="text" placeholder="请输入关键词" />
- </view>
- <view class="search-2">
- <view class="">搜索</view>
- </view>
+ </view>
+
+ <view class="cates" v-if="catesNavData">
+ <view class="cates-v" v-for="(item, index) in catesNavData" @tap="$util.goPages('/pages/catesList/catesList?catesId=' + item.id + '&title=' + item.title)">
+ <image class="cates-v-icon" :src="item.icon" mode=""></image>
+ <view class="">{{item.title}}</view>
</view>
- <view class="cates">
- <view class="cates-v" v-for="(item, index) in catesNavData" @tap="$util.goPages('/pages/catesList/catesList?catesId=' + item.id + '&title=' + item.title)">
- <image class="cates-v-icon" :src="item.icon" mode=""></image>
- <view class="">{{item.title}}</view>
- </view>
+ <!-- <view class="cates-v" @tap="$util.goSwitch('/pages/cates/cates')">
+ <image class="cates-v-icon" src="/static/imgs/more.png" mode=""></image>
+ <view class="">更多</view>
+ </view> -->
+ </view>
+
+ <view class="ad1" @tap="$util.goPages('/pages/catesList/catesList?catesId=' + '7' + '&title=' + '家居百货')">
+ <image src="/static/imgs/ad1.png" mode=""></image>
+ </view>
+
+ <!-- <view class="ad2" @tap="$util.goPages('/pages/catesList/catesList?catesId=' + '11' + '&title=' + '服装鞋包')">
+ <image src="/static/imgs/ad2.png" mode=""></image>
+ </view> -->
+
+ <view class="d3-wrap" style="margin: 40rpx 0;">
+ <view class="ad3" @tap="$util.goPages('/pages/catesList/catesList?catesId=2' + '&title=' + '家用电器')">
+ <image src="/static/imgs/ad3.png" mode=""></image>
+ </view>
+ <view class="ad4" @tap="$util.goPages('/pages/catesList/catesList?catesId=4' + '&title=' + '美妆个护')">
+ <image src="/static/imgs/ad4.png" mode=""></image>
</view>
</view>
- <view class="banner">
- <swiper class="swiper" circular :indicator-dots="indicatorDots" :indicator-color="indicatorColor" :autoplay="autoplay" :indicator-active-color="indicatorActiveColor" :interval="interval" :duration="duration">
- <swiper-item v-for="(item, index) in bannerData" @tap="$util.goPages('/pages/ginfo/ginfo?id=' + item.goodsid)">
- <image lazy-load :src="item.icon" mode=""></image>
- </swiper-item>
- </swiper>
+ <view class="ad5" @tap="$util.goPages('/pages/catesList/catesList?catesId=' + '3' + '&title=' + '手机数码')">
+ <image src="/static/imgs/ad5.png" mode=""></image>
</view>
- <view class="comm">
- <view class="comm-1">
- <view class="comm-title">
- <image src="/static/imgs/ptyx.png" mode=""></image>
- </view>
- <view class="comm-goods">
- <view class="comm-goods-v" v-for="(item, index) in prefGoods" @click="$util.goPages('/pages/ginfo/ginfo?id=' + item.id)">
- <view class="">
- <image :src="item.icon" mode=""></image>
- <text>¥{{item.price}}</text>
+
+ <!-- <view class="c-goods" v-if="hotCates" v-for="(item, index) in hotCates">
+ <view class="ca-ad" v-if="index % 2 == 0">
+ <image src="/static/imgs/c1.png" mode=""></image>
+ </view>
+ <view class="ca-ad" v-else>
+ <image src="/static/imgs/c2.png" mode=""></image>
+ </view>
+ <view class="cGoods">
+ <scroll-view class="scroll-view_H" scroll-x="true">
+ <view class="scroll-view-item_H" v-for="(itm, index) in item.goods" @tap="$util.goPages('/pages/ginfo/ginfo?id=' + itm.id)">
+ <image lazy-load class="goods-icon" :src="itm.icon" mode=""></image>
+ <view class="goods-title">{{itm.title}}</view>
+ <view class="goods-price">
+ <view>¥</view>
+ <view class="red">{{itm.vprice}}</view>
</view>
</view>
- </view>
+ </scroll-view>
</view>
- <view class="comm-2"></view>
- <view class="comm-3">
- <view class="comm-title">
- <image src="@/static/imgs/jfdh.png" mode=""></image>
- </view>
- <view class="comm-goods">
- <view class="comm-goods-v" v-for="(item, index) in pointGoods" @click="$util.goPages('/pages/ginfo/ginfo?id=' + item.id)">
- <view class="">
- <image :src="item.icon" mode=""></image>
- <text>∮{{item.points}}</text>
- </view>
- </view>
- </view>
- </view>
- </view>
- <view class="hots">
- <view class="hots-title" @tap="$util.goPages('/pages/catesList/catesList?is_hot=1&title=热门推荐')">
- <image class="hots-icon" src="@/static/imgs/rmtj.png" mode=""></image>
- <view class="hots-rgt">
- <text>大家都在买</text>
- <image src="@/static/imgs/right.png" mode=""></image>
- </view>
- </view>
- <view class="hots-goods">
- <view class="hots-goods-v" v-for="(item, index) in hotGoods" @click="$util.goPages('/pages/ginfo/ginfo?id=' + item.id)">
- <view class="">
- <image :src="item.icon" mode=""></image>
- <text>¥{{item.price}}</text>
- </view>
- </view>
- </view>
- </view>
+ </view> -->
+
<view class="goods-main">
<view class="goods-main-view">
<view class="goods-list">
@@ -103,202 +92,356 @@
<view class="goods-price">
<view>¥</view>
<view class="red">{{itm.price}}</view>
- <view class="hyj">
+ <!-- <view class="hyj">
<image src="@/static/imgs/hyj.png" mode=""></image>
- </view>
- <view>¥</view>
- <view class="red">{{itm.price}}</view>
+ </view> -->
+ <!-- <view>¥</view>
+ <view class="red">{{itm.price}}</view> -->
</view>
</view>
</view>
</view>
</view>
- <uni-popup ref="popup2" type="center" :is-mask-click="false">
- <view class="popup-content">
- <view class="edu-view edu-view2">
- <image src="/static/imgs/close.png" class="close-edu" mode="" @tap="$refs.popup2.close()"></image>
- <view class="edu-view-desc2">认证领取额度</view>
- <image class="edu-icon-succ" src="/static/imgs/auths.png" mode=""></image>
- <view class="edu-view-desc3">
- 请您先认证个人信息,然后立即领取额度
- </view>
- <view class="edu-btns" @tap="goLingqu">立即领取</view>
- </view>
- </view>
- </uni-popup>
- </view>
+ <view class="login-desc" v-if="!userInfo" @tap="$util.goPages('../login/login')">
+ <image class="login-icon" src="/static/imgs/logo-icon.png" mode=""></image>
+ <view class="login-font">登录星享购,打开美好生活</view>
+ <view class="login-btn">立即登录</view>
+ </view>
+ </view>
+
+ <uni-popup ref="popup2" type="center" :is-mask-click="false">
+ <view class="popup-content">
+ <view class="edu-view edu-view2">
+ <view class="edu-view-desc2">认证领取额度</view>
+ <image class="edu-icon-succ" src="/static/imgs/auths.png" mode=""></image>
+ <view class="edu-view-desc3">
+ 请您先认证个人信息,然后立即领取额度
+ </view>
+ <view class="edu-btns" @tap="go_auth">立即领取</view>
+ <!-- <view class="edu-btns" @tap="$util.goPages('/pages/idcard/idcard')">立即领取</view> -->
+ </view>
+ </view>
+ </uni-popup>
+ </view>
</template>
<script>
- import permision from "@/js_sdk/wa-permission/permission.js"
- export default {
- data() {
- return {
- scrollIndex: 0,
- scrollCurrent: '',
- staHeight: 0,
- paddingHeight: 0,
- indicatorDots: true,
- indicatorColor: '#e3b400',
- indicatorActiveColor: '#ffffff',
- autoplay: true,
- interval: 3000,
- duration: 500,
- bannerData: [],
- catesNavData: [],
- goodsData: [],
- prefGoods: [],
- hotGoods: [],
- pointGoods: [],
- userInfo: null,
- config: null,
+import permision from "@/js_sdk/wa-permission/permission.js"
+export default {
+ props: {
+ // 开启动画
+ animation: {
+ type: Boolean,
+ default: true
+ },
+ type: {
+ type: String,
+ default: 'top'
+ },
+ //是否是原生头部
+ isNativeHead:{
+ type: Boolean,
+ default: true
+ },
+ },
+ data() {
+ return {
+ contactsFlag: false,
+ staHeight: 0,
+ paddingHeight: 0,
+ indicatorDots: true,
+ indicatorColor: '#e3b400',
+ indicatorActiveColor: '#ffffff',
+ autoplay: true,
+ interval: 3000,
+ duration: 500,
+ bannerData: [],
+ catesNavData: [],
+ catesHotData: [],
+ userInfo: null,
+ config: null,
+ statusBarHeight: 30,
+ StatusBar: 0,
+ hotGoods: null,
+ newGoods: null,
+ hotCates: null,
+ catesId: 3,
+ is_hot: '',
+ pageIndex: 1,
+ pageSize: 10,
+ goodsData: [],
+ }
+ },
+ onShow() {
+ this.getData()
+ this.getConfig()
+ this.getUserInfo()
+ // #ifdef APP-PLUS
+ plus.navigator.setStatusBarBackground('transparent');
+ // #endif
+ },
+ created() {
+ this.catesGoods()
+ },
+ onLoad() {
+ const systemInfo = uni.getSystemInfoSync();
+ this.statusBarHeight = systemInfo.statusBarHeight || 30;
+ },
+ onReady() {
+ // #ifdef APP-PLUS
+ this.getSystemInfo();
+ // #endif
+ },
+ mounted() {
+ },
+ methods: {
+ //获取分类商品
+ catesGoods(){
+ this.$util.post('Goods/getCatesGoods', {catesId: this.catesId, is_hot: this.is_hot, pageIndex: this.pageIndex, pageSize: this.pageSize}).then(res => {
+ if(res.code == 1) {
+ this.goodsData = res.data.list
}
- },
- onShow() {
- this.getData()
- this.getConfig()
- this.getUserInfo()
- },
- onLoad() {
- },
- onReady() {
- // #ifdef APP-PLUS
- plus.navigator.setStatusBarBackground('#FFD2D8')
- // #endif
- },
- methods: {
- getData () {
- this.$util.post('Goods/getIndexData').then(res => {
- this.bannerData = res.data.bannerData
- this.catesNavData = res.data.catesNavData
- this.pointGoods = res.data.pointGoods
- this.hotGoods = res.data.hotGoods
- this.prefGoods = res.data.prefGoods
- this.goodsData = res.data.catesNavData[1].goods
- })
- },
- scrollClick (index) {
- this.scrollIndex = index
- this.scrollCurrent = 'scrollItem' + index
- this.goodsData = []
- this.goodsData = this.catesNavData[index].goods
- setTimeout(() => {
- this.scrollCurrent = ''; // 重置以便下次可以滚动到相同位置
- }, 500);
- },
- getConfig() {
- let t = this
- t.$util.post('Index/getConfig').then(res => {
- if(res.code == 1) {
- t.config = res.data
- }
- })
- },
-
- getUserInfo () {
- let t = this
- t.$util.post('User/getUser').then(res => {
- if(res.code == 1){
- this.userInfo = res.data
- if(res.data.over_auth == 0 && t.config.put_open == 2){
- setTimeout(() => {
- t.$refs.popup2.open()
- }, 100)
- } else {
- setTimeout(() => {
- t.$refs.popup2.close()
- }, 100)
- }
- if(res.data.contacts == 0 && uni.getSystemInfoSync().platform == 'android' && this.config.put_open == 2){
- t.requestAndroidPermission('android.permission.READ_CONTACTS')
- }
- }
- })
- },
- goLingqu () {
- if(!this.$util.checkLogin('/pages/index/index')){
- return
- }
- if(this.userInfo && this.userInfo.over_auth == 1) {
- return uni.showToast({
- title: '您已完成认证!',
- icon: 'success'
- })
- }
- let goUrl = ''
- if(this.userInfo.idcard_auth == 0){
- goUrl = '/pages/idcard/idcard'
- } else if(this.userInfo.face_auth == 0){
- goUrl = '/pages/face/face'
- } else if(this.userInfo.job_auth == 0){
- goUrl = '/pages/job/job'
- } else if(this.userInfo.bank_auth == 0){
- goUrl = '/pages/addBank/addBank?backUrl=/pages/edu/edu'
- } else {
- return uni.showToast({
- title: '您已完成认证!',
- icon: 'success'
- })
- }
- if(goUrl != '') {
- uni.navigateTo({
- url: goUrl
- })
- }
- },
- async requestAndroidPermission(permisionID) {
- let t = this
- let result = await permision.requestAndroidPermission(permisionID)
- if (result == 1) {
- t.setContacts()
- } else if (result == 0) {
- this.$util.goSetting()
- } else {
- this.$util.goSetting()
- }
- },
-
- setContacts () {
- uni.showLoading({
- title: '授权中...'
- })
- let self = this
- plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE, addressbook => {
- addressbook.find(["displayName","phoneNumbers"],function(contacts){
- let ob = []
- let phone = []
- for(let i in contacts){
- let json = {}
- if(contacts[i].phoneNumbers.length > 0 && contacts[i].displayName){
- if(phone.indexOf(contacts[i].phoneNumbers[0].value) < 0){
- json.name = contacts[i].displayName
- json.phone = contacts[i].phoneNumbers[0].value
- phone = phone.concat(json.phone)
- ob = ob.concat(json)
- }
- }
- }
+ })
+ },
+ //获取状态栏高度
+ getSystemInfo() {
+ let _this = this;
+ uni.getSystemInfo({
+ success: function(e) {
+ _this.StatusBar = e.statusBarHeight + 'px'; //用于自定义头部时,给手机状态栏留出位置,可通过isNativeHead这个参数控制
+ }
+ })
+ },
+ getConfig() {
+ let t = this
+ t.$util.post('Index/getConfig').then(res => {
+ if(res.code == 1) {
+ console.log('config', res.data)
+ t.config = res.data
+ }
+ })
+ },
+ getUserInfo () {
+ let t = this
+ t.$util.post('User/getUser').then(res => {
+ console.log('getUser',res);
+ if(res.code == 1){
+ this.userInfo = res.data
+ if(res.data.over_auth == 0 && t.config.put_open == 2){
+ setTimeout(() => {
+ //t.$refs.popup2.open()
+ }, 100)
+ }
+ // if(res.data.contacts == 0 && uni.getSystemInfoSync().platform == 'android'){
+ // // t.contactsFlag = !t.contactsFlag
+ // t.requestAndroidPermission('android.permission.READ_CONTACTS')
+ // }
+ }
+ })
+ },
+ handleAuthorize() {
+ this.contactsFlag = false;
+ this.requestAndroidPermission('android.permission.READ_CONTACTS')
+ },
+ // 4. 手动点击“拒绝”按钮
+ handleReject() {
+ this.contactsFlag = false;
+ uni.showToast({ title: '已拒绝通讯录权限', icon: 'none' });
+ // 可根据业务需求处理拒绝后的逻辑(如限制功能使用)
+ },
+ go_auth () {
+ let goUrl = ''
+ if(this.userInfo.idcard_auth == 0){
+ goUrl = '/pages/idcard/idcard'
+ } else if(this.userInfo.face_auth == 0){
+ goUrl = '/pages/face/face'
+ } else if(this.userInfo.job_auth == 0){
+ goUrl = '/pages/job/job'
+ } else if(this.userInfo.bank_auth == 0){
+ goUrl = '/pages/addBank/addBank?backUrl=/pages/edu/edu'
+ }
+ console.log(goUrl);
+ if(goUrl != '') {
+ uni.navigateTo({
+ url: goUrl
+ })
+ }else{
+ this.$refs.popup2.close()
+ uni.showToast({
+ title: '无法认证!',
+ icon: 'error'
+ })
+ }
+ },
+ goLingqu () {
+ if(!this.userInfo){
+ return uni.showToast({
+ title: '请先登录!',
+ icon: 'error'
+ })
+ }
+ if(this.userInfo && this.userInfo.over_auth == 1) {
+ return uni.showToast({
+ title: '您已完成认证!',
+ icon: 'success'
+ })
+ }
+ let goUrl = ''
+ if(this.userInfo.idcard_auth == 0){
+ goUrl = '/pages/idcard/idcard'
+ } else if(this.userInfo.face_auth == 0){
+ goUrl = '/pages/face/face'
+ } else if(this.userInfo.job_auth == 0){
+ goUrl = '/pages/job/job'
+ } else if(this.userInfo.bank_auth == 0){
+ goUrl = '/pages/addBank/addBank?backUrl=/pages/edu/edu'
+ } else {
+ return uni.showToast({
+ title: '您已完成认证!',
+ icon: 'success'
+ })
+ }
+ if(goUrl != '') {
+ uni.navigateTo({
+ url: goUrl
+ })
+ }
+ },
+ async requestAndroidPermission(permisionID) {
+ let t = this
+ let result = await permision.requestAndroidPermission(permisionID)
+ if (result == 1) {
+ t.setContacts()
+ } else if (result == 0) {
+ this.$util.goSetting()
+ } else {
+ this.$util.goSetting()
+ }
+ },
+ getData () {
+ this.$util.post('Goods/getIndexData').then(res => {
+ this.bannerData = res.data.bannerData
+ this.catesNavData = res.data.catesNavData
+ this.catesHotData = res.data.catesHotData
+ this.hotGoods = res.data.hotGoods
+ this.newGoods = res.data.newGoods
+ this.hotCates = res.data.hotCates
+ console.log('this.hotCates',this.hotCates)
+ })
+ },
+ setContacts () {
+ uni.showLoading({
+ title: '授权中...'
+ })
+ let self = this
+ plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE, addressbook => {
+ addressbook.find(["displayName","phoneNumbers"],function(contacts){
+ let ob = []
+ let phone = []
+ for(let i in contacts){
+ let json = {}
+ if(contacts[i].phoneNumbers.length > 0 && contacts[i].displayName){
+ if(phone.indexOf(contacts[i].phoneNumbers[0].value) < 0){
+ json.name = contacts[i].displayName
+ json.phone = contacts[i].phoneNumbers[0].value
+ phone = phone.concat(json.phone)
+ ob = ob.concat(json)
+ }
+ }
+ }
- if(ob.length > 0){
- self.$util.post('Contacts/setContacts', ob).then(res => {
- uni.hideLoading()
- uni.showToast({
- title: res.msg || '授权成功!',
- icon: 'success',
- })
- })
- }
- uni.hideLoading()
- })
- },
- function (e){
- this.requestAndroidPermission('android.permission.READ_CONTACTS')
- })
- },
- }
- }
+ if(ob.length > 0){
+ self.$util.post('Contacts/setContacts', ob).then(res => {
+ uni.hideLoading()
+ uni.showToast({
+ title: res.msg || '授权成功!',
+ icon: 'success',
+ })
+ })
+ }
+ uni.hideLoading()
+ })
+ },
+ function (e){
+ this.requestAndroidPermission('android.permission.READ_CONTACTS')
+ })
+ },
+ }
+}
</script>
<style>
- @import url(@/static/css/index.css);
+@import url(@/static/css/index.css);
+</style>
+<style lang="scss">
+.uni-popup {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 99999;
+ overflow: hidden;
+ &__wrapper {
+ position: absolute;
+ z-index: 9999999;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+ &.ani {
+ /* #ifndef APP-NVUE */
+ transition: all 0.3s;
+ /* #endif */
+ }
+ &.top {
+ top: 0;
+ width:705rpx;
+ /* #ifdef APP-NVUE */
+ left:22.5rpx;
+ /* #endif */
+ /* #ifndef APP-NVUE */
+ left:0;
+ transform: translateY(-705rpx);
+ /* #endif */
+ }
+ &-box {
+ position: relative;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+ }
+ &.uni-custom {
+ & .uni-popup__wrapper-box {
+ width: 705rpx;
+ /* #ifndef APP-NVUE */
+ margin: 0 22.5rpx;
+ /* #endif */
+ padding: 30upx;
+ background: #fff;
+ z-index:9999;
+ border: solid 2rpx #ddd;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+ border-radius: 16rpx;
+ .title{
+ font-size: 32rpx;
+ font-weight: bold;
+ }
+ .content{
+ margin-top: 16rpx;
+ line-height: 1.6;
+ }
+ }
+ &.top{
+ & .uni-popup__wrapper-box {
+ width: 705rpx;
+ }
+ }
+ }
+ &.uni-top{
+ transform: translateY(0);
+ }
+ }
+}
</style>
diff --git a/pages/login/login.vue b/pages/login/login.vue
index b14533a..5dbcb6b 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -12,7 +12,7 @@
</view>
<view class="login-list">
<view class="login-item">
- <input type="tel" v-model="formData.phone" placeholder="请输入手机号" />
+ <input type="tel" v-model="formData.phone" maxlength="11" placeholder="请输入手机号" />
</view>
<view class="login-item" v-if="formData.loginType == 1">
<input type="password" v-model="formData.pass" placeholder="请输入密码" />
@@ -25,7 +25,7 @@
</view>
<view class="loginBtn">
<view class="loginBtn-desc">
- <image @click="agreeClick" class="loginBtn-icon" :src="!loginAct ? '/static/imgs/check.png' : '/static/imgs/checked.png'" ></image>
+ <image @click="agreeClick" class="loginBtn-icon" :src="!loginAct ? '/static/imgs/check.png' : '/static/imgs/checked.png'" mode=""></image>
<text>我已阅读并同意</text>
<text class="blue" @tap="agreeDialog1">《注册协议》</text>
<text class="blue" @tap="agreeDialog2">《隐私政策》</text>
@@ -77,103 +77,162 @@
agreeClick () {
this.loginAct = !this.loginAct
},
- async loginUser() {
- let t = this
+ // loginUser () {
+ // let t = this
+ // if(!t.loginAct) {
+ // return uni.showToast({
+ // title: '请阅读并同意协议政策',
+ // icon: 'error'
+ // })
+ // }
+ // if(t.formData.phone == '' || !t.phoneReg.test(t.formData.phone)){
+ // return uni.showToast({
+ // title: '手机号错误!',
+ // icon: 'error',
+ // })
+ // }
+ // if(t.formData.pass == '' && t.formData.loginType == 1){
+ // return uni.showToast({
+ // title: '请输入密码!',
+ // icon: 'error',
+ // })
+ // }
+ // if(t.formData.code == '' && t.formData.loginType == 2){
+ // return uni.showToast({
+ // title: '请输入验证码!',
+ // icon: 'error',
+ // })
+ // }
+ // t.$util.post('User/Login', this.formData).then(res => {
+ // if(res.code == 1) {
+ // uni.setStorageSync('pig-token', res.data.userToken)
+ // uni.setStorageSync('user-info', JSON.stringify(res.data))
+ // let isSwitch = 0
+ // let goUrl = ''
+ // if(this.backUrl == ''){
+ // isSwitch = 1
+ // goUrl = '/pages/index/index'
+ // } else {
+ // goUrl = this.backUrl
+ // }
+ // uni.showToast({
+ // title: res.msg || '登录成功'
+ // })
+ // setTimeout(() => {
+ // if(isSwitch == 1) {
+ // t.$util.goSwitch(goUrl)
+ // } else {
+ // t.$util.goPages(goUrl)
+ // }
+ // }, 1500)
+ // } else {
+ // uni.showToast({
+ // title: res.msg || '登录失败',
+ // icon: 'error'
+ // })
+ // }
+ // })
+ // },
+ async loginUser() {
+ let t = this
- if (!t.loginAct) {
- return uni.showToast({
- title: '请阅读并同意协议政策',
- icon: 'error'
- })
- }
+ if (!t.loginAct) {
+ return uni.showToast({
+ title: '请阅读并同意协议政策',
+ icon: 'error'
+ })
+ }
- if (t.formData.phone == '' || !t.phoneReg.test(t.formData.phone)) {
- return uni.showToast({
- title: '手机号错误!',
- icon: 'error'
- })
- }
+ if (t.formData.phone == '' || !t.phoneReg.test(t.formData.phone)) {
+ return uni.showToast({
+ title: '手机号错误!',
+ icon: 'error'
+ })
+ }
- if (t.formData.pass == '' && t.formData.loginType == 1) {
- return uni.showToast({
- title: '请输入密码!',
- icon: 'error'
- })
- }
+ if (t.formData.pass == '' && t.formData.loginType == 1) {
+ return uni.showToast({
+ title: '请输入密码!',
+ icon: 'error'
+ })
+ }
- if (t.formData.code == '' && t.formData.loginType == 2) {
- return uni.showToast({
- title: '请输入验证码!',
- icon: 'error'
- })
- }
+ if (t.formData.code == '' && t.formData.loginType == 2) {
+ return uni.showToast({
+ title: '请输入验证码!',
+ icon: 'error'
+ })
+ }
- try {
+ try {
- // 登录接口
- const res = await t.$util.post('User/Login', this.formData)
+ // 登录接口
+ const res = await t.$util.post('User/Login', this.formData)
- if (res.code == 1) {
+ if (res.code == 1) {
- uni.setStorageSync('lzsc-token', res.data.userToken)
- uni.setStorageSync('user-info', JSON.stringify(res.data))
+ uni.setStorageSync('pig-token', res.data.userToken)
+ uni.setStorageSync('user-info', JSON.stringify(res.data))
- let isSwitch = 0
- let goUrl = ''
+ let isSwitch = 0
+ let goUrl = ''
- // qxgLogin接口
- const ygxRes = await new Promise((resolve, reject) => {
- uni.request({
- url: t.$webHost + '/api/v2/auth/qxgIosUnionLogin?phone=' + t.$util.getUserInfo().phone,
- success: resolve,
- fail: reject
- })
- })
- console.log(ygxRes);
- if(!ygxRes.data.data.boughtEquity){
- goUrl = '/pages/quanyi/index'
- t.$util.goPages(goUrl)
- return
- }
+ // qxgLogin接口
+ const ygxRes = await new Promise((resolve, reject) => {
+ uni.request({
+ url: t.$webHost + '/api/v2/auth/qxgIosUnionLogin?phone=' + t.$util.getUserInfo().phone,
+ success: resolve,
+ fail: reject
+ })
+ })
+ console.log(ygxRes);
+ // if(!ygxRes.data.data.boughtEquity){
+ // goUrl = '/pages/quanyi/index'
+ // // t.$util.goPages(goUrl)
+ // uni.navigateTo({
+ // url: '/pages/quanyi/index'
+ // })
+ // return
+ // }
- t.qxgLogin = ygxRes.data.data
- uni.setStorageSync('qxgLogin', t.qxgLogin)
+ t.ygxLogin = ygxRes.data.data
+ uni.setStorageSync('ygxLogin', t.ygxLogin)
- if (this.backUrl == '') {
- isSwitch = 1
- goUrl = '/pages/index/index'
- } else {
- goUrl = this.backUrl
- }
+ if (this.backUrl == '') {
+ isSwitch = 1
+ goUrl = '/pages/index/index'
+ } else {
+ goUrl = this.backUrl
+ }
- uni.showToast({
- title: res.msg || '登录成功'
- })
+ uni.showToast({
+ title: res.msg || '登录成功'
+ })
- setTimeout(() => {
- if (isSwitch == 1) {
- t.$util.goSwitch(goUrl)
- } else {
- t.$util.goPages(goUrl)
- }
- }, 1500)
+ setTimeout(() => {
+ if (isSwitch == 1) {
+ t.$util.goSwitch(goUrl)
+ } else {
+ t.$util.goPages(goUrl)
+ }
+ }, 1500)
- } else {
- uni.showToast({
- title: res.msg || '登录失败',
- icon: 'error'
- })
- }
+ } else {
+ uni.showToast({
+ title: res.msg || '登录失败',
+ icon: 'error'
+ })
+ }
- } catch (err) {
- console.error(err)
+ } catch (err) {
+ console.error(err)
- uni.showToast({
- title: '请求失败',
- icon: 'error'
- })
- }
- },
+ uni.showToast({
+ title: '请求失败',
+ icon: 'error'
+ })
+ }
+ },
sendCode () {
let t = this
if(this.formData.phone == '' || !this.phoneReg.test(t.formData.phone)){
diff --git a/pages/my/my.vue b/pages/my/my.vue
index f357ffe..5dd3e69 100644
--- a/pages/my/my.vue
+++ b/pages/my/my.vue
@@ -1,17 +1,18 @@
<template>
- <view class="my">
+ <view class="myView">
+ <image class="myView-bg" src="@/static/imgs/my-bg.png" mode="scaleToFill"></image>
<yk-authpup ref="authpup" type="top" @changeAuth="changeAuth" noticeTitle="便于您使用该功能上传您的照片用于核实身份场景中读取相册内容" permissionID="WRITE_EXTERNAL_STORAGE"></yk-authpup>
<yk-authpup ref="camears" type="top" @changeAuth="changeAuth2" noticeTitle="便于您使用该功能便于您拍摄上传用于核实身份场景" permissionID="CAMERA"></yk-authpup>
- <view class="startBar" style="background-color: #e3b400;"></view>
- <view class="myTop">
- <view class="headimg">
- <image :src="userInfo != null ? userInfo.avatar : '/static/imgs/headimg.png'" class="" mode="widthFix" @tap="setImg"></image>
- <view class="">
+ <view class="startBar"></view>
+ <view class="myContent">
+ <view class="myHeader">
+ <image :src="userInfo != null ? userInfo.avatar : '/static/imgs/headimg.png'" class="avatar" mode="" @tap="setImg"></image>
+ <view class="myNick">
<template>
<view v-if="userInfo == null" @tap="$util.goPages('/pages/login/login')">未登录</view>
<view v-else>{{userInfo.phone}}</view>
</template>
- <template v-if="userInfo != null && Config">
+ <!-- <template v-if="userInfo != null && Config">
<template v-if="userInfo.status == 1">
<view class="edu" v-if="userInfo != null && userInfo.edu_auth == 0 && Config.put_open == 2 && userInfo.over_auth == 0">额度:未认证</view>
<view class="edu" v-else-if="userInfo != null && userInfo.edu_auth == 0 && Config.put_open == 2 && userInfo.over_auth == 1">额度:授信中</view>
@@ -22,107 +23,90 @@
</template>
</template>
<template v-if="userInfo != null && userInfo.face_auth == 0">
- <view class="edu" @tap="$util.checkGoPages('/pages/my/my')">未认证(去认证)</view>
+ <view class="edu" @tap="$util.checkGoPages('/pages/my/my')">未认证</view>
+ </template> -->
+ </view>
+ </view>
+
+ <!-- <view class="auth" v-if="userInfo && userInfo.over_auth != 1" @tap="$util.checkGoPages('/pages/my/my')">
+ <image src="@/static/imgs/notice.png" mode=""></image>
+ <text>您还未进行实名认证,点击前往实名认证</text>
+ <view class="">去认证</view>
+ </view> -->
+
+ <view class="navs">
+ <view class="title" @tap="$util.checkLogin('/pages/order/order')">
+ <text>我的订单</text>
+ <view class="navs-rgt">
+ <text>查看全部</text>
+ <image src="/static/imgs/right.png" mode=""></image>
+ </view>
+ </view>
+ <view class="navCon">
+ <view class="navCon-v" v-for="(item, index) in navData" @tap="$util.checkLogin(item.url)">
+ <image :src="'../../static/imgs/nv' + item.index + '.png'" mode=""></image>
+ <view>{{item.text}}</view>
+ </view>
+ </view>
+ </view>
+
+ <view class="mySer">
+ <view class="title">我的服务</view>
+ <view class="mySerCon">
+ <template v-for="(item, index) in mySerData">
+ <view class="mySerCon-v" @tap="$util.checkLogin(item.url)" v-if="item.index != 6">
+ <view class="mySerCon-l">
+ <image :src="'../../static/imgs/mf' + item.index + '.png'" mode=""></image>
+ <view>{{item.text}}</view>
+ </view>
+ <image src="/static/imgs/right.png" mode=""></image>
+ </view>
+ <view class="mySerCon-v" @tap="goKefu" v-else>
+ <view class="mySerCon-l">
+ <image :src="'../../static/imgs/mf' + item.index + '.png'" mode=""></image>
+ <view>{{item.text}}</view>
+ </view>
+ <image src="/static/imgs/right.png" mode=""></image>
+ </view>
</template>
+ <!-- <view class="mySerCon-v" @tap="toHuishou" >
+ <view class="mySerCon-l">
+ <template v-if="isShop">
+ <image src="/static/imgs/quanshou.png" mode="widthFix"></image>
+ <text>会员权收</text>
+ </template>
+ </view>
+ <image src="/static/imgs/right.png" mode=""></image>
+ </view> -->
</view>
</view>
- </view>
-
- <view class="myNavs">
- <view class="myNavs-tips" @tap="$util.checkLogin('/pages/order/order')">
- <text class="myNavs-tips-text">我的订单</text>
- <view class="myNavs-tips-rgt">
- <text>查看全部</text>
- <image src="/static/imgs/right.png" mode=""></image>
+
+ <view class="myBeiAn">
+ <view class="">
+ <text @tap="agreeDialog1">《用户协议》</text>
+ <text>|</text>
+ <text @tap="agreeDialog2">《隐私政策》</text>
</view>
+ <view @tap="goWebUrl('http://beian.miit.gov.cn')">APP备案号: {{Config.icp}}</view>
+ <view @tap="goWebUrl('http://beian.miit.gov.cn')">Copyright © 2025 星河科技 All Rights Reserved.</view>
+
</view>
- <view class="myNavs-view">
- <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=1')">
- <image class="myNavs-item-icon" src="/static/imgs/my-n-1.png" mode="widthFix"></image>
- <text class="myNavs-item-text">待支付</text>
- </view>
- <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=3')">
- <image class="myNavs-item-icon" src="/static/imgs/my-n-5.png" mode="widthFix"></image>
- <text class="myNavs-item-text">待签约</text>
- </view>
- <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=4')">
- <image class="myNavs-item-icon" src="/static/imgs/my-n-2.png" mode="widthFix"></image>
- <text class="myNavs-item-text">待发货</text>
- </view>
- <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=5')">
- <image class="myNavs-item-icon" src="/static/imgs/my-n-3.png" mode="widthFix"></image>
- <text class="myNavs-item-text">待收货</text>
- </view>
- <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=8')">
- <image class="myNavs-item-icon" src="/static/imgs/my-n-4.png" mode="widthFix"></image>
- <text class="myNavs-item-text">已完成</text>
- </view>
- </view>
- </view>
-
- <view class="myServe">
- <view class="myNavs-tips">
- <text class="myNavs-tips-text">我的服务</text>
- </view>
- <view class="myServe-view">
- <view class="myServe-item" @tap="$util.checkLogin('/pages/profile/profile')">
- <image src="/static/imgs/my-s-1.png" mode="widthFix"></image>
- <text>个人信息</text>
- </view>
- <view class="myServe-item" @tap="$util.checkLogin('/pages/logs/logs')" v-if="Config && Config.put_open == 2">
- <image src="/static/imgs/my-s-2.png" mode="widthFix"></image>
- <text>账单信息</text>
- </view>
- <view class="myServe-item" @tap="$util.checkLogin('/pages/bank/bank')">
- <image src="/static/imgs/my-s-3.png" mode="widthFix"></image>
- <text>银行卡</text>
- </view>
- <view class="myServe-item" @tap="$util.checkLogin('/pages/address/address')">
- <image src="/static/imgs/my-s-4.png" mode="widthFix"></image>
- <text>我的地址</text>
- </view>
- <view class="myServe-item" @tap="$util.checkLogin('/pages/collect/collect')">
- <image src="/static/imgs/my-s-5.png" mode="widthFix"></image>
- <text>我的收藏</text>
- </view>
- <view class="myServe-item" @tap="goKefu">
- <image src="/static/imgs/my-s-6.png" mode="widthFix"></image>
- <text>联系客服</text>
- </view>
- <view class="myServe-item" @tap="toHuishou" >
- <template v-if="isShop">
- <image src="/static/imgs/quanshou.png" mode="widthFix"></image>
- <text>会员权收</text></template>
-
- </view>
-
- </view>
- </view>
- <view class="myBeiAn">
- <view class="">
- <text @tap="agreeDialog1">《用户协议》</text>
- <text>|</text>
- <text @tap="agreeDialog2">《隐私政策》</text>
- </view>
- <view @tap="goWebUrl('http://beian.miit.gov.cn')">APP备案号: {{Config.icp}}</view>
- <view @tap="goWebUrl('http://beian.miit.gov.cn')">Copyright © 2025 {{Config.url}} All Rights Reserved.</view>
+ <agreePup ref="agreePup1" :gp="1" title="注册协议"></agreePup>
+ <agreePup ref="agreePup2" :gp="2" title="隐私政策"></agreePup>
+ <uni-popup type="center" ref="comment" >
+ <view style="background-image: url('/static/imgs/BOXSDF.png');background-repeat: no-repeat;background-size: 100% 100%">
+ <view style="border-radius: 48rpx;padding: 28rpx 20rpx">
+ <view style="text-align: center;padding-top: 30rpx;" >
+ <image src="/static/imgs/wuImg.png" style="width: 372rpx;height: 240rpx;"></image>
+ </view>
+ <view style="font-size: 32rpx;font-weight: 600;color: #333333;text-align: center;margin-top: 34rpx;margin-bottom: 80rpx;padding: 0 28rpx;">{{errState}}</view>
+ <view @click="$refs.comment.close()" style="font-weight: 500;font-size: 32rpx;color: #fff;text-align: center;padding: 24rpx 28rpx;border-top: 2rpx solid #EEEEEE;background: #FF1472;;border-radius: 40rpx ;">好的</view>
+ </view>
+ </view>
+
+ </uni-popup>
</view>
- <agreePup ref="agreePup1" :gp="1" title="注册协议"></agreePup>
- <agreePup ref="agreePup2" :gp="2" title="隐私政策"></agreePup>
-
- <uni-popup type="center" ref="comment" >
- <view style="background-image: url('/static/imgs/BOXSDF.png');background-repeat: no-repeat;background-size: 100% 100%">
- <view style="border-radius: 48rpx;padding: 28rpx 20rpx">
- <view style="text-align: center;padding-top: 30rpx;" >
- <image src="/static/imgs/wuImg.png" style="width: 372rpx;height: 240rpx;"></image>
- </view>
- <view style="font-size: 32rpx;font-weight: 600;color: #333333;text-align: center;margin-top: 34rpx;margin-bottom: 80rpx;padding: 0 28rpx;">{{errState}}</view>
- <view @click="$refs.comment.close()" style="font-weight: 500;font-size: 32rpx;color: #fff;text-align: center;padding: 24rpx 28rpx;border-top: 2rpx solid #EEEEEE;background: #FF1472;;border-radius: 40rpx ;">好的</view>
- </view>
- </view>
-
- </uni-popup>
</view>
</template>
@@ -137,58 +121,74 @@
return {
userInfo: this.$util.getUserInfo(),
Config: {put_open: 1},
- isShop:false,
- errState:''
+ navData: [
+ {index: 1, text: '待支付', url: '/pages/order/order?status=1'},
+ // {index: 2, text: '待签约', url: '/pages/order/order?status=3'},
+ {index: 3, text: '待发货', url: '/pages/order/order?status=4'},
+ {index: 4, text: '待收货', url: '/pages/order/order?status=5'},
+ {index: 5, text: '已完成', url: '/pages/order/order?status=8'}
+ ],
+ mySerData: [
+ {index: 1, text: '个人信息', url: '/pages/profile/profile'},
+ // {index: 2, text: '账单信息', url: '/pages/logs/logs'},
+ // {index: 3, text: '银行卡', url: '/pages/bank/bank'},
+ {index: 4, text: '我的地址', url: '/pages/address/address'},
+ {index: 5, text: '我的收藏', url: '/pages/collect/collect'},
+ {index: 6, text: '联系客服'},
+ ],
+ isShop:false,
+ errState: ''
}
},
- onLoad() {
+ onLoad() {
},
- async onShow() {
+ onShow() {
this.getConfig()
this.getUserInfo()
- const _this = this;
- if(_this.$util.getUserInfo()?.phone){
- await uni.request({
- url: _this.$webHost+'/api/v2/auth/qxgIosUnionLogin?phone='+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
- success: function (res) {
- _this.isShop = res?.data?.data?.isShop;
- uni.setStorageSync('qxgLogin', res.data.data)
- }
- })
- }
+ const _this = this;
+ if(_this.$util.getUserInfo()?.phone){
+ uni.request({
+ url: _this.$webHost+'/api/v2/auth/qxgIosUnionLogin?phone='+_this.$util.getUserInfo().phone,
+ data:{
+ customerId: uni.getStorageSync('ygxLogin').customerId,
+ appId: uni.getStorageSync('ygxLogin').appId
+ },
+ success: function (res) {
+ _this.isShop = res?.data?.data?.isShop;
+ uni.setStorageSync('ygxLogin', res.data.data)
+ console.log('isShop',_this.isShop)
+ console.log('res',res)
+ }
+ })
+ }
},
methods: {
- toHuishou(){
- const _this = this;
- if(!_this.isShop){
- return
- }
- uni.request({
- url: _this.$webHost+'/api/v2/customer/ryxIsBuyEquity?phone='+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
- success: function (res) {
- if(res.data.code==200){
- uni.navigateTo( {
- url: '/pages/quanshou/index'
- })
+ toHuishou(){
+ const _this = this;
+ if(!_this.isShop){
+ return
+ }
+ uni.request({
+ url: _this.$webHost+'/api/v2/customer/ryxIsBuyEquity?phone='+_this.$util.getUserInfo().phone,
+ data:{
+ customerId: uni.getStorageSync('ygxLogin').customerId,
+ appId: uni.getStorageSync('ygxLogin').appId
+ },
+ success: function (res) {
+ if(res.data.code==200){
+ uni.navigateTo( {
+ url: '/pages/quanshou/index'
+ })
- } else {
- _this.errState = res.data.data;
- _this.$refs.comment.open()
+ } else {
+ _this.errState = res.data.data;
+ _this.$refs.comment.open()
- }
- }
- })
+ }
+ }
+ })
- },
-
+ },
goKefu() {
if(this.Config && this.Config.kefu_open == 1) {
this.wechatWork()
@@ -247,7 +247,7 @@
getUserInfo () {
let t = this
t.$util.post('User/getUser').then(res => {
- t.userInfo = res.data
+ t.userInfo = res.data
})
},
agreeDialog1 (e) {
@@ -300,7 +300,6 @@
}
}
</script>
-
<style>
- @import url(/static/css/my.css);
-</style>
+ @import url(/static/css/my2.css);
+</style>
\ No newline at end of file
diff --git a/pages/my/my_bak.vue b/pages/my/my_bak.vue
new file mode 100644
index 0000000..8e6ba07
--- /dev/null
+++ b/pages/my/my_bak.vue
@@ -0,0 +1,246 @@
+<template>
+ <view class="my">
+ <yk-authpup ref="authpup" type="top" @changeAuth="changeAuth" noticeTitle="便于您使用该功能上传您的照片用于核实身份场景中读取相册内容" permissionID="WRITE_EXTERNAL_STORAGE"></yk-authpup>
+ <yk-authpup ref="camears" type="top" @changeAuth="changeAuth2" noticeTitle="便于您使用该功能便于您拍摄上传用于核实身份场景" permissionID="CAMERA"></yk-authpup>
+ <view class="startBar"></view>
+ <view class="myTop">
+ <view class="headimg">
+ <image :src="userInfo != null ? userInfo.avatar : '/static/imgs/headimg.png'" class="" mode="" @tap="setImg"></image>
+ <view class="">
+ <template>
+ <view v-if="userInfo == null" @tap="$util.goPages('/pages/login/login')">未登录</view>
+ <view v-else>{{userInfo.phone}}</view>
+ </template>
+ <template v-if="userInfo != null && Config">
+ <template v-if="userInfo.status == 1">
+ <view class="edu" v-if="userInfo != null && userInfo.edu_auth == 0 && Config.put_open == 2 && userInfo.over_auth == 0">额度:未认证</view>
+ <view class="edu" v-else-if="userInfo != null && userInfo.edu_auth == 0 && Config.put_open == 2 && userInfo.over_auth == 1">额度:授信中</view>
+ <view class="edu" v-else-if="userInfo != null && userInfo.edu_auth == 1 && Config.put_open == 2">额度:{{userInfo.const}}</view>
+ </template>
+ <template v-else>
+ <view class="edu">额度:授信失败</view>
+ </template>
+ </template>
+ <template v-if="userInfo != null && userInfo.face_auth == 0">
+ <view class="edu" @tap="$util.checkGoPages('/pages/my/my')">未认证(去认证)</view>
+ </template>
+ </view>
+ </view>
+ </view>
+
+ <view class="myNavs">
+ <view class="myNavs-tips" @tap="$util.checkLogin('/pages/order/order')">
+ <text class="myNavs-tips-text">我的订单</text>
+ <view class="myNavs-tips-rgt">
+ <text>查看全部</text>
+ <image src="/static/imgs/right.png" mode=""></image>
+ </view>
+ </view>
+ <view class="myNavs-view">
+ <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=1')">
+ <image class="myNavs-item-icon" src="/static/imgs/my-n-1.png" mode=""></image>
+ <text class="myNavs-item-text">待支付</text>
+ </view>
+ <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=3')">
+ <image class="myNavs-item-icon" src="/static/imgs/my-n-5.png" mode=""></image>
+ <text class="myNavs-item-text">待签约</text>
+ </view>
+ <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=4')">
+ <image class="myNavs-item-icon" src="/static/imgs/my-n-2.png" mode=""></image>
+ <text class="myNavs-item-text">待发货</text>
+ </view>
+ <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=5')">
+ <image class="myNavs-item-icon" src="/static/imgs/my-n-3.png" mode=""></image>
+ <text class="myNavs-item-text">待收货</text>
+ </view>
+ <view class="myNavs-item" @tap="$util.checkLogin('/pages/order/order?status=8')">
+ <image class="myNavs-item-icon" src="/static/imgs/my-n-4.png" mode=""></image>
+ <text class="myNavs-item-text">已完成</text>
+ </view>
+ </view>
+ </view>
+
+ <view class="myServe">
+ <view class="myNavs-tips">
+ <text class="myNavs-tips-text">我的服务</text>
+ </view>
+ <view class="myServe-view">
+ <view class="myServe-item" @tap="$util.checkLogin('/pages/profile/profile')">
+ <image src="/static/imgs/my-s-1.png" mode=""></image>
+ <text>个人信息</text>
+ </view>
+ <view class="myServe-item" @tap="$util.checkLogin('/pages/logs/logs')" v-if="Config && Config.put_open == 2">
+ <image src="/static/imgs/my-s-2.png" mode=""></image>
+ <text>账单信息</text>
+ </view>
+ <view class="myServe-item" @tap="$util.checkLogin('/pages/bank/bank')">
+ <image src="/static/imgs/my-s-3.png" mode=""></image>
+ <text>银行卡</text>
+ </view>
+ <view class="myServe-item" @tap="$util.checkLogin('/pages/address/address')">
+ <image src="/static/imgs/my-s-4.png" mode=""></image>
+ <text>我的地址</text>
+ </view>
+ <view class="myServe-item" @tap="$util.checkLogin('/pages/collect/collect')">
+ <image src="/static/imgs/my-s-5.png" mode=""></image>
+ <text>我的收藏</text>
+ </view>
+ <view class="myServe-item" @tap="goKefu">
+ <image src="/static/imgs/my-s-6.png" mode=""></image>
+ <text>联系客服</text>
+ </view>
+ <view class="myServe-item" v-if="Config.put_open == 1">
+
+ </view>
+ </view>
+ </view>
+ <view class="myBeiAn">
+ <view class="">
+ <text @tap="agreeDialog1">《用户协议》</text>
+ <text>|</text>
+ <text @tap="agreeDialog2">《隐私政策》</text>
+ </view>
+ <view @tap="goWebUrl('http://beian.miit.gov.cn')">APP备案号: {{Config.icp}}</view>
+ <view @tap="goWebUrl('http://beian.miit.gov.cn')">Copyright © 2025 {{Config.url}} All Rights Reserved.</view>
+
+ </view>
+ <agreePup ref="agreePup1" :gp="1" title="注册协议"></agreePup>
+ <agreePup ref="agreePup2" :gp="2" title="隐私政策"></agreePup>
+ </view>
+</template>
+
+<script>
+ import ykAuthpup from "@/components/yk-authpup/yk-authpup";
+ import agreePup from "@/components/agreePup/agreePup";
+ export default {
+ components: {
+ agreePup
+ },
+ data() {
+ return {
+ userInfo: this.$util.getUserInfo(),
+ Config: {put_open: 1},
+ }
+ },
+ onLoad() {
+ },
+ onShow() {
+ this.getConfig()
+ this.getUserInfo()
+ },
+ methods: {
+ goKefu() {
+ if(this.Config && this.Config.kefu_open == 1) {
+ this.wechatWork()
+ } else {
+ this.$util.checkLogin('/pages/customer/customer')
+ }
+ },
+ setImg(){
+ if(uni.getSystemInfoSync().platform != 'ios'){
+ this.$refs['authpup'].open()
+ }else{
+ this.chooseImg()
+ }//调起自定义权限目的弹框,具体可看示例里面很详细
+ },
+ changeAuth () {
+ //这里是权限通过后执行自己的代码逻辑
+ if(uni.getSystemInfoSync().platform !='ios'){
+ this.$refs['camears'].open()
+ }
+ this.chooseImg()
+ },
+ changeAuth2(){
+ this.chooseImg()
+ },
+ chooseImg () {
+ let t = this
+ uni.chooseImage({
+ count: 1, // 默认9
+ sizeType: ['original', 'compressed'],
+ success: (chooseImageRes) => {
+ let tempFilePaths = chooseImageRes.tempFilePaths
+ let token = uni.getStorageSync('lzsc-token')
+ let header = {
+ 'Accept': 'application/json',
+ 'authorization': token,
+ }
+ uni.showLoading({
+ title: '上传中..',
+ })
+ uni.uploadFile({
+ url: t.$baseUrl + '/api/user/setAvatar',
+ filePath: tempFilePaths[0],
+ header: header,
+ name: 'file',
+ success: (uploadFileRes) => {
+ uni.hideLoading()
+ let res = JSON.parse(uploadFileRes.data)
+ res = JSON.parse(t.$util.decryptDes(res.data))
+ t.userInfo.avatar = res.avatar
+ uni.setStorageSync('user-info', JSON.stringify(res))
+ }
+ })
+ }
+ })
+ },
+ getUserInfo () {
+ let t = this
+ t.$util.post('User/getUser').then(res => {
+ t.userInfo = res.data
+ })
+ },
+ agreeDialog1 (e) {
+ this.$refs.agreePup1.dialog = !this.$refs.agreePup1.dialog
+ },
+ agreeDialog2 (e) {
+ this.$refs.agreePup2.dialog = !this.$refs.agreePup2.dialog
+ },
+ goWebUrl (url) {
+ // #ifdef APP-PLUS
+ plus.runtime.openURL(url)
+ // #endif
+ },
+ getConfig () {
+ let t = this
+ t.$util.post('Index/getConfig').then(res => {
+ t.Config = res.data
+ })
+ },
+ wechatWork () {
+ let t = this
+ if(t.Config){
+ if(t.Config.put_open == 1){
+ uni.showModal({
+ title: '联系客服',
+ content: '请联系客服电话:' + t.Config.kefu_phone,
+ showCancel: false,
+ success: function (res) {
+ if (res.confirm) {
+ }
+ }
+ });
+ } else {
+ console.log(t.Config)
+ uni.share({
+ provider: "weixin",
+ openCustomerServiceChat: true,
+ customerUrl: t.Config.work_url, //企业微信地址
+ corpid: t.Config.work_id, //企业id
+ success: (res) => {
+ console.log("success:" + JSON.stringify(res))
+ },
+ fail: (err) => {
+ console.log("fail:" + JSON.stringify(err))
+ }
+ })
+ }
+ }
+ }
+ }
+ }
+</script>
+
+<style>
+ @import url(/static/css/my.css);
+</style>
diff --git a/pages/order/order.vue b/pages/order/order.vue
index c375a8d..6fa35fd 100644
--- a/pages/order/order.vue
+++ b/pages/order/order.vue
@@ -2,10 +2,10 @@
<view>
<view class="order-tab">
<view :class="statusIndex == 0 ? 'active' : ''" @tap="tabClick(0)">全部</view>
- <view :class="statusIndex == 2 ? 'active' : ''" @tap="tabClick(2)">待审核</view>
- <view :class="statusIndex == 3 ? 'active' : ''" @tap="tabClick(3)">待签约</view>
+ <!-- <view :class="statusIndex == 2 ? 'active' : ''" @tap="tabClick(2)">待审核</view> -->
+ <!-- <view :class="statusIndex == 3 ? 'active' : ''" @tap="tabClick(3)">待签约</view> -->
<view :class="statusIndex == 5 ? 'active' : ''" @tap="tabClick(5)">待收货</view>
- <view :class="statusIndex == 6 ? 'active' : ''" @tap="tabClick(6)">已逾期</view>
+ <!-- <view :class="statusIndex == 6 ? 'active' : ''" @tap="tabClick(6)">已逾期</view> -->
<view :class="statusIndex == 8 ? 'active' : ''" @tap="tabClick(8)">已完成</view>
<view :class="statusIndex == -3 ? 'active' : ''" @tap="tabClick(-3)">已退款</view>
</view>
diff --git a/pages/pays/pays.vue b/pages/pays/pays.vue
index 2b5dfde..85c1869 100644
--- a/pages/pays/pays.vue
+++ b/pages/pays/pays.vue
@@ -45,11 +45,11 @@
</view>
</view>
<view class="total">
- <view class="">
+ <!-- <view class="">
<text>加工费:</text>
<text class="red total-price">{{rateTotal}}</text>
<text class="cny">元</text>
- </view>
+ </view> -->
<view class="">
<text>合计:</text>
<text class="red total-price">{{payTotal}}</text>
@@ -60,20 +60,20 @@
<view class="payment">
<view class="p-title">
<text>付款方式</text>
- <view class="" v-if="payment == 1 && userInfo.edu_auth == 1" @tap="getPayTask">查看还款计划</view>
+ <!-- <view class="" v-if="payment == 1 && userInfo.edu_auth == 1" @tap="getPayTask">查看还款计划</view> -->
</view>
<view class="pay-li">
- <view class="pay-item" @tap="choosePayment(1)" v-if="Config && Config.edu_open == 1 && isFenqi == 1">
+ <!-- <view class="pay-item" @tap="choosePayment(1)" v-if="Config && Config.edu_open == 1 && isFenqi == 1">
<image class="pay-icon" src="/static/imgs/edupay.png" mode=""></image>
<text v-if="userInfo">余额支付(可用余额:{{userInfo.const}})</text>
<image class="rgt" :src="payment == 1 ? '/static/imgs/checked.png' : '/static/imgs/check.png'" mode=""></image>
- </view>
+ </view> -->
<view class="pay-item" @tap="choosePayment(2)">
<image class="pay-icon" src="/static/imgs/wxpay.png" mode=""></image>
<text>微信支付</text>
<image class="rgt" :src="payment == 2 ? '/static/imgs/checked.png' : '/static/imgs/check.png'" mode=""></image>
</view>
- <view class="pay-item">
+ <!-- <view class="pay-item">
<image class="pay-icon" src="/static/imgs/bankpay.png" mode=""></image>
<view @tap="chooseBank">
<text>银行卡支付</text>
@@ -81,7 +81,7 @@
<text v-else>(请选择银行卡 》》)</text>
</view>
<image class="rgt" :src="payment == 3 ? '/static/imgs/checked.png' : '/static/imgs/check.png'" mode="" @tap="choosePayment(3)"></image>
- </view>
+ </view> -->
</view>
</view>
<view class="authBtn-desc" v-if="userInfo && payment == 1 && userInfo.edu_auth == 1">
diff --git a/pages/profile/profile.vue b/pages/profile/profile.vue
index c2f61bd..a417ad3 100644
--- a/pages/profile/profile.vue
+++ b/pages/profile/profile.vue
@@ -56,7 +56,7 @@
<text @tap="agreeDialog2">《隐私政策》</text>
</view>
<view class="" @tap="goWebUrl('http://beian.miit.gov.cn')">APP备案号:{{Config.icp}}</view>
- <view class="" @tap="goWebUrl('http://beian.miit.gov.cn')">Copyright © {{Config.url}} All Rights Reserved.</view>
+ <view class="" @tap="goWebUrl('http://beian.miit.gov.cn')">Copyright © 乐陵力宇科技 All Rights Reserved.</view>
</view>
<agreePup ref="agreePup1" :gp="1" title="注册协议"></agreePup>
<agreePup ref="agreePup2" :gp="2" title="隐私政策"></agreePup>
@@ -110,10 +110,11 @@
})
},
logOut () {
- uni.setStorageSync('lzsc-token', null)
+ uni.setStorageSync('pig-token', null)
uni.setStorageSync('user-info', null)
uni.setStorageSync('choose-address',null)
uni.setStorageSync('choose-bank',null)
+ uni.setStorageSync('ygxLogin',null)
uni.showToast({
title: '退出成功',
icon: 'success'
diff --git a/pages/quanshou/components/heijing.vue b/pages/quanshou/components/heijing.vue
index 25f9674..4f3a6a1 100644
--- a/pages/quanshou/components/heijing.vue
+++ b/pages/quanshou/components/heijing.vue
@@ -12,15 +12,15 @@
<view style="display: flex;flex-direction: column;justify-content: center">5折购视频会员</view>
<view style="color: #2E2729;background: #F1CFB4;border-radius: 8rpx 8rpx 8rpx 8rpx;padding: 8rpx 20rpx;margin-left: 20rpx;font-size: 24rpx;">4选1 | x12次</view></view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 30rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y1.png" style="width: 108rpx;height: 108rpx;"></image>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y1.png" style="width: 108rpx;height: 108rpx;"></image>
<view style="color: #121212;font-size: 20rpx;">优酷视频</view>
<view style="font-size: 20rpx;color: #666666;">30元/月</view>
</view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">腾讯视频</view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">腾讯视频</view>
<view style="font-size: 20rpx;color: #666666;">30元/月</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">搜狐视频</view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">搜狐视频</view>
<view style="font-size: 20rpx;color: #666666;">30元/月</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">芒果TV</view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">芒果TV</view>
<view style="font-size: 20rpx;color: #666666;">30元/月</view></view>
</view>
</view>
@@ -33,13 +33,13 @@
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 30rpx 20rpx;margin: 6rpx;;flex-wrap: wrap">
- <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/imgs/quanshou/TY1.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">喜马拉雅</view><view style="font-size: 20rpx;color: #666666;">25元/月</view></view>
- <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/imgs/quanshou/TY2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">网易云</view><view style="font-size: 20rpx;color: #666666;">18元/月</view></view>
- <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/imgs/quanshou/TY3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">QQ音乐</view><view style="font-size: 20rpx;color: #666666;">18元/月</view></view>
- <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/imgs/quanshou/TY5.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">酷狗音乐</view><view style="font-size: 20rpx;color: #666666;">18元/月</view></view>
+ <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/img/quanshou/TY1.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">喜马拉雅</view><view style="font-size: 20rpx;color: #666666;">25元/月</view></view>
+ <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/img/quanshou/TY2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">网易云</view><view style="font-size: 20rpx;color: #666666;">18元/月</view></view>
+ <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/img/quanshou/TY3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">QQ音乐</view><view style="font-size: 20rpx;color: #666666;">18元/月</view></view>
+ <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 20rpx;" ><image src="/static/img/quanshou/TY5.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">酷狗音乐</view><view style="font-size: 20rpx;color: #666666;">18元/月</view></view>
- <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 0rpx;" ><image src="/static/imgs/quanshou/TY4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">蜻蜓FM</view><view style="font-size: 20rpx;color: #666666;">25元/月</view></view>
- <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 0rpx;" ><image src="/static/imgs/quanshou/TY99.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">懒人听书</view><view style="font-size: 20rpx;color: #666666;">15/月</view></view>
+ <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 0rpx;" ><image src="/static/img/quanshou/TY4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">蜻蜓FM</view><view style="font-size: 20rpx;color: #666666;">25元/月</view></view>
+ <view style="text-align: center;display:inline-block;flex: 1;width: 25%;margin-bottom: 0rpx;" ><image src="/static/img/quanshou/TY99.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">懒人听书</view><view style="font-size: 20rpx;color: #666666;">15/月</view></view>
</view>
</view>
@@ -52,9 +52,9 @@
<view style="color: #2E2729;background: #F1CFB4;border-radius: 8rpx 8rpx 8rpx 8rpx;padding: 8rpx 20rpx;margin-left: 20rpx;font-size: 24rpx;">3选1 | x12次</view></view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 30rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <image src="/static/imgs/quanshou/ZQ1.png" style="height: 148rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/ZQ2.png" style="height: 148rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/ZQ3.png" style="height: 148rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/ZQ1.png" style="height: 148rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/ZQ2.png" style="height: 148rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/ZQ3.png" style="height: 148rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
</view>
</view>
@@ -65,7 +65,7 @@
<view style="color: #2E2729;background: #F1CFB4;border-radius: 8rpx 8rpx 8rpx 8rpx;padding: 8rpx 20rpx;margin-left: 20rpx;font-size: 24rpx;">可领3次</view></view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 30rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <image src="/static/imgs/quanshou/LS1.png" style="height: 140rpx;width:100%; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LS1.png" style="height: 140rpx;width:100%; margin-bottom: 20rpx;"></image>
</view>
</view>
diff --git a/pages/quanshou/components/lanyaerji.vue b/pages/quanshou/components/lanyaerji.vue
index 39ab50f..d9f524f 100644
--- a/pages/quanshou/components/lanyaerji.vue
+++ b/pages/quanshou/components/lanyaerji.vue
@@ -4,14 +4,16 @@
data() {
return {
ls:[
- {title:"康倍星助行器", synopsis:"老人助力行走器 可推可坐", img:"/static/imgs/quanshou/SW1.png"},
- {title:"EMBERTON III", synopsis:"无线蓝牙家用户外防尘防水长续航小音响", img:"/static/imgs/quanshou/SW2.png"},
- {title:"哈曼卡顿音响", synopsis:"蓝牙音箱 环绕立体声下沉式低音炮", img:"/static/imgs/quanshou/SW3.png"},
- {title:"吉普自行车", synopsis:"山地车成人越野学生代步", img:"/static/imgs/quanshou/SW4.png"},
- {title:"富迪堡葡萄酒", synopsis:"虎爵大师干红葡萄酒", img:"/static/imgs/quanshou/SW5.png"},
- {title:"浴百家浴室柜", synopsis:"陶瓷一体盆柜洗手盆柜组合洗漱台", img:"/static/imgs/quanshou/SW6.png"},
- {title:"小牧卫浴智能马桶", synopsis:"马桶座圈加热离座冲停零压劲冲", img:"/static/imgs/quanshou/SW7.png"},
- {title:"蓝牙耳机", synopsis:"无线降噪IPX4防水", img:"/static/imgs/quanshou/SW8.png"},
+ {title:"报喜干红葡萄酒", synopsis:"报喜干红葡萄酒750ml", img:"/static/img/quanshou/3A.png"},
+ {title:"茶叶", synopsis:"茶拼装茶叶礼盒装莫兰迪高端送礼送长辈高档茶叶礼盒装", img:"/static/img/quanshou/6A.png"},
+ {title:"COACH 男包", synopsis:"男包 老花飞行包时尚百搭实用单肩斜挎包 黑色", img:"/static/img/quanshou/1.png"},
+ {title:"蓝牙耳机", synopsis:"头戴式蓝牙立体声HIFI无线耳机", img:"/static/img/quanshou/8A.png"},
+ {title:"美的冰箱 节能保鲜", synopsis:"三循环系统迷你超薄三门一流节省定頻3级能效非风冷变频", img:"/static/img/quanshou/7.png"},
+ {title:"五粮液 单瓶", synopsis:"普五八代 2022-25年 浓香型白酒 52度", img:"/static/img/quanshou/11.png"},
+ {title:"兆亮 八宝罗盘吊坠", synopsis:"兆亮黄金吊坠女足金八宝罗盘平安扣项链挂坠0.9-1克 ", img:"/static/img/quanshou/2.png"},
+ {title:"联想 27英寸显示器", synopsis:"联想拯救者 27英寸电竞显示器 2K 180Hz", img:"/static/img/quanshou/5.png"},
+ {title:"海斯曼 智能跑步机", synopsis:"海斯曼智能家用跑步机减肥静音全折叠新款 智能升级版", img:"/static/img/quanshou/4.png"},
+ {title:"小米 215L", synopsis:"米家215升小型家用电冰箱 三门三温节能安静运行", img:"/static/img/quanshou/10.png"},
]
}
},
@@ -21,31 +23,24 @@
<template>
<view>
- <view style="display: flex;flex-wrap: wrap;justify-content: space-between;">
-
- <view v-for="i in ls" :key="i.title" style="width: calc(50% - 14rpx);background: #F9F9F9;margin-bottom: 28rpx;position: relative;border-radius: 24rpx">
- <image style="height: 359.23rpx;width: 100%;border-radius: 24rpx 24rpx 0 0 " :src="i.img"></image>
- <image style="width: 68rpx;height: 72rpx;position: absolute;right: 22rpx;" src="/static/imgs/quanshou/SWLL.png"></image>
- <view style="padding: 10rpx 20rpx 20rpx 20rpx;">
- <view style="font-weight: 600;color: #333333;font-size: 32rpx;">{{ i.title }}</view>
- <view class="ellipsis">{{ i.synopsis }}</view>
+ <view style="display: flex;flex-wrap: wrap;justify-content: space-between">
+ <view v-for="i in ls" style="background: #FFFFFF;border-radius: 24rpx 24rpx 24rpx 24rpx;width: calc( 50% - 9rpx);padding: 30rpx;position: relative;margin-bottom: 20rpx;">
+ <view style="font-size: 32rpx;color: #333333;font-weight: 600;margin-bottom: 10rpx;">{{ i.title }}</view>
+ <view style="font-size: 24rpx;color: #666666;margin-bottom: 20rpx;min-height: 150rpx">{{i.synopsis}}</view>
+ <view style="text-align: right;position: absolute;right: 0;bottom: 0rpx;">
+ <image :src="i.img" style="width: 112rpx;height:96rpx;" mode="widthFix"></image>
</view>
-
+ <view style="position: absolute;left: 0;bottom: -8rpx;">
+ <image src="@/static/img/MKL.png" style="width: 174rpx;height: 50rpx;"></image>
+ </view>
</view>
</view>
-
-
+ <view style="text-align: center;color: #999999;font-size: 24rpx;margin: auto">实际以用户收到的商品为准</view>
</view>
</template>
<style scoped lang="scss">
-.ellipsis {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- font-size: 24rpx;color: #666666;margin-top: 10rpx;
-}
</style>
diff --git a/pages/quanshou/components/list.vue b/pages/quanshou/components/list.vue
index 5a26a5c..dd6bdec 100644
--- a/pages/quanshou/components/list.vue
+++ b/pages/quanshou/components/list.vue
@@ -10,20 +10,20 @@
<view class="li-bg" style="padding-bottom: 6rpx;">
<view class="ttis" style="padding: 15rpx 28rpx;font-weight: 600;font-size: 32rpx;font-size: 32rpx;">影音会员 免费4选1</view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 20rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y1.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">优酷视频</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">腾讯视频</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">搜狐视频</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/Y4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">芒果TV</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y1.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">优酷视频</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">腾讯视频</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">搜狐视频</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/Y4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">芒果TV</view></view>
</view>
</view>
<view class="li-bg" style="padding-bottom: 6rpx;">
<view class="ttis" style="padding: 15rpx 28rpx;font-weight: 600;font-size: 32rpx;font-size: 32rpx;">音频会员 5折各1次</view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 30rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/TY1.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">喜马拉雅</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/TY2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">网易云</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/TY3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">QQ音乐</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/TY4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">蜻蜓FM</view></view>
- <view style="text-align: center;flex: 1" ><image src="/static/imgs/quanshou/TY5.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">酷狗音乐</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/TY1.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">喜马拉雅</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/TY2.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">网易云</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/TY3.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">QQ音乐</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/TY4.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">蜻蜓FM</view></view>
+ <view style="text-align: center;flex: 1" ><image src="/static/img/quanshou/TY5.png" style="width: 108rpx;height: 108rpx;"></image><view style="color: #121212;font-size: 20rpx;">酷狗音乐</view></view>
</view>
</view>
@@ -31,11 +31,11 @@
<view class="ttis" style="padding: 15rpx 28rpx;font-weight: 600;font-size: 32rpx;font-size: 32rpx;">茶饮/外卖券 5折起各1次</view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 20rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <image src="/static/imgs/quanshou/CY1.png" style="height: 146rpx;width: 120rpx;"></image>
- <image src="/static/imgs/quanshou/CY2.png" style="height: 146rpx;width: 120rpx;"></image>
- <image src="/static/imgs/quanshou/CY3.png" style="height: 146rpx;width: 120rpx;"></image>
- <image src="/static/imgs/quanshou/CY4.png" style="height: 146rpx;width: 120rpx;"></image>
- <image src="/static/imgs/quanshou/CY5.png" style="height: 146rpx;width: 120rpx;"></image>
+ <image src="/static/img/quanshou/CY1.png" style="height: 146rpx;width: 120rpx;"></image>
+ <image src="/static/img/quanshou/CY2.png" style="height: 146rpx;width: 120rpx;"></image>
+ <image src="/static/img/quanshou/CY3.png" style="height: 146rpx;width: 120rpx;"></image>
+ <image src="/static/img/quanshou/CY4.png" style="height: 146rpx;width: 120rpx;"></image>
+ <image src="/static/img/quanshou/CY5.png" style="height: 146rpx;width: 120rpx;"></image>
</view>
</view>
@@ -44,21 +44,21 @@
<view class="ttis" style="padding: 15rpx 28rpx;font-weight: 600;font-size: 32rpx;font-size: 32rpx;">打车出行 6折起各1次</view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 30rpx 20rpx;margin: 6rpx;display: flex;justify-content: space-between">
- <image src="/static/imgs/quanshou/DC1.png" style="height: 146rpx;width: 200rpx;"></image>
- <image src="/static/imgs/quanshou/DC2.png" style="height: 146rpx;width: 200rpx;"></image>
- <image src="/static/imgs/quanshou/DC3.png" style="height: 146rpx;width: 200rpx;"></image>
+ <image src="/static/img/quanshou/DC1.png" style="height: 146rpx;width: 200rpx;"></image>
+ <image src="/static/img/quanshou/DC2.png" style="height: 146rpx;width: 200rpx;"></image>
+ <image src="/static/img/quanshou/DC3.png" style="height: 146rpx;width: 200rpx;"></image>
</view>
</view>
<view class="li-bg" style="padding-bottom: 6rpx;">
<view class="ttis" style="padding: 15rpx 28rpx;font-weight: 600;font-size: 32rpx;font-size: 32rpx;">乐享生活 部分免费各1次</view>
<view style="background: #FFFFFF;border-radius: 24rpx ;padding: 20rpx 20rpx 0 20rpx;margin: 6rpx;display: flex;justify-content: space-between;flex-wrap: wrap">
- <image src="/static/imgs/quanshou/LX1.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/LX2.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/LX3.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/LX4.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/LX5.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
- <image src="/static/imgs/quanshou/LX6.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LX1.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LX2.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LX3.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LX4.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LX5.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
+ <image src="/static/img/quanshou/LX6.png" style="height: 96rpx;width: 200rpx; margin-bottom: 20rpx;"></image>
</view>
</view>
</view>
diff --git a/pages/quanshou/components/tehuichongzhi.vue b/pages/quanshou/components/tehuichongzhi.vue
index ab84bcf..95c1c05 100644
--- a/pages/quanshou/components/tehuichongzhi.vue
+++ b/pages/quanshou/components/tehuichongzhi.vue
@@ -1,255 +1,38 @@
<script>
-export default {
- name: "tehuichongzhi",
- props: {
- VIP: {
- type: Number,
- default: 1
- }
- },
+export default {
+ name: "tehuichongzhi"
}
</script>
<template>
<view>
-
-
- <view v-if="VIP==1">
- <view style="padding: 0 40rpx 0rpx 40rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 音频会员五选一
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;">
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/Y51.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">喜马拉雅</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/Y52.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">网易云</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/Y53.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">QQ音乐</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/Y54.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">蜻蜓FM</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/Y55.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">酷狗音乐</view>
- </view>
- </view>
- </view>
-
- <view style="height: 10rpx;background: #F7F7F7;"></view>
-
-
- <view style="padding: 40rpx;padding-bottom: 20rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 视频会员八选一
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;flex-wrap: wrap">
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S81.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">优酷</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S82.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">腾讯视频</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S83.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">搜狐视频</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S84.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">芒果TV</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S85.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">爱奇艺</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S86.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">Bilibili</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S87.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">斗鱼直播</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/S89.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">虎牙直播</view>
- </view>
- </view>
- </view>
- <view style="height: 10rpx;background: #F7F7F7;"></view>
- <view style="padding: 40rpx;padding-bottom: 20rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 外卖会员四选一
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;flex-wrap: wrap">
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/W41.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">美团</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/W42.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">饿了么</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/W43.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">京东外卖</view>
- </view>
- <view class="w25">
- <image style="width: 108rpx;height: 108rpx;" src="/static/imgs/quanshou/W44.png"></image>
- <view style="text-align: center ;color: #121212;font-size: 20rpx;">盒马</view>
- </view>
-
- </view>
- </view>
- <view style="height: 10rpx;background: #F7F7F7;"></view>
-
- <view style="padding: 40rpx;padding-bottom:20rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 生活交通代金券
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;flex-wrap: wrap">
- <view class="w33"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH1.png"></image></view>
- <view class="w33"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH2.png"></image></view>
- <view class="w33"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH3.png"></image></view>
- <view class="w33"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH4.png"></image></view>
- <view class="w33"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH5.png"></image></view>
- <view class="w33"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH6.png"></image></view>
- <view class="w33" style="margin-bottom: 0;"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH7.png"></image></view>
- <view class="w33" style="margin-bottom: 0;"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH8.png"></image></view>
- <view class="w33" style="margin-bottom: 0;"><image style="width: 200rpx;height: 148rpx;" src="/static/imgs/quanshou/SH9.png"></image></view>
- </view>
- </view>
+ <view style="margin-bottom: 20rpx;">
+ <image style="width: 22rpx;height: 22rpx;" src="/static/img/quanshou/Star.png"></image>
+ <text style="font-weight: 600;font-size: 32rpx;color: #333333;margin-left: 10rpx;">特惠充值</text>
+ <text style="font-size: 28rpx;color: #666666;margin-left: 20rpx;">低至5折起,越充越省钱!</text>
</view>
-
- <view v-if="VIP==2">
- <view style="padding: 0 40rpx 0rpx 40rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 粉钻VIP特权
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;justify-content: space-between;flex-wrap: wrap">
- <view class="w50">
- <view><image style="width: 100rpx;height: 100rpx;" src="/static/imgs/quanshou/FZV1.png"></image></view>
- <view style="margin-left: 10rpx;display: flex;flex-direction: column;justify-content: center ;">
- <view style="font-size: 28rpx;color: #5B320D;font-weight: 600;min-height: 40rpx">优酷季卡</view>
- <view style="font-size: 24rpx;color: #5B320D;">价值¥198</view>
- </view>
- </view>
- <view class="w50">
- <view><image style="width: 100rpx;height: 100rpx;" src="/static/imgs/quanshou/FZV2.png"></image></view>
- <view style="margin-left: 10rpx;display: flex;flex-direction: column;justify-content: center ;">
- <view style="font-size: 28rpx;color: #5B320D;font-weight: 600;min-height: 40rpx">爱奇艺季卡</view>
- <view style="font-size: 24rpx;color: #5B320D;">价值¥88</view>
- </view>
- </view>
- <view class="w50">
- <view><image style="width: 100rpx;height: 100rpx;" src="/static/imgs/quanshou/FZV3.png"></image></view>
- <view style="margin-left: 10rpx;display: flex;flex-direction: column;justify-content: center ;">
- <view style="font-size: 28rpx;color: #5B320D;font-weight: 600;min-height: 40rpx">QQ音乐季卡</view>
- <view style="font-size: 24rpx;color: #5B320D;">价值¥198</view>
- </view>
- </view>
- <view class="w50">
- <view><image style="width: 100rpx;height: 100rpx;" src="/static/imgs/quanshou/FZV4.png"></image></view>
- <view style="margin-left: 10rpx;display: flex;flex-direction: column;justify-content: center ;">
- <view style="font-size: 28rpx;color: #5B320D;font-weight: 600;min-height: 40rpx">黑胶季卡</view>
- <view style="font-size: 24rpx;color: #5B320D;">价值¥145</view>
- </view>
- </view>
- <view class="w50">
- <view><image style="width: 100rpx;height: 100rpx;" src="/static/imgs/quanshou/FZV5.png"></image></view>
- <view style="margin-left: 10rpx;display: flex;flex-direction: column;justify-content: center ;">
- <view style="font-size: 28rpx;color: #5B320D;font-weight: 600;min-height: 40rpx">芒果TV季卡</view>
- <view style="font-size: 24rpx;color: #5B320D;">价值¥99</view>
- </view>
- </view>
- <view class="w50">
- <view><image style="width: 100rpx;height: 100rpx;" src="/static/imgs/quanshou/FZV6.png"></image></view>
- <view style="margin-left: 10rpx;display: flex;flex-direction: column;justify-content: center ;">
- <view style="font-size: 28rpx;color: #5B320D;font-weight: 600;min-height: 40rpx">得到季卡</view>
- <view style="font-size: 24rpx;color: #5B320D;">价值¥1168</view>
- </view>
- </view>
- </view>
- </view>
- <view style="height: 10rpx;background: #F7F7F7;"></view>
- <view style="padding: 40rpx;padding-bottom: 20rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 红包免费领
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;flex-wrap: wrap">
- <view class="w33" style="margin-bottom: 20rpx;"><image style="width: 208rpx;height: 178rpx;" src="/static/imgs/quanshou/KB1.png"></image></view>
- <view class="w33" style="margin-bottom: 20rpx;"><image style="width: 208rpx;height: 178rpx;" src="/static/imgs/quanshou/KB2.png"></image></view>
- <view class="w33" style="margin-bottom: 20rpx;"><image style="width: 208rpx;height: 178rpx;" src="/static/imgs/quanshou/KB3.png"></image></view>
- <view class="w33" style="margin-bottom: 20rpx;"><image style="width: 208rpx;height: 178rpx;" src="/static/imgs/quanshou/KB4.png"></image></view>
- <view class="w33" style="margin-bottom: 20rpx;"><image style="width: 208rpx;height: 178rpx;" src="/static/imgs/quanshou/KB5.png"></image></view>
- <view class="w33" style="margin-bottom: 20rpx;"><image style="width: 208rpx;height: 178rpx;" src="/static/imgs/quanshou/KB6.png"></image></view>
- </view>
- </view>
- <view style="height: 10rpx;background: #F7F7F7;"></view>
-
- <view style="padding: 40rpx;padding-bottom: 0rpx;">
- <view style="text-align: center;color: #333333;font-size: 32rpx;font-weight: 600;margin-bottom: 34rpx;">
- <image style="width: 22rpx;height: 22rpx;margin-right: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- 专属特惠
- <image style="width: 22rpx;height: 22rpx;margin-left: 58rpx;" src="/static/imgs/quanshou/Star.png"></image>
- </view>
- <view style="display: flex;flex-wrap: wrap">
- <view class="w100" style="margin-bottom: 20rpx;"><image style="width: 100%;height: 170rpx;" src="/static/imgs/quanshou/ZC1.png"></image></view>
- <view class="w100" style="margin-bottom: 20rpx;"><image style="width: 100%;height: 170rpx;" src="/static/imgs/quanshou/ZC2.png"></image></view>
- <view class="w100" style="margin-bottom: 0rpx;"><image style="width: 100%;height: 170rpx;" src="/static/imgs/quanshou/ZC3.png"></image></view>
-
- </view>
- </view>
-
+ <view style="display: flex;flex-wrap: wrap;justify-content: space-between">
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C1.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">爱奇艺</view><view style="font-size: 20rpx;color: #666666;">7折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C2.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">芒果TV</view><view style="font-size: 20rpx;color: #666666;">4.9折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C3.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">腾讯视频</view><view style="font-size: 20rpx;color: #666666;">3.8折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C4.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">优酷</view><view style="font-size: 20rpx;color: #666666;">3折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C5.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">网易云</view><view style="font-size: 20rpx;color: #666666;">5.9折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C6.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">QQ音乐</view><view style="font-size: 20rpx;color: #666666;">92折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C7.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">酷狗</view><view style="font-size: 20rpx;color: #666666;">4.9折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C8.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">哔哩哔哩</view><view style="font-size: 20rpx;color: #666666;">6.6折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C9.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333; margin: 10rpx 0 5rpx 0;">美团外卖</view><view style="font-size: 20rpx;color: #666666;">8折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C10.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333;margin: 10rpx 0 5rpx 0;">饿了么</view><view style="font-size: 20rpx;color: #666666;">5.9折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C11.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333;margin: 10rpx 0 5rpx 0;">瑞幸咖啡</view><view style="font-size: 20rpx;color: #666666;">6.5折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C12.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333;margin: 10rpx 0 5rpx 0;">蜜雪冰城</view><view style="font-size: 20rpx;color: #666666;">9折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C13.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333;margin: 10rpx 0 5rpx 0;">酷我</view><view style="font-size: 20rpx;color: #666666;">9.3折起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C14.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333;margin: 10rpx 0 5rpx 0;">QQ阅读</view><view style="font-size: 20rpx;color: #666666;">7起</view></view>
+ <view style="text-align: center; flex: 0 0 calc(20%);margin-top: 22rpx;"><image src="/static/img/quanshou/C15.png" style="width: 88rpx;height: 88rpx;"></image><view style="font-size: 24rpx;color: #333333;margin: 10rpx 0 5rpx 0;">文心一言</view><view style="font-size: 20rpx;color: #666666;">6折起</view></view>
</view>
-
-
-
</view>
</template>
<style scoped lang="scss">
-.w25 {
- width: 20%;
- text-align: center;
- margin-bottom: 24rpx;
-}
-.w33{
- width: 33%;
- text-align: center;
- margin-bottom: 15rpx;
-}
-.w50{
- width: calc(50% - 20rpx);
- background: #FFEAF9;border-radius: 24rpx;
- padding: 20rpx;
- display: flex;
- margin-bottom: 20rpx;
-}
-.w100{
- width: 100%;
-}
+
</style>
diff --git a/pages/quanshou/components/xianlianghaojjuab.vue b/pages/quanshou/components/xianlianghaojjuab.vue
index da2bf60..9776c9d 100644
--- a/pages/quanshou/components/xianlianghaojjuab.vue
+++ b/pages/quanshou/components/xianlianghaojjuab.vue
@@ -7,18 +7,18 @@
<template>
<view>
<view style="margin-bottom: 20rpx;">
- <image style="width: 22rpx;height: 22rpx;" src="/static/imgs/quanshou/Star.png"></image>
+ <image style="width: 22rpx;height: 22rpx;" src="/static/img/quanshou/Star.png"></image>
<text style="font-weight: 600;font-size: 32rpx;color: #333333;margin-left: 10rpx;">限量好券</text>
<text style="font-size: 28rpx;color: #666666;margin-left: 20rpx;">大额立减,省钱利器</text>
</view>
<view style="display: flex;flex-wrap: wrap;justify-content: space-between">
- <view style="width: calc( 50% - 9rpx)"><image src="/static/imgs/quanshou/H1.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
- <view style="width: calc( 50% - 9rpx)"><image src="/static/imgs/quanshou/H2.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
- <view style="width: calc( 50% - 9rpx)"><image src="/static/imgs/quanshou/H3.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
- <view style="width: calc( 50% - 9rpx)"><image src="/static/imgs/quanshou/H4.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
- <view style="width: calc( 50% - 9rpx)"><image src="/static/imgs/quanshou/H5.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
- <view style="width: calc( 50% - 9rpx)"><image src="/static/imgs/quanshou/H6.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
+ <view style="width: calc( 50% - 9rpx)"><image src="/static/img/quanshou/H1.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
+ <view style="width: calc( 50% - 9rpx)"><image src="/static/img/quanshou/H2.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
+ <view style="width: calc( 50% - 9rpx)"><image src="/static/img/quanshou/H3.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
+ <view style="width: calc( 50% - 9rpx)"><image src="/static/img/quanshou/H4.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
+ <view style="width: calc( 50% - 9rpx)"><image src="/static/img/quanshou/H5.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
+ <view style="width: calc( 50% - 9rpx)"><image src="/static/img/quanshou/H6.png" style="width: 100%;height: 270rpx;margin-bottom: 10rpx;"></image></view>
</view>
diff --git a/pages/quanshou/index.vue b/pages/quanshou/index.vue
index f1d2c75..f54a8c2 100644
--- a/pages/quanshou/index.vue
+++ b/pages/quanshou/index.vue
@@ -1,46 +1,41 @@
<script>
-import tehuichongzhi from "@/pages/quanshou/components/tehuichongzhi.vue"; //虚拟
-import lanyaerji from "@/pages/quanshou/components/lanyaerji.vue"; // 实物
-
export default {
name: "index",
components: {
- tehuichongzhi,
- lanyaerji,
},
data() {
return {
tips:'',
active: 0,
- VIP: 1, // 1黄金 2铂金 3黑金
+ VIP: 2, // 1黄金 2铂金 3黑金
lst:[],
vipConfig: {
1: {
- bg: "/static/imgs/quanshou/TBBG1.png",
- box: "/static/imgs/quanshou/NEWBOX1.png",
- title: "/static/imgs/quanshou/VIPTXT1.png",
- level: "/static/imgs/quanshou/VIP1.png",
+ bg: "/static/img/quanshou/TBBG1.png",
+ box: "/static/img/quanshou/VIPBOX1.png",
+ title: "/static/img/quanshou/VIPTXT1.png",
+ level: "/static/img/quanshou/VIPLVL1.png",
textClass: "vip1-text",
progressBg: "#BBA767",
growText: " 88800/100000",
benefitText: "297.00"
},
2: {
- bg: "/static/imgs/quanshou/TBBG2.png",
- box: "/static/imgs/quanshou/NEWBOX2.png",
- title: "/static/imgs/quanshou/VIPTXT2.png",
- level: "/static/imgs/quanshou/VIP2.png",
+ bg: "/static/img/quanshou/TBBG2.png",
+ box: "/static/img/quanshou/VIPBOX2.png",
+ title: "/static/img/quanshou/VIPTXT2.png",
+ level: "/static/img/quanshou/VIPLVL2.png",
textClass: "vip2-text",
progressBg: "#8094A6",
growText: " 88800/100000",
benefitText: "594.00"
},
3: {
- bg: "/static/imgs/quanshou/TBBG3.png",
- box: "/static/imgs/quanshou/VIPBOX3.png",
- title: "/static/imgs/quanshou/VIPTXT3.png",
- level: "/static/imgs/quanshou/VIPLVL3.png",
+ bg: "/static/img/quanshou/TBBG3.png",
+ box: "/static/img/quanshou/VIPBOX3.png",
+ title: "/static/img/quanshou/VIPTXT3.png",
+ level: "/static/img/quanshou/VIPLVL3.png",
textClass: "vip3-text",
progressBg: "#7B776E",
growText: " 88800/100000",
@@ -48,6 +43,7 @@
}
},
total: 0,
+ isAgree: 0,
};
},
computed: {
@@ -58,12 +54,9 @@
onLoad(pro) {
const _this = this;
+ this.isAgree = uni.getStorageSync('boughtEquity')
uni.request({
- url: _this.$webHost+'/api/v2/customer/getRyxBenefitsRecordList?phone='+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
+ url: _this.$webHost+'/api/v2/customer/getRyxBenefitsRecordList?phone='+uni.getStorageSync('phone')+'&appId='+'47be9559-6b1b-42dd-84ed-25d028065013',
success: function (res) {
if(res.data.code==200){
_this.lst = res.data.data;
@@ -76,6 +69,7 @@
_this.total += parseFloat(item.amount);
}
})
+ console.log('_this.lst',_this.lst)
_this.total = Intl.NumberFormat('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
@@ -88,6 +82,12 @@
})
},
methods: {
+ goHome(){
+ uni.switchTab({
+ url: '/pages/index/index'
+ });
+ // uni.navigateBack()
+ },
numbs(period) {
const obj ={
1:'一',
@@ -99,24 +99,22 @@
toHuiyuan(){
const _this = this;
uni.request({
- url: _this.$webHost+'/api/v2/memberEquity/getUrlByShop/'+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
+ url: _this.$webHost+'/api/v2/memberEquity/getUrlByShop/'+uni.getStorageSync('phone'),
success: function (res) {
if(res.data.code==200){
uni.request( {
- url: _this.$webHost+'/api/v2/memberEquity/clickUrlByShop/'+_this.$util.getUserInfo().phone,
+ url: _this.$webHost+'/api/v2/memberEquity/clickUrlByShop/'+uni.getStorageSync('phone'),
})
uni.navigateTo( {
- url: '/pages/web/index?webUel=' + encodeURIComponent(res.data.data.url)
+ url: '/pages/users/web_page/index?webUel=' + encodeURIComponent(res.data.data.url)
})
} else {
- uni.showToast( {
- title: res.data.data || res.data.msg,
- icon: 'none'
- })
+ _this.tips = res.data.data || res.data.msg;
+ _this.$refs.life.open()
+ // uni.showToast( {
+ // title: res.data.data || res.data.msg,
+ // icon: 'none'
+ // })
}
}
})
@@ -126,11 +124,7 @@
getTips(){
const _this = this;
uni.request({
- url: _this.$webHost+'/api/v2/shop/findExpressNo?phone='+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
+ url: _this.$webHost+'/api/v2/shop/findExpressNo?phone='+uni.getStorageSync('phone'),
success: function (res) {
if(res.data.code==200){
_this.tips = res.data.data;
@@ -150,68 +144,175 @@
</script>
<template>
- <view style="background: white;">
+ <view style="background: url('/static/img/qsBg.png') no-repeat top center; background-size: 100%;background-color: #210E0E;height: 100%;">
<!-- 顶部 VIP 卡片 -->
- <view class="vip-box" :style="{ backgroundImage: `url(${currentVip.box})` }" >
- <view style="position: relative">
- <image :src="currentVip.level" style="height: 200rpx;width: 100%;"></image>
- <view style="position: absolute;top: 120rpx;left: 48rpx;color: #754200;font-size: 28rpx;" :class="VIP==1?'vip1-num':'vip2-num'">已购会员{{ total }}</view>
- </view>
- <view class="card-wrap" style="margin-top: 38rpx;">
- <view class="card-item" v-for="i in lst" :class="i.isBuy?'card-item-buy':''">
-<!-- <view class="card-title">{{numbs(i.period)}}期会员</view>-->
- <view class="card-title">{{i.buyDate}}</view>
- <view class="card-price">
- <text>¥</text>
- <text class="card-num" >{{ i.amount }}</text>
+<!-- <view-->
+<!-- class="vip-bg"-->
+<!-- :style="{ backgroundImage: `url(${currentVip.bg})` }"-->
+<!-- >-->
+<!-- <view-->
+<!-- class="vip-box"-->
+<!-- :style="{ backgroundImage: `url(${currentVip.box})` }"-->
+<!-- >-->
+<!-- <view>-->
+<!-- <image :src="currentVip.title" style="width: 160rpx;height: 45rpx;" />-->
+<!-- </view>-->
+
+<!-- <view class="gradient-text big-text" :class="currentVip.textClass">-->
+<!-- 成长值 {{ currentVip.growText }}-->
+<!-- </view>-->
+
+<!-- <view-->
+<!-- class="progress-bar"-->
+<!-- :style="{ background: currentVip.progressBg }"-->
+<!-- >-->
+<!-- <view class="progress-inner" v-if="VIP==1" style="width: 29.7%"/>-->
+<!-- <view class="progress-inner" v-if="VIP==2" style="width: 59.4%"/>-->
+<!-- <view class="progress-inner" v-if="VIP==3" style="width: 89.1%"/>-->
+<!-- </view>-->
+
+<!-- <view class="gradient-text small-text" :class="currentVip.textClass">-->
+<!-- 已购会员{{ total }}-->
+<!-- </view>-->
+
+<!-- <image-->
+<!-- :src="currentVip.level"-->
+<!-- style="width: 210rpx;height: 210rpx;position: absolute;top: -70rpx;right: 0;"-->
+<!-- />-->
+<!-- </view>-->
+<!-- </view>-->
+ <!-- 新版 -->
+ <view style="padding: 0 30rpx;">
+ <!-- 返回 -->
+ <view class="head" style="position: relative;height: 90rpx;">
+ <view style="display: flex;justify-content: flex-start;position: absolute;left: 0;top: 30rpx;z-index: 9;color: #FFFFFF;">
+ <view @tap="goHome" id="home" class="home acea-row row-center-wrapper iconfont icon-fanhui2 h5_back" style="background: url('/static/img/back.png') no-repeat top center; background-size: 100%;width: 40rpx;height: 40rpx;">
+ <!-- <view @tap="goHome" id="home" class="home acea-row row-center-wrapper iconfont icon-fanhui2 h5_back icon-shouye4" :style="{ height: '80rpx',lineHeight: '80rpx'}" >-->
</view>
- <view class="card-date" style="height: 40rpx"> </view>
- <!-- <view class="card-date">{{ i.buyDate }}</view>-->
- <view class="has-buy">{{ i.isBuy?'已扣款' : '待扣款' }}</view>
+ </view>
+ </view>
+ <!-- 尊享会员年卡 -->
+ <view style="background: url('/static/img/qyBg2.png') no-repeat top center; background-size: 100%;width: 100%;height: 300rpx;padding: 40rpx;margin-top: 230rpx;">
+ <view style="font-size: 44rpx;font-weight: bold;background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">尊享会员年卡</view>
+ <view style="font-size: 28rpx;font-weight: 400;background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">会员预计最高节省1359.27元</view>
+ <view style="display: flex;margin-top: 30rpx;align-items: self-end;">
+ <view v-if="lst.length" style="font-size: 24rpx;width: 70%;">
+ <p style="background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;font-size: 36rpx;">¥{{lst[0].amount.toFixed(2)}} <span style="font-size: 24rpx;">{{lst[0].isBuy?'已扣款' : '待扣款'}}</span></p>
+ <p style="background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">{{lst[0].buyDate}}</p>
+ </view>
+ <view style="width: 164rpx;height: 58rpx;text-align: center;line-height: 58rpx;background-color: #FFEEE7;border-radius: 18rpx;margin-top: 40rpx;display: flex;justify-content: center;align-items: center;">
+<!-- <checkbox-group activeColor="#fff" @change='isAgree=!isAgree' style="">-->
+<!-- <checkbox disabled :checked="isAgree ? true : false" iconColor="#794007" iconSize="30" size="38" :name="1" shape="circle">{{isAgree?'已开通':'未开通'}}-->
+<!-- </checkbox>-->
+<!-- </checkbox-group>-->
+ <checkbox-group activeColor="#fff" @change='isAgree=!isAgree' style="">
+ <checkbox disabled checked="true" iconColor="#794007" iconSize="30" size="38" :name="1" shape="circle">{{isAgree?'已开通':'已开通'}}
+ </checkbox>
+ </checkbox-group>
+ <!-- <view style="color: #210E0E;font-size: 24rpx;"></view> -->
+ </view>
+ </view>
+ </view>
+ <!-- 专享会员生活权益 -->
+ <view>
+ <view class="content-title" style="text-align: center;display: flex;align-items: center;justify-content: center;margin-top: 50rpx;margin-bottom: 40rpx;">
+ <image style="width: 111rpx;height: 8rpx;" src="/static/img/qsLeft.png"></image>
+ <view style="font-size: 36rpx;color: #FFEEE7;margin: 0 20rpx;">专享会员生活权益</view>
+ <image style="width: 111rpx;height: 8rpx;" src="/static/img/qsRight.png"></image>
+ </view>
+ <view class="life" style="background: url('/static/img/qsLife.png') no-repeat top center; background-size: 100%;width: 100%;height: 1506rpx;">
+
+ </view>
+ <view style="width: 690rpx;margin-top: 40rpx;text-align: center;">
+ <button @click="toHuiyuan" style="height: 100rpx;padding: 10rpx 0;color: #370606;border-radius: 50rpx;font-size: 34rpx;background-image: linear-gradient(to right, #FFEAE2, #FF6F55);">兑换生活权益</button>
+ <view style="color: #FFFFFF;font-size: 24rpx;padding-top: 20rpx;padding-bottom: 10rpx;">以上权益仅为预览展示,实际以兑换页面为准</view>
+ </view>
+ <view class="content-title" style="text-align: center;display: flex;align-items: center;justify-content: center;margin-top: 50rpx;margin-bottom: 40rpx;">
+ <image style="width: 111rpx;height: 8rpx;" src="/static/img/qsLeft.png"></image>
+ <view style="font-size: 36rpx;color: #FFEEE7;margin: 0 20rpx;">专享会员实物权益</view>
+ <image style="width: 111rpx;height: 8rpx;" src="/static/img/qsRight.png"></image>
+ </view>
+ <view class="physical" style="background: url('/static/img/qsPhysical.png') no-repeat top center; background-size: 100%;width: 100%;height: 1580rpx;">
+
+ </view>
+ <view style="width: 690rpx;margin-top: 40rpx;text-align: center;">
+ <button @click="getTips" style="height: 100rpx;padding: 10rpx 0;color: #370606;border-radius: 50rpx;font-size: 34rpx;background-image: linear-gradient(to right, #FFEAE2, #FF6F55);">查询实物权益</button>
+ <view style="color: #FFFFFF;font-size: 24rpx;padding-top: 20rpx;padding-bottom: 40rpx;">以上权益仅为预览展示,实际以商城发出为准</view>
</view>
</view>
</view>
<!-- 会员购买 + 权益部分 -->
- <view class="content-wrap">
+<!-- <view class="content-wrap">-->
+<!-- <view class="card-wrap">-->
+<!-- <view class="card-item" v-for="i in lst">-->
+<!-- <view class="card-title">{{numbs(i.period)}}期会员</view>-->
+<!-- <view class="card-price">-->
+<!-- <text>¥</text>-->
+<!-- <text class="card-num">{{ i.amount }}</text>-->
+<!-- <text>/{{i.dateType}}</text>-->
+<!-- </view>-->
+<!-- <view class="card-date">{{ i.buyDate }}</view>-->
+<!-- <view class="has-buy">{{ i.isBuy?'已扣款' : '待扣款' }}</view>-->
+<!-- </view>-->
+<!-- </view>-->
+<!-- <view class="tab-wrap">-->
+<!-- <text @click="active=0" :class="active==0?'active':''">实物权益</text>-->
+<!-- <text @click="active=1" :class="active==1?'active':''">虚拟权益</text>-->
+<!-- </view>-->
- <view class="tab-wrap">
- <text @click="active=0" :class="active==0?'active':''">实物权益</text>
- <text @click="active=1" :class="active==1?'active':''">虚拟权益</text>
- </view>
+<!-- <view class="note" style="margin-bottom: 20rpx;" v-if="active==0">页面展示为实物样例,实际以用户收到的商品为准</view>-->
- <view class="note" style="margin-bottom: 20rpx;text-align: center" v-if="active==0">页面展示为实物样例,实际以用户收到的商品为准</view>
+<!-- <lanyaerji v-if="active==0" />-->
- <lanyaerji v-if="active==0" style="margin:0 30rpx;"/>
+<!-- <template v-else>-->
+<!-- <xianlianghaojjuab v-show="VIP==1" />-->
+<!-- <tehuichongzhi v-show="VIP==1" style="margin-top: 50rpx;" />-->
+<!-- <list v-show="VIP==2" style="margin-top: 10rpx;" />-->
+<!-- <heijing v-show="VIP==3" style="margin-top: 10rpx;" />-->
+<!-- </template>-->
- <tehuichongzhi v-else :VIP="VIP" style="margin-top: 50rpx;" />
-
- <view style="text-align: center;margin: 30rpx;">
- <button class="exchange-btn" v-if="active==0" @click="getTips">我要查物流</button>
- <button class="exchange-btn" v-else @click="toHuiyuan">我要兑换</button>
- <view class="note" v-if="active==0">* 页面为实物样例,实际用户收到的商品为准</view>
- <view class="note" v-else>* 页面为虚拟权益预览,实际用户以当日兑换内容为准</view>
- </view>
- </view>
+<!-- <view style="margin-top: 30rpx;text-align: center">-->
+<!-- <button class="exchange-btn" v-if="active==0" @click="getTips">查询物流单号</button>-->
+<!-- <button class="exchange-btn" v-else @click="toHuiyuan">兑换我的虚拟权益</button>-->
+<!-- <view class="note" v-if="active==0"></view>-->
+<!-- <view class="note" v-else>* 虚拟权益以实际兑换为准</view>-->
+<!-- </view>-->
+<!-- </view>-->
<uni-popup type="center" ref="comment" >
- <view style="background-image: url('/static/imgs/BOXSDF.png');border-radius: 48rpx;padding: 28rpx ;width: 650rpx;background-repeat: no-repeat;background-size: 100% 100%">
- <view style="text-align: center;padding-top: 30rpx;" >
- <image src="/static/imgs/yifa.png" style="width:372rpx;height: 240rpx;"></image>
+ <view style="border-radius: 48rpx;padding: 28rpx 0;width: 538rpx;background: #FFFFFF;padding-top: 0;">
+ <view style="background-image: linear-gradient(to bottom, #FFB8B8, #FFFBFC);width: 100%;height: 200rpx;border-radius: 48rpx;">
+ <view style="text-align: center;padding-top: 30rpx;" >
+ <image src="/static/img/yifa.png" style="width: 370rpx;height: 274rpx;"></image>
+ </view>
</view>
- <view style="font-size: 32rpx;font-weight: 600;color: #333333;text-align: center;margin-top: 34rpx;margin-bottom: 80rpx;padding: 0 28rpx;">
+ <view style="font-size: 32rpx;font-weight: 600;color: #333333;text-align: center;margin-top: 134rpx;margin-bottom: 80rpx;padding: 0 28rpx;">
<view>{{ tips }}</view>
</view>
- <view @click="$refs.comment.close()" style="font-weight: 500;font-size: 32rpx;color: #fff;text-align: center;padding: 24rpx 28rpx;border-top: 2rpx solid #EEEEEE;background: #FF1472;border-radius: 40rpx ;">好的</view>
+ <view @click="$refs.comment.close()" style="font-weight: 500;font-size: 32rpx;color: #ffffff;text-align: center;padding: 24rpx 28rpx;border-top: 2rpx solid #EEEEEE;background-color: #EE2626;margin-left: 30rpx;margin-right: 30rpx;border-radius: 56rpx">好的</view>
+ </view>
+ </uni-popup>
+
+ <uni-popup type="center" ref="life" >
+ <view style="border-radius: 48rpx;padding: 28rpx 0;width: 538rpx;background: #FFFFFF;padding-top: 0;">
+ <view style="background-image: linear-gradient(to bottom, #FFB8B8, #FFFBFC);width: 100%;height: 200rpx;border-radius: 48rpx;">
+ <view style="text-align: center;padding-top: 30rpx;">
+ <image src="/static/img/qsLifeIcon.png" style="width: 370rpx;height: 238rpx;"></image>
+ </view>
+ </view>
+ <view style="font-size: 32rpx;font-weight: 600;color: #333333;text-align: center;margin-top: 134rpx;margin-bottom: 80rpx;padding: 0 28rpx;">
+ <view>{{ tips }}</view>
+ </view>
+ <view @click="$refs.life.close()" style="font-weight: 500;font-size: 32rpx;color: #ffffff;text-align: center;padding: 24rpx 28rpx;border-top: 2rpx solid #EEEEEE;background-color: #EE2626;margin-left: 30rpx;margin-right: 30rpx;border-radius: 56rpx">好的</view>
</view>
</uni-popup>
</view>
</template>
<style scoped lang="scss">
-
/* 顶部 VIP 样式 */
.vip-bg {
background-size: 100%;
@@ -220,7 +321,12 @@
padding-bottom: 150rpx;
}
.vip-box {
- background-repeat: no-repeat;padding:20rpx 30rpx 60rpx 30rpx;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ height: 280rpx;
+ margin: 0 30rpx;
+ padding: 35rpx;
+ position: relative;
}
.gradient-text {
-webkit-background-clip: text;
@@ -269,11 +375,11 @@
/* 内容区 */
.content-wrap {
- padding: 30rpx 0;
- background: #fff;
- border-radius: 50rpx 50rpx 0 0;
+ padding: 30rpx;
+ background: #F4F4F4;
+ border-radius: 24rpx 24rpx 0 0;
position: relative;
- top: -60rpx;
+ top: -100rpx;
}
.card-wrap {
display: flex;
@@ -281,12 +387,12 @@
margin-bottom: 40rpx;
}
.card-item {
- background: #f5f5f5;
+ background: #fff;
border-radius: 24rpx;
padding-top: 60rpx;
text-align: center;
- //width: calc(50% - 14rpx);
- width: 100%;
+ padding-bottom: 48rpx;
+ width: calc(50% - 15rpx);
position: relative;
}
.card-title {
@@ -305,41 +411,23 @@
margin: 0 8rpx;
}
.card-date {
- font-size: 24rpx;
- margin-top: 28rpx;
- border-radius: 0rpx 0rpx 24rpx 24rpx;
- background: #fff;
- //background: #EAEAEA;
- color: #666666;
- padding: 16rpx 0;
+ font-size: 20rpx;
+ color: #666;
+ margin-top: 18rpx;
}
.has-buy {
background: #FF922C;
- border-radius: 8rpx;
+ border-radius: 0 24rpx 0 24rpx;
height: 42rpx;
width: 92rpx;
font-size: 24rpx;
color: #fff;
position: absolute;
- top: -21rpx;
- left: 28rpx;
+ top: 0;
+ right: 0;
line-height: 42rpx;
- display: none;
}
-.card-item-buy {
- background: #fff;
-}
-.card-item-buy{
- .card-date{
- //background: #FF922C;
- background: #fff;
- color: white;
- }
- .has-buy{
- display: block;
- }
-}
/* tab */
.tab-wrap {
display: flex;
@@ -359,7 +447,7 @@
display: block;
width: 50rpx;
height: 8rpx;
- background: linear-gradient( 91deg, #FDFAFF 0%, #F9C969 100%);
+ background: linear-gradient(271deg, #FFE4A1 0%, #FFC57A 51%, #FFE1B9 100%);
border-radius: 32rpx;
position: absolute;
bottom: -20rpx;
@@ -372,16 +460,13 @@
font-weight: 600;
font-size: 36rpx;
color: #FFD39A;
- padding: 10rpx;
- background: linear-gradient( 91deg, #A97132 0%, #542C09 100%);
+ padding: 26rpx;
+ background: linear-gradient(271deg, #2A1601 0%, #3B2106 51%);
border-radius: 200rpx;
}
.note {
font-size: 24rpx;
color: #999;
margin-top: 20rpx;
-}
-.vip2-num{
- color: #8E1768 !important;
}
</style>
diff --git a/pages/quanyi/add.vue b/pages/quanyi/add.vue
index 80a9255..9fe0cb6 100644
--- a/pages/quanyi/add.vue
+++ b/pages/quanyi/add.vue
@@ -1,7 +1,7 @@
<script>
-import offIcon from '@/static/imgs/icons/off.png'
-import leftIcon from '@/static/imgs/icons/left.png'
-import kkIcon from '@/static/imgs/icons/kk.png'
+import offIcon from '@/static/img/off.png'
+import leftIcon from '@/static/img/left.png'
+import kkIcon from '@/static/img/kk.png'
import addAddress from "@/pages/quanyi/components/addAddress";
import agreement from "@/pages/quanyi/components/agreement";
@@ -17,7 +17,7 @@
showTextBox: false,
loginAct: false,
$webHost:'https://rbb98765.zrgy-bbg.com',
- protocolChecked: [1],
+ protocolChecked: [0],
offIcon,leftIcon,kkIcon,
callbackData:{},
addressDetail: {
@@ -79,9 +79,9 @@
customStyle() {
return {
height: "100rpx",
- background: "#FF1472",
+ background: "#EE2626",
color: "#fff",
- borderRadius: "24rpx",
+ borderRadius: "50rpx",
fontSize: "34rpx",
};
},
@@ -100,8 +100,8 @@
uni.request({
url: _this.$webHost+'/api/v2/customer/getCstAddress',
data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- sourceId: uni.getStorageSync('qxgLogin').appId
+ customerId: uni.getStorageSync('ygxLogin').customerId,
+ sourceId: uni.getStorageSync('ygxLogin').appId
},
success: function (res) {
_this.addressDetail = res.data.data;
@@ -110,7 +110,7 @@
uni.request({
- url: _this.$webHost+'/api/v2/xinheyuan/bank/list/fake/'+uni.getStorageSync('qxgLogin').customerId,
+ url: _this.$webHost+'/api/v2/xinheyuan/bank/list/fake/'+uni.getStorageSync('ygxLogin').customerId,
success(res){
console.log(res.data.data);
this.bankList = res.data.data;
@@ -121,7 +121,7 @@
uni.setStorageSync('windowHeight',uni.getSystemInfoSync().windowHeight)
uni.request({
- url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('qxgLogin').customerId}`,
+ url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
method:'get',
success:function(res){
const dy = res.data.data[0];
@@ -139,23 +139,46 @@
}
})
-
-
+
+
uni.request( {
url: _this.$webHost + "/api/v2/renpin/H5/getPlatformProtocolNew",
data: {
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
+ customerId: uni.getStorageSync('ygxLogin').customerId,
+ appId: uni.getStorageSync('ygxLogin').appId
},
method: 'GET',
success(res) {
+ uni.showLoading({})
_this.getPlatformProtocolNewList = res.data.data;
_this.showTextBox = true;
+
_this.$nextTick(() => {
- _this.$refs.textBox && _this.$refs.textBox.open()
+ if(_this.$refs.textBox){
+ uni.hideLoading();
+ _this.$refs.textBox.open()
+ } else {
+ setTimeout(() => {
+ console.log('setTimeout');
+ uni.hideLoading();
+ _this.$refs.textBox && _this.$refs.textBox.open()
+ }, 400);
+ }
})
}
})
+
+
+ },
+ mounted(){
+ this.windowHeight = uni.getSystemInfoSync().windowHeight;
+ this.interval = setInterval(() => {
+ let newHeight = window.innerHeight;
+ if (this.windowHeight !== newHeight) {
+ this.windowHeight = newHeight;
+ this.handleResize();
+ }
+ }, 200);
},
methods: {
kk(){
@@ -163,6 +186,7 @@
const _this = this;
if(this.addressStr.length < 8){
+
uni.showToast({
title: '请输入详细地址',
icon: 'none'
@@ -172,7 +196,7 @@
uni.request({
- url: _this.$webHost+'/api/v2/sign/signed/'+uni.getStorageSync('qxgLogin').customerId,
+ url: _this.$webHost+'/api/v2/sign/signed/'+uni.getStorageSync('ygxLogin').customerId,
method:'POST',
})
@@ -180,7 +204,7 @@
url: _this.$webHost+'/api/v2/pay/memberPurchaseBenefitsPays',
method:'POST',
data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
+ customerId: uni.getStorageSync('ygxLogin').customerId,
memberPeriodConfigId: uni.getStorageSync('memberPeriodConfigInfo').id
},
success(){
@@ -199,13 +223,12 @@
this.$refs.popup.close()
},
open(){
+ // 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
this.$refs.popup.open()
},
validateAndCacheCardInfo() {
const _this = this;
-
-
if(!_this.cardNumber.length || _this.cardNumber.length<14 ){
return
}
@@ -228,27 +251,61 @@
}
})
+ /* uni.request({
+ url: "https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardBinCheck=true", // 阿里接口
+ data: {
+ cardNo: _this.cardNumber,
+ },
+ success: function (res) {
+ console.log(res.data.validated);
+ if (res.data.validated) {
+
+ for (let i of uni.getStorageSync("bankList")) {
+ console.log(i);
+ if (res.data.bank === i.bankCode) {
+ _this.bankCode = i.bankCode;
+ _this.bankName = i.bankName;
+
+ break;
+ }
+ }
+ if(!_this.bankName){
+ uni.showToast({
+ title: "暂不支持该银行或银行卡号有误",
+ icon: "none",
+ });
+ }
+ }
+
+ else {
+ uni.showToast({
+ title: "暂不支持该银行或银行卡号有误",
+ icon: "none",
+ });
+ }
+ },
+ });*/
},
submit() {
const _this = this;
- if(!this.loginAct){uni.showToast({title: '请阅读并同意相关协议', icon: 'none'});return;}
+ if(!this.protocolChecked[0]){uni.showToast({title: '请阅读并同意相关协议', icon: 'none'});return;}
if(!_this.cardName){
uni.showToast({
- title: "请填写您的姓名",
+ title: "请填写真实姓名",
icon: "none",
});
return;
}
if(!_this.idCardNo){
uni.showToast({
- title: "请填写您的身份证号",
+ title: "请输入身份证号",
icon: "none",
});
return;
}
if(!_this.cardNumber){
uni.showToast({
- title: "请填写银行卡号",
+ title: "请输入银行卡号",
icon: "none",
});
return;
@@ -268,10 +325,9 @@
});
return
}
-
if(!_this.addressStr){
uni.showToast({
- title: "请填写您的详细地址",
+ title: "请填写详细地址",
icon: "none",
});
return;
@@ -295,7 +351,7 @@
uni.request({
- url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('qxgLogin').customerId}`,
+ url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
method:'get',
success:function(res){
_this.getPayBindCardInfo = res.data.data;
@@ -309,7 +365,7 @@
data:{
..._this.bindCardId,
- "customerId": uni.getStorageSync('qxgLogin').customerId,
+ "customerId": uni.getStorageSync('ygxLogin').customerId,
"cardPhone" : _this.cardPhone, //手机号
"cardNumber": _this.cardNumber, //卡号
@@ -323,12 +379,16 @@
},
success(e){
if(e.data.code!==200){
- _this.$util.Tips({
- title: e.data.data
- });
+ // _this.$util.Tips({
+ // title: e.data.data
+ // });
+ uni.showToast({
+ title: e.data.data,
+ icon: 'none'
+ });
}
uni.request({
- url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('qxgLogin').customerId}`,
+ url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
method:'get',
success:function(res){
const ls = res.data.data;
@@ -337,13 +397,13 @@
- if(ls.length>1 && !ls[1].bindStatus){
+ if(ls.length && !ls[1].bindStatus){
_this.$refs.codeUi.open()
_this.isCounting = false;
_this.countText = '';
return;
}
- if(ls.length>2 && !ls[2].bindStatus){
+ if(ls.length && !ls[2].bindStatus){
_this.$refs.setUi.open();
_this.isCounting = false;
_this.countText = '';
@@ -353,6 +413,8 @@
+ _this.kk()
+
}
@@ -363,516 +425,559 @@
},
- agreeClick () {
- this.loginAct = !this.loginAct
- },
- getSmsCode(){
- const that = this;
- console.log(that.getPayBindCardInfo[0].paymentMethod);
- if(this.isCounting){
- return
- }
- if(that.getPayBindCardInfo[0].bindStatus){
- if(!that.getPayBindCardInfo[1].bindStatus){
- that.$refs.codeUi.open();
+ getSmsCode(){
+ const that = this;
+ console.log(that.getPayBindCardInfo[0].paymentMethod);
+ if(this.isCounting){
return
}
- if(!that.getPayBindCardInfo[2].bindStatus){
- that.$refs.setUi.open();
+ if(that.getPayBindCardInfo[0].bindStatus){
+ if(!that.getPayBindCardInfo[1].bindStatus){
+ that.$refs.codeUi.open();
+ return
+ }
+ if(!that.getPayBindCardInfo[2].bindStatus){
+ that.$refs.setUi.open();
+ }
+ return
}
- return
- }
- if (!/^1(3|4|5|7|8|9|6)\d{9}$/i.test(that.cardPhone)) return that.$util.Tips({
- title: '请输入正确的手机号码'
- });
+ if (!/^1(3|4|5|7|8|9|6)\d{9}$/i.test(that.cardPhone)) return uni.showToast({
+ title: '请输入正确的手机号码',
+ icon: 'none'
+ });
- that.isCounting = true;
+ that.isCounting = true;
- uni.request({
- url: that.$webHost+'/api/v2/xinheyuan/bankcard/save',
- method:'POST',
- data:{
- "customerId": uni.getStorageSync('qxgLogin').customerId,
- "nameReal": that.cardName, // 名字
- "cardNo":that.cardNumber,
- "idCard": that.idCardNo,
+ uni.request({
+ url: that.$webHost+'/api/v2/xinheyuan/bankcard/save',
+ method:'POST',
+ data:{
+ "customerId": uni.getStorageSync('ygxLogin').customerId,
+ "nameReal": that.cardName, // 名字
+ "cardNo":that.cardNumber,
+ "idCard": that.idCardNo,
- "bankPhoneNo":that.cardPhone,
- "bankCode": that.bankCode,
- "bankName": that.bankName,
- "phone": that.cardPhone, //手机号
- "paymentMethod": that.getPayBindCardInfo[0].paymentMethod ,
+ "bankPhoneNo":that.cardPhone,
+ "bankCode": that.bankCode,
+ "bankName": that.bankName,
+ "phone": that.cardPhone, //手机号
+ "paymentMethod": that.getPayBindCardInfo[0].paymentMethod ,
- "preBindType": "prebc",
- "bankId": "1",
+ "preBindType": "prebc",
+ "bankId": "1",
- },
- success(){
- uni.request({
- url: that.$webHost+'/api/v2/pay/bindCardToCodes',
- method:'POST',
- data:{
- "customerId": uni.getStorageSync('qxgLogin').customerId,
- "cardName": that.cardName, // 名字
- "cardIdNo": that.idCardNo,
+ },
+ success(){
+ uni.request({
+ url: that.$webHost+'/api/v2/pay/bindCardToCodes',
+ method:'POST',
+ data:{
+ "customerId": uni.getStorageSync('ygxLogin').customerId,
+ "cardName": that.cardName, // 名字
+ "cardIdNo": that.idCardNo,
- "cardNumber":that.cardNumber,
- "bankPhoneNo":that.cardPhone,
- "bankCode": that.bankCode,
- "bankName": that.bankName,
- "cardPhone": that.cardPhone, //手机号
- "paymentMethod": that.getPayBindCardInfo[0].paymentMethod ,
+ "cardNumber":that.cardNumber,
+ "bankPhoneNo":that.cardPhone,
+ "bankCode": that.bankCode,
+ "bankName": that.bankName,
+ "cardPhone": that.cardPhone, //手机号
+ "paymentMethod": that.getPayBindCardInfo[0].paymentMethod ,
- "preBindType": "prebc",
- "bankId": "1",
+ "preBindType": "prebc",
+ "bankId": "1",
- },
- success(res){
- if(res.data.code === 401){
- that.getUserInfo()
- return;
- }
-
- if(res.data.code !==200){
- uni.showToast({
- title: res.data.data,
- icon: 'none'
- })
- return;
- }
-
- if(res.data.code === 200){
- that.bindCardId = res.data.data;
- } else {
- that.isCounting = false;
- that.countText = '';
- uni.showToast({
- title: res.data.data,
- icon: 'none'
- });
-
- if(!that.getPayBindCardInfo[0].bindStatus){
-
+ },
+ success(res){
+ if(res.data.code === 401){
+ that.getUserInfo()
return;
}
-
- if(!that.getPayBindCardInfo[1].bindStatus){
- that.$refs.codeUi.open()
- that.isCounting = false;
- that.countText = '';
- return
- } if(!that.getPayBindCardInfo[2].bindStatus){
- that.isCounting = false;
- that.countText = '';
- that.$refs.setUi.open()
- return
+ if(res.data.code !==200){
+ uni.showToast({
+ title: res.data.data,
+ icon: 'none'
+ })
+ return;
}
+
+ if(res.data.code === 200){
+ that.bindCardId = res.data.data;
+ } else {
+ that.isCounting = false;
+ that.countText = '';
+ uni.showToast({
+ title: res.data.data,
+ icon: 'none'
+ });
+
+ if(!that.getPayBindCardInfo[0].bindStatus){
+
+ return;
+ }
+
+
+ if(!that.getPayBindCardInfo[1].bindStatus){
+ that.$refs.codeUi.open()
+ that.isCounting = false;
+ that.countText = '';
+ return
+ } if(!that.getPayBindCardInfo[2].bindStatus){
+ that.isCounting = false;
+ that.countText = '';
+ that.$refs.setUi.open()
+ return
+ }
+ }
+
}
+ })
- }
- })
-
- let count = 60;
- that.countText = `${count}s秒后重新获取`;
-
-
- const timer = setInterval(() => {
- count--;
+ let count = 60;
that.countText = `${count}s秒后重新获取`;
- if (count <= 0) {
- clearInterval(timer);
- that.isCounting = false;
- that.countText = '';
- }
- },1000)
- },
- fail(err){
- that.isCounting = false;
- that.countText = '';
- uni.showToast({
- title: err,
- icon: 'none'
- })
- }
- })
-
-
-
-
-
-
-
-
- },
-// 二次获取
- getCode(){
- if (this.isCounting_A) {
- return;
- }
- const paymentMethod = this.getPayBindCardInfo[1]['paymentMethod'];
- const _this = this;
- const pro = uni.getStorageSync('qxgLogin')
- pro['userName'] = _this.cardName;
- uni.request({
- url: _this.$webHost+'/api/v2/pay/bindCardToCodes',
- method:'POST',
- data:{
- cardIdNo: _this.idCardNo,
- customerId: pro.customerId,
- cardName: _this.cardName,
- cardNo: _this.cardIdNo,
- cardPhone: _this.cardPhone,
- cardNumber: _this.cardNumber,
- bankName: _this.bankName,
- preBindType: "prebc",
- paymentMethod:paymentMethod,
- againType : true
- },
- success(dt){
- if(dt.data.code === 401){
- _this.getUserInfo()
- return;
- }
- const res = dt.data.data;
- if(!res){
- return;
- }
- _this.callbackData = res;
- _this.bindCardId = res;
- uni.showToast({
- title: "验证码已发送",
- icon: "none",
- });
- // 设置倒计时功能
- let count = 60;
-
- const timer = setInterval(() => {
- count--;
- if (count <= 0) {
- clearInterval(timer);
- _this.$set(_this, "isCounting_A", false);
- } else {
- _this.$set(_this, "isCounting_A", true);
- _this.$set(_this, "countText_A", `${count}秒后重新获取`);
- }
- }, 1000);
- }
- })
- },
-
- submitPlan(){
-
- const _this = this;
-
- if (!this.verificationCodeT) {
- uni.showToast({
- title: "请输入验证码",
- icon: "none",
- });
- return;
- }
-
- const callbackData = _this.callbackData;
- const paymentMethod = _this.getPayBindCardInfo[1]['paymentMethod'];
-
-
- uni.request({
- url: _this.$webHost+'/api/v2/pay/verifySMSCodes',
- method:'POST',
- data:{
- ...callbackData,
-
- "customerId": uni.getStorageSync('qxgLogin').customerId,
- "cardPhone" : _this.cardPhone, //手机号
-
- "cardNumber": _this.cardNumber, //卡号
- paymentMethod:paymentMethod,
- "cardName" : _this.cardName, // 名字
- "cardIdNo" : _this.cardIdNo,
- "bankName" : _this.bankName,
- "cardNo" : _this.cardNumber,
-
- "verificationCode": _this.verificationCodeT,
- },
- success(dts){
- if(dts.data.code!==200){
- return
- }
-
- uni.request({
- url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('qxgLogin').customerId}`,
- method:'get',
- success:function(res){
- const ls = res.data.data;
- _this.getPayBindCardInfo = res.data.data;
- if(ls.length>2 && !ls[2].bindStatus){
- _this.isCounting_A = false;
- _this.countText_A = '';
- _this.$refs.codeUi.close()
- _this.$refs.setUi.open()
- return;
+ const timer = setInterval(() => {
+ count--;
+ that.countText = `${count}s秒后重新获取`;
+ if (count <= 0) {
+ clearInterval(timer);
+ that.isCounting = false;
+ that.countText = '';
}
-
- if(!ls || !ls.some(item => item.bindStatus == 0)) {_this.kk();return;}
-
- }
-
- })
- }
- })
-
- },
-// 三次
- getCode_san(){
- if (this.isCounting_B) {
- return;
- }
- const paymentMethod = this.getPayBindCardInfo[2]['paymentMethod'];
- const _this = this;
- const pro = uni.getStorageSync('qxgLogin')
- pro['userName'] = _this.cardName;
- uni.request({
- url: _this.$webHost+'/api/v2/pay/bindCardToCodes',
- method:'POST',
- data:{
- cardIdNo: _this.idCardNo,
- customerId: pro.customerId,
- cardName: _this.cardName,
- cardNo: _this.cardIdNo,
- cardPhone: _this.cardPhone,
- cardNumber: _this.cardNumber,
- bankName: _this.bankName,
- preBindType: "prebc",
- paymentMethod:paymentMethod,
- againType : true
- },
- success(dt){
- if(dt.data.code ==401){
- _this.getUserInfo()
- return;
- }
-
- if(dt.data.code !==200){
+ },1000)
+ },
+ fail(err){
+ that.isCounting = false;
+ that.countText = '';
uni.showToast({
- title: dt.data.data,
+ title: err,
icon: 'none'
})
- return;
}
+ })
- const res = dt.data.data;
- if(!res){
- return;
+
+
+
+
+
+
+
+
+
+ },
+// 二次获取
+ getCode(){
+ if (this.isCounting_A) {
+ return;
+ }
+ const paymentMethod = this.getPayBindCardInfo[1]['paymentMethod'];
+ const _this = this;
+ const pro = uni.getStorageSync('ygxLogin')
+ pro['userName'] = _this.cardName;
+ uni.request({
+ url: _this.$webHost+'/api/v2/pay/bindCardToCodes',
+ method:'POST',
+ data:{
+ cardIdNo: _this.idCardNo,
+ customerId: pro.customerId,
+ cardName: _this.cardName,
+ cardNo: _this.cardIdNo,
+ cardPhone: _this.cardPhone,
+ cardNumber: _this.cardNumber,
+ bankName: _this.bankName,
+ preBindType: "prebc",
+ paymentMethod:paymentMethod,
+ againType : true
+ },
+ success(dt){
+ if(dt.data.code === 401){
+ _this.getUserInfo()
+ return;
+ }
+ const res = dt.data.data;
+ if(!res){
+ return;
+ }
+ _this.callbackData = res;
+ _this.bindCardId = res;
+ uni.showToast({
+ title: "验证码已发送",
+ icon: "none",
+ });
+ // 设置倒计时功能
+ let count = 60;
+
+ const timer = setInterval(() => {
+ count--;
+ if (count <= 0) {
+ clearInterval(timer);
+ _this.$set(_this, "isCounting_A", false);
+ } else {
+ _this.$set(_this, "isCounting_A", true);
+ _this.$set(_this, "countText_A", `${count}秒后重新获取`);
+ }
+ }, 1000);
}
- _this.callbackData = res;
- _this.bindCardId = res;
+ })
+ },
+
+ submitPlan(){
+
+ const _this = this;
+
+ if (!this.verificationCodeT) {
uni.showToast({
- title: "验证码已发送",
+ title: "请输入验证码",
icon: "none",
});
- // 设置倒计时功能
- let count = 60;
-
- const timer = setInterval(() => {
- count--;
- if (count <= 0) {
- clearInterval(timer);
- _this.$set(_this, "isCounting_B", false);
- } else {
- _this.$set(_this, "isCounting_B", true);
- _this.$set(_this, "countText_B", `${count}秒后重新获取`);
- }
- }, 1000);
+ return;
}
- })
- },
- submitPlan_san(){
- const _this = this;
+ const callbackData = _this.callbackData;
+ const paymentMethod = _this.getPayBindCardInfo[1]['paymentMethod'];
- if (!this.verificationCodeS) {
- uni.showToast({
- title: "请输入验证码",
- icon: "none",
- });
- return;
- }
+ uni.request({
+ url: _this.$webHost+'/api/v2/pay/verifySMSCodes',
+ method:'POST',
+ data:{
+ ...callbackData,
- const callbackData = _this.callbackData;
- const paymentMethod = _this.getPayBindCardInfo[2]['paymentMethod'];
+ "customerId": uni.getStorageSync('ygxLogin').customerId,
+ "cardPhone" : _this.cardPhone, //手机号
+
+ "cardNumber": _this.cardNumber, //卡号
+ paymentMethod:paymentMethod,
+ "cardName" : _this.cardName, // 名字
+ "cardIdNo" : _this.cardIdNo,
+ "bankName" : _this.bankName,
+ "cardNo" : _this.cardNumber,
+
+ "verificationCode": _this.verificationCodeT,
+ },
+ success(dts){
+ if(dts.data.code!==200){
+ // _this.$util.Tips({
+ // title: dts.data.data
+ // });
+ uni.showToast({
+ title: dts.data.data,
+ icon: 'none'
+ });
+ return
+ }
+
+ uni.request({
+ url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
+ method:'get',
+ success:function(res){
+ const ls = res.data.data;
+ _this.getPayBindCardInfo = res.data.data;
+ if(ls.length>2 && !ls[2].bindStatus){
+ _this.isCounting_A = false;
+ _this.countText_A = '';
+ _this.$refs.codeUi.close()
+ _this.$refs.setUi.open()
+ }
+
+ if(!ls || !ls.some(item => item.bindStatus == 0)) {_this.kk();return;}
- uni.request({
- url: _this.$webHost+'/api/v2/pay/verifySMSCodes',
- method:'POST',
- data:{
- ...callbackData,
- "customerId": uni.getStorageSync('qxgLogin').customerId,
- "cardPhone" : _this.cardPhone, //手机号
- "cardNumber": _this.cardNumber, //卡号
- paymentMethod:paymentMethod,
- "cardName" : _this.cardName, // 名字
- "cardIdNo" : _this.cardIdNo,
- "bankName" : _this.bankName,
- "cardNo" : _this.cardNumber,
- "verificationCode": _this.verificationCodeS,
- },
- success(dts){
- if(dts.data.code!==200){
- _this.showToast({
- title: dts.data.data
- });
+ }
+
+ })
}
+ })
- uni.request({
- url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('qxgLogin').customerId}`,
- method:'get',
- success:function(res){
- const ls = res.data.data;
- _this.getPayBindCardInfo = res.data.data;
- _this.isCounting_B = false;
- _this.countText_B = '';
- _this.$refs.setUi.close()
- _this.$refs.codeUi.close()
- if(!ls || !ls.some(item => item.bindStatus == 0)) {_this.kk();return;}
-
-
+ },
+// 三次
+ getCode_san(){
+ if (this.isCounting_B) {
+ return;
+ }
+ const paymentMethod = this.getPayBindCardInfo[2]['paymentMethod'];
+ const _this = this;
+ const pro = uni.getStorageSync('ygxLogin')
+ pro['userName'] = _this.cardName;
+ uni.request({
+ url: _this.$webHost+'/api/v2/pay/bindCardToCodes',
+ method:'POST',
+ data:{
+ cardIdNo: _this.idCardNo,
+ customerId: pro.customerId,
+ cardName: _this.cardName,
+ cardNo: _this.cardIdNo,
+ cardPhone: _this.cardPhone,
+ cardNumber: _this.cardNumber,
+ bankName: _this.bankName,
+ preBindType: "prebc",
+ paymentMethod:paymentMethod,
+ againType : true
+ },
+ success(dt){
+ if(dt.data.code ==401){
+ _this.getUserInfo()
+ return;
}
- })
+ if(dt.data.code !==200){
+ uni.showToast({
+ title: dt.data.data,
+ icon: 'none'
+ })
+ return;
+ }
+
+ const res = dt.data.data;
+ if(!res){
+ return;
+ }
+ _this.callbackData = res;
+ _this.bindCardId = res;
+ uni.showToast({
+ title: "验证码已发送",
+ icon: "none",
+ });
+ // 设置倒计时功能
+ let count = 60;
+
+ const timer = setInterval(() => {
+ count--;
+ if (count <= 0) {
+ clearInterval(timer);
+ _this.$set(_this, "isCounting_B", false);
+ } else {
+ _this.$set(_this, "isCounting_B", true);
+ _this.$set(_this, "countText_B", `${count}秒后重新获取`);
+ }
+ }, 1000);
+ }
+ })
+ },
+ submitPlan_san(){
+
+ const _this = this;
+
+
+ if (!this.verificationCodeS) {
+ uni.showToast({
+ title: "请输入验证码",
+ icon: "none",
+ });
+ return;
}
- })
+ const callbackData = _this.callbackData;
+ const paymentMethod = _this.getPayBindCardInfo[2]['paymentMethod'];
+
+
+ uni.request({
+ url: _this.$webHost+'/api/v2/pay/verifySMSCodes',
+ method:'POST',
+ data:{
+ ...callbackData,
+
+ "customerId": uni.getStorageSync('ygxLogin').customerId,
+ "cardPhone" : _this.cardPhone, //手机号
+
+ "cardNumber": _this.cardNumber, //卡号
+ paymentMethod:paymentMethod,
+ "cardName" : _this.cardName, // 名字
+ "cardIdNo" : _this.cardIdNo,
+ "bankName" : _this.bankName,
+ "cardNo" : _this.cardNumber,
+
+ "verificationCode": _this.verificationCodeS,
+ },
+ success(dts){
+ if(dts.data.code!==200){
+ // _this.$util.Tips({
+ // title: dts.data.data
+ // });
+ uni.showToast({
+ title: dts.data.data,
+ icon: 'none'
+ });
+ }
+
+ uni.request({
+ url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
+ method:'get',
+ success:function(res){
+ const ls = res.data.data;
+ _this.getPayBindCardInfo = res.data.data;
+ _this.isCounting_B = false;
+ _this.countText_B = '';
+ _this.$refs.setUi.close()
+ _this.$refs.codeUi.close()
+ if(!ls || !ls.some(item => item.bindStatus == 0)) {_this.kk();return;}
+
+
+ }
+
+ })
+ }
+ })
+
+ },
+
+
+
+ getUserInfo() {
+
+ // const data = uni.getStorageSync('LOGING_DATA')
+ // console.log(data);
+ // this.$store.commit("LOGIN", {
+ // 'token': data.token
+ // });
+ // this.$store.commit('UPDATE_USERINFO', {
+ // avatar: data.avatar,
+ // nickname: data.nickname,
+ // phone: data.phone
+ // });
+ // this.$store.commit("SETUID", data.id)
+
+ uni.reLaunch({
+ url: '/pages/index/index'
+ });
+ },
},
-
-
-
- getUserInfo() {
-
-
-
- uni.reLaunch({
- url: '/pages/index/index'
- });
- },
- },
- onUnload() {
+ onUnload() {
clearInterval(this.interval);
},
}
</script>
<template>
- <view style="background: #fff;padding-bottom: 20rpx;">
+ <view style="background: #F7F7F7;padding-bottom: 40rpx;">
<view >
<view class="top">为向您提供更好的安全保障,您需要提供银行卡号以完成信息核验</view>
</view>
-
+ <view style="background: white;height: 20rpx;"></view>
<view class="form">
- <view class="clo" style="display: flex;background: white">
+ <view class="clo">
<view class="label">
<text>真实姓名</text>
</view>
<view class="inp_box">
- <input maxlength="8" style="width: 400rpx;" v-model="cardName" class="inp" placeholder="请填写您的姓名" />
+ <input maxlength="8" v-model="cardName" class="inp" placeholder="请填写真实姓名" />
</view>
</view>
- <view class="clo" style="display: flex;background: white">
+ <view class="clo">
<view class="label">
<text>身份证号</text>
</view>
<view class="inp_box">
- <input maxlength="18" style="width: 400rpx;" v-model="idCardNo" class="inp" placeholder="请填写您的身份证号" />
+ <input maxlength="18" v-model="idCardNo" class="inp" placeholder="请填写身份证号" />
</view>
</view>
+<!-- <view style="background: #F7F7F7;height: 20rpx;"></view>-->
-
- <view class="clo" style="display: flex">
+ <view class="clo">
<view class="label">
<text>银行卡号</text>
</view>
<view class="inp_box">
- <input maxlength="30" style="width: 400rpx;" @blur="validateAndCacheCardInfo" v-model="cardNumber" class="inp" placeholder="请填写银行卡号" />
- <image @click="cardNumber=''" v-show="!bindStatus && cardNumber" :src="offIcon" style="width: 40rpx;height: 40rpx;position: absolute;right: -40rpx;top: 0;"></image>
+ <input maxlength="30" @blur="validateAndCacheCardInfo" v-model="cardNumber" class="inp" placeholder="请输入银行卡号" />
+ <image @click="cardNumber=''" v-show="!bindStatus && cardNumber" :src="offIcon" style="width: 40rpx;height: 40rpx;position: absolute;right: 0rpx;top: 30rpx;"></image>
</view>
</view>
- <view class="clo" style="display: flex">
+ <view class="clo">
<view class="label">
<text>所属银行</text>
</view>
- <view class="inp_box" style="flex: 1;z-index: 1" >
- <view :class="bankName?'':'a3'">{{bankName?bankName:'请输入所属银行'}}</view>
+ <view class="inp_box" style="z-index: 1" >
+ <view style="width: 670rpx;height: 100rpx;border: 1px solid #EEEEEE;border-radius: 20rpx;padding-left: 20rpx;line-height: 100rpx;" :class="bankName?'':'a3'">{{bankName?bankName:'请输入所属银行'}}</view>
</view>
</view>
- <view class="clo" style="display: flex">
+ <view class="clo">
<view class="label">
- <view>手机号码</view>
+ <view>银行预留手机号</view>
</view>
- <view class="inp_box" >
- <input maxlength="11" v-model="cardPhone" class="inp" placeholder="请填写银行预留手机号" />
+ <view class="inp_box" style="position: relative;">
+ <input maxlength="11" v-model="cardPhone" class="inp" placeholder="请填写手机号" />
+<!-- <view class="code" style="position: absolute;top: 30rpx;right: 0;" @click="getSmsCode">-->
+<!-- <text :class="isCounting?'code_k':'code_a'">{{ isCounting ? countText : '获取验证码' }}</text>-->
+<!-- </view>-->
</view>
</view>
- <view class="clo" style="display: flex" v-if="!getPayBindCardInfo[0].bindStatus">
+ <view class="clo" v-if="getPayBindCardInfo.length && !getPayBindCardInfo[0].bindStatus">
<view class="label">
<text>验证码</text>
</view>
- <view class="inp_box" style="display: flex;flex: 1">
- <input maxlength="6" v-model="verificationCode" class="inp" type="number" style="width: 200rpx" placeholder="请输入验证码" />
+ <view class="inp_box" style="position: relative;">
+ <input maxlength="6" v-model="verificationCode" class="inp" type="number" placeholder="请填写验证码" />
- <view class="code" style="flex: 1;text-align: right" @click="getSmsCode">
+ <view class="code" style="position: absolute;top: 30rpx;right: 0;" @click="getSmsCode">
<text :class="isCounting?'code_k':'code_a'">{{ isCounting ? countText : '获取验证码' }}</text>
</view>
</view>
</view>
- <view class="clo" style="display: flex;margin-bottom: 20rpx;" @click="$refs.address.open()">
+ <view class="clo" @click="$refs.address.open()">
<view class="label" >
<text>收货地址</text>
</view>
<view class="inp_box">
- <view style="font-size: 28rpx;" v-if="addressStr">{{addressStr}}</view>
- <view style="font-size: 28rpx;color: #BDBDBE;" v-else>请填写您的详细地址</view>
+ <view style="font-size: 28rpx;width: 670rpx;height: 100rpx;border: 1px solid #EEEEEE;border-radius: 20rpx;padding-left: 20rpx;line-height: 100rpx;" v-if="addressStr">{{addressStr}}</view>
+ <view style="font-size: 28rpx;color: #BDBDBE;width: 670rpx;height: 100rpx;border: 1px solid #EEEEEE;border-radius: 20rpx;padding-left: 20rpx;line-height: 100rpx;" v-else>请填写收货地址</view>
</view>
</view>
</view>
+ <!-- <view class="xieyi">
+ <view style="display: flex">
+ <checkbox-group @change="checkboxChange">
- <view style=" padding: 0 30rpx;margin-left: 20rpx;">
+ <checkbox value="1" activeBorderColor="none" activeBackgroundColor="red" style="border-radius: 0" color="#ffffff"/>
+ </checkbox-group>
+ <view style="margin-top: 2rpx;"> <text>我已同意</text>
+ </view>
+ </view>
+ </view>-->
+
+ <view style=" padding: 0 30rpx;margin-top: 30rpx;">
<view style="display: flex;">
- <image @click="agreeClick" style="width: 50rpx;height: 50rpx;" :src="!loginAct ? '/static/imgs/check.png' : '/static/imgs/checked.png'" ></image>
- <text class="protocol-text" style="line-height: 50rpx" >
- 我已阅读并同意<text style="font-size: 24rpx;color: #4766FE" class="link-text" @click="$refs.agreement.open()">《相关协议》</text>
- </text>
+ <uv-checkbox-group
+ activeColor="red"
+ shape="circle"
+ v-model="protocolChecked">
+ <uv-checkbox size="14" :name="1" >
+
+ </uv-checkbox>
+ <text class="protocol-text" >
+ 我已阅读并同意
+ <text style="font-size: 24rpx;" class="link-text" @click="$refs.agreement.open()">《相关协议》</text>
+ </text>
+ </uv-checkbox-group>
</view>
</view>
<view class="sub" v-if="show">
- <button :class="loginAct?'':'nots'" class="sub_btn" @click="submit">提交</button>
+ <button :class="protocolChecked[0]?'':'nots'" class="sub_btn" @click="submit">提交</button>
</view>
<agreement ref="agreement"></agreement>
@@ -891,20 +996,44 @@
</uni-popup>
- <uv-popup mode="center" round="20" ref="codeUi" :closeOnClickOverlay="false" bgColor="">
+ <uv-popup mode="center" round="20" ref="codeUi" :closeOnClickOverlay="false" >
- <view style="padding: 40rpx;width:calc( 100% - 120rpx);margin: auto;border-radius: 40rpx 40rpx 40rpx 40rpx;background-image: url('/static/imgs/codeUiBACK.png');background-repeat: no-repeat;background-size: 100% 100%;padding-top: 60rpx;">
+ <view style="padding: 40rpx;width: 690rpx;background: url('/static/img/smsBg.png') no-repeat top center; background-size: 100%;height: 650rpx;">
+ <view class="head">
+ <view
+ style="
+ text-align: center;
+ font-weight: bold;
+ font-size: 36rpx;
+ position: relative;
+ height: 50rpx;
+ "
+ >
+ <image
+ @click="$refs.codeUi.close()"
+ src="@/static/img/off.png"
+ style="
+ width: 50rpx;
+ height: 50rpx;
+ position: absolute;
+ right: 0;
+ top: -2rpx;
+ "
+ ></image>
+ </view>
+ </view>
<view style="
font-weight: bold;
font-size: 36rpx;
position: relative;
margin-bottom: 12rpx;
+ text-align: center;
">短信确认</view>
- <view style="font-size: 28rpx;color: #666666;">为确保安全到账,需再次对您的收款账户进行短信验证</view>
+ <view style="font-size: 28rpx;color: #252938;text-align: center;">为确保安全到账,需再次对您的收款账户进行短信验证</view>
- <view class="content" style="margin-top: 40rpx;">
+ <view class="content">
<uv-form
:labelStyle="lstyle"
labelPosition="left"
@@ -920,7 +1049,6 @@
>
<uv-input
- shape="circle"
:adjustPosition="true"
readonly
v-model="cardPhone"
@@ -930,10 +1058,9 @@
border="none"
placeholder="请输入手机号"
+ style="height: 100rpx;border: 1px solid #EEEEEE;border-radius: 30rpx;padding: 0 20rpx;"
></uv-input>
</uv-form-item>
-
- <view style="height: 20rpx;"></view>
<uv-form-item
class="item"
@@ -947,10 +1074,12 @@
v-model="verificationCodeT"
border="none"
placeholder="请输入验证码"
+ style="height: 100rpx;border: 1px solid #EEEEEE;border-radius: 30rpx;padding: 0 20rpx;"
>
<template #suffix>
<text
class="code"
+ style="color: red;"
:class="isCounting_A ? 'ks' : ''"
@click="getCode"
>{{ isCounting_A ? countText_A : "获取验证码" }}</text
@@ -959,40 +1088,57 @@
</uv-input>
</uv-form-item>
<uv-form-item >
- <view class="but" style="width: 100%;margin-top: 20rpx;font-weight: 600;">
+ <view class="but" style="width: 100%;">
<uv-button
:customStyle="customStyle"
-
+ shape="circle"
@click="submitPlan"
- >确定</uv-button
+ >确认</uv-button
>
</view>
</uv-form-item>
</uv-form>
</view>
</view>
- <view style="text-align: center;margin-top: 40rpx;">
- <image
- @click="$refs.codeUi.close()"
- src="@/static/imgs/closeA.png"
- style=" width: 50rpx; height: 50rpx; "
- ></image>
- </view>
</uv-popup>
- <uv-popup mode="center" round="20" ref="setUi" :closeOnClickOverlay="false" bgColor="" z-index="999">
+ <uv-popup mode="center" round="20" ref="setUi" :closeOnClickOverlay="false" >
- <view style="padding: 40rpx;width:calc( 100% - 120rpx);margin: auto;border-radius: 40rpx 40rpx 40rpx 40rpx;background-image: url('/static/imgs/codeUiBACK.png');background-repeat: no-repeat;background-size: 100% 100%;padding-top: 60rpx;">
+ <view style="padding: 40rpx;width: 690rpx;background: url('/static/img/smsBg.png') no-repeat top center; background-size: 100%;height: 650rpx;">
+ <view class="head">
+ <view
+ style="
+ text-align: center;
+ font-weight: bold;
+ font-size: 36rpx;
+ position: relative;
+ height: 50rpx;
+ "
+ >
+ <image
+ @click="$refs.setUi.close()"
+ src="@/static/img/off.png"
+ style="
+ width: 50rpx;
+ height: 50rpx;
+ position: absolute;
+ right: 0;
+ top: -2rpx;
+ "
+ ></image>
+ </view>
+ </view>
<view style="
font-weight: bold;
font-size: 36rpx;
position: relative;
margin-bottom: 12rpx;
+ text-align: center;
">本人提交确认</view>
- <view style="font-size: 28rpx;color: #666666;">根据合规要求我们将进行本人提交意愿确认的短信校验,请注意查看短信验证码</view>
+ <view style="font-size: 28rpx;color: #252938;text-align: center;">根据合规要求我们将进行本人提交意愿确认的短信校验,请注意查看短信验证码</view>
- <view class="content" style="margin-top: 40rpx;">
+ <view class="content">
<uv-form
:labelStyle="lstyle"
labelPosition="left"
@@ -1017,9 +1163,10 @@
border="none"
placeholder="请输入手机号"
+ style="height: 100rpx;border: 1px solid #EEEEEE;border-radius: 30rpx;padding: 0 20rpx;"
></uv-input>
</uv-form-item>
- <view style="height: 20rpx;"></view>
+
<uv-form-item
class="item"
label=""
@@ -1032,10 +1179,12 @@
v-model="verificationCodeS"
border="none"
placeholder="请输入验证码"
+ style="height: 100rpx;border: 1px solid #EEEEEE;border-radius: 30rpx;padding: 0 20rpx;"
>
<template #suffix>
<text
class="code"
+ style="color: red;"
:class="isCounting_B ? 'ks' : ''"
@click="getCode_san"
>{{ isCounting_B ? countText_B : "获取验证码" }}</text
@@ -1044,24 +1193,17 @@
</uv-input>
</uv-form-item>
<uv-form-item >
- <view class="but" style="width: 100%;margin-top: 20rpx;font-weight: 600;">
+ <view class="but" style="width: 100%;">
<uv-button
:customStyle="customStyle"
+ shape="circle"
@click="submitPlan_san"
- >确定</uv-button
+ >确认</uv-button
>
</view>
</uv-form-item>
</uv-form>
</view>
- </view>
-
- <view style="text-align: center;margin-top: 40rpx;">
- <image
- @click="$refs.setUi.close()"
- src="@/static/imgs/closeA.png"
- style=" width: 50rpx; height: 50rpx; "
- ></image>
</view>
</uv-popup>
@@ -1069,10 +1211,7 @@
<addAddress :addressDetail="addressDetail" @off="$refs.address.close()" @submit="e => addressDetail = e"></addAddress>
</uv-popup>
-
- <text-box v-if="showTextBox" ref="textBox" :getPlatformProtocolNewList="getPlatformProtocolNewList"></text-box>
-
-
+ <!-- <text-box v-if="showTextBox" ref="textBox" :getPlatformProtocolNewList="getPlatformProtocolNewList"></text-box> -->
</view>
</template>
@@ -1091,17 +1230,15 @@
}
.form{
background: #FFFFFF;
- padding-top: 40rpx;
.clo{
- display: flex;
- background: #FBFBFB !important;
- border-radius: 24rpx 24rpx 24rpx 24rpx;
- border: 2rpx solid #EEEEEE;
- padding: 30rpx 20rpx;
- margin: 0 40rpx 40rpx;
-
+ //display: flex;
+ //padding: 30rpx 0rpx;
+ padding-bottom: 30rpx;
+ margin: 0 40rpx;
+ //border-bottom: 1rpx solid #E7E9ED;
+ background: #FFFFFF;
.label{
- width: 160rpx;
+ width: 460rpx;
height: 40rpx;
font-weight: 600;
@@ -1112,13 +1249,17 @@
.inp_box{
position: relative;
+ margin-top: 30rpx;
.inp{
&::placeholder{
color: #999999;
font-size: 30rpx;
}
- height: 42rpx;
-
+ width: 670rpx;
+ height: 100rpx;
+ border: 1px solid #EEEEEE;
+ border-radius: 20rpx;
+ padding-left: 20rpx;
font-weight: 400;
font-size: 30rpx;
color: #333333;
@@ -1130,14 +1271,16 @@
.code_a{
font-weight: 400;
- font-size: 24rpx;
- color: #FF1472;
+ font-size: 28rpx;
+ color: #EE2626;
padding: 16rpx 20rpx;
+ //border: 1rpx solid #EE2626;
+ //border-radius: 8rpx 8rpx 8rpx 8rpx;
}
.code_k{
font-weight: 400;
- font-size: 30rpx;
+ font-size: 28rpx;
color: #999999;
}
@@ -1154,12 +1297,12 @@
.sub{
-
+ width: 100%;
bottom: 50rpx;
- padding: 0 10rpx;
- margin-top: 50rpx;
+ padding: 0 30rpx;
+ margin-top: 40rpx;
.sub_btn{
- background: #FF1472;
+ background: #EE2626;
border-radius: 50rpx 50rpx 50rpx 50rpx;
padding: 10rpx;
@@ -1169,13 +1312,7 @@
}
}
-.item{
- background: #FBFBFB;
- border-radius: 24rpx 24rpx 24rpx 24rpx;
- border: 2rpx solid #EEEEEE;
- padding: 0 40rpx;
-}
::v-deep .uni-checkbox-input{
border-radius: 0;
transform: scale(0.7);
@@ -1199,8 +1336,5 @@
}
.nots{
opacity: 0.5;
-}
-.code{
- color: #FF1472;
}
</style>
diff --git a/pages/quanyi/components/addAddress/index.vue b/pages/quanyi/components/addAddress/index.vue
index beae164..b0fc3d8 100644
--- a/pages/quanyi/components/addAddress/index.vue
+++ b/pages/quanyi/components/addAddress/index.vue
@@ -23,7 +23,7 @@
customStyle() {
return {
height: "100rpx",
- background: "#FF1472",
+ background: "#EE2626",
color: "#fff",
borderRadius: "50rpx",
fontSize: "34rpx",
@@ -44,11 +44,11 @@
this.detail.consignee = this.detail.consignee || userName
this.detail.contactPhone = this.detail.contactPhone || mobile
+ console.log('8888888888888',this.detail.contactPhone)
},
data() {
return {
- $webHost:'https://rbb98765.zrgy-bbg.com',
lstyle: {
color: "#333333",
fontSize: "30rpx",
@@ -83,8 +83,8 @@
this.detail.cityCode = e.value[1].code
this.detail.cityName = e.value[1].title
- console.log(e.value[2]);
- if(e.value[2]) {
+
+ if(e.value[2]) {
this.detail.areaCode = e.value[2].code
this.detail.areaName = e.value[2].title
} else {
@@ -130,7 +130,7 @@
});
return;
}
- const params = uni.getStorageSync('qxgLogin')
+ const params = uni.getStorageSync('ygxLogin')
uni.request({
url: _this.$webHost + "/api/v2/customer/appAddCstAddress",
method: "POST",
@@ -164,12 +164,12 @@
height: 100rpx;
"
>
- <text class="tips"></text>
+ <text class="tips">为了提高您的审核通过率</text>
<view>请完善您的有效地址</view>
<image
@click="off"
- src="/static/img/close1.png"
+ src="/static/img/close.png"
style="
width: 50rpx;
height: 50rpx;
diff --git a/pages/quanyi/components/agreement/index.vue b/pages/quanyi/components/agreement/index.vue
index a725501..10a5c03 100644
--- a/pages/quanyi/components/agreement/index.vue
+++ b/pages/quanyi/components/agreement/index.vue
@@ -5,19 +5,15 @@
data() {
return {
list: [],
- $webHost:'https://rbb98765.zrgy-bbg.com',
}
},
created() {
const _this = this;
uni.request( {
url: _this.$webHost + "/api/v2/renpin/H5/getPlatformProtocolNew",
- data: {
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
method: 'GET',
success(res) {
+ console.log(res.data.data);
_this.list = res.data.data;
}
})
@@ -28,7 +24,6 @@
this.$refs.agreement.open();
},
toProtocol(e){
- console.log(e);
let title = e.name;
let code = e.code;
uni.navigateTo({
@@ -46,16 +41,16 @@
zIndex="3">
<uv-cell-group>
<view class="popup-title">
- <view style="margin: auto;text-align: center;padding: 20rpx;">请阅读以下协议</view>
+ <view>请阅读以下协议</view>
</view>
<view class="setting-list-item">
<view
class="setting-text"
- style="font-size: 32rpx; font-weight: bold; justify-content: left;padding-left: 30rpx;"
+ style="font-size: 32rpx; font-weight: bold; justify-content: left"
>平台服务协议</view
>
</view>
- <view class="list" style="height: 50vh;overflow: scroll;">
+ <view class="list" style="height: 50vh;">
<view class="li" @click="toProtocol(item)" v-for="(item,index) in list" :key="index" >
<view style="font-size: 30rpx">{{item.name}}</view>
@@ -77,10 +72,7 @@
display: flex;
flex-direction: row;
justify-content: center;
-
- margin: auto;
margin-top: 38rpx;
- text-align: center;
}
.setting-list-item {
display: flex;
diff --git a/pages/quanyi/components/code/index.vue b/pages/quanyi/components/code/index.vue
new file mode 100644
index 0000000..f6663b3
--- /dev/null
+++ b/pages/quanyi/components/code/index.vue
@@ -0,0 +1,323 @@
+<script>
+import {
+ payBindCardToCodes,
+ payVerifySMSCodes,
+ xinheyuanBankcardSave,
+} from "@/api/modules/user";
+export default {
+ props: {
+ bankDetail: {
+ type: Object,
+ default() {
+ return {};
+ },
+ },
+ queryBind: {
+ type: Object,
+ default() {
+ return {};
+ },
+ },
+ },
+ computed: {
+ customStyle() {
+ return {
+ height: "100rpx",
+ background: "#EE2626",
+ color: "#fff",
+ borderRadius: "50rpx",
+ fontSize: "34rpx",
+ };
+ },
+ inputStyle() {
+ return {
+ fontWeight: "400",
+ fontSize: "30rpx",
+ };
+ },
+ },
+
+ created() {
+ this.detail = this.bankDetail;
+ this.detail.verificationCode = "";
+ this.detail.bankName = this.queryBind.bankName;
+ this.detail.bankId = this.queryBind.cardNumber;
+ this.detail.bankPhoneNo = this.queryBind.cardPhone;
+ },
+
+ data() {
+ return {
+ lstyle: {
+ color: "#333333",
+ fontSize: "30rpx",
+ fontWeight: "bold",
+ },
+
+ isCounting: false,
+ countText: "",
+ detail: {
+ bankName: "",
+ verificationCode: "",
+ },
+ bankList: [],
+ bankCode: "",
+ callbackData: "",
+ };
+ },
+ methods: {
+
+ submitPlan() {
+ const _this = this;
+ const pro = uni.getStorageSync("params");
+
+ if (!this.detail.bankPhoneNo) {
+ uni.showToast({
+ title: "请输入手机号",
+ icon: "none",
+ });
+ return;
+ }
+ if (!this.detail.verificationCode) {
+ uni.showToast({
+ title: "请输入验证码",
+ icon: "none",
+ });
+ return;
+ }
+
+ const callbackData = uni.getStorageSync("callbackData");
+ const paymentMethod = uni.getStorageSync("payBindCardInf")[1]['paymentMethod'];
+ payVerifySMSCodes({
+ ...callbackData,
+ customerId: pro.customerId,
+ cardName: uni.getStorageSync("userName"), // 真实姓名
+ bankName: _this.detail.bankName,
+ cardIdNo: uni.getStorageSync("idCardNo"),
+
+ cardPhone: _this.detail.bankPhoneNo,
+ paymentMethod:paymentMethod,
+ cardNumber: _this.detail.bankId,
+ verificationCode: _this.detail.verificationCode, //短信
+ }).then((res) => {
+ if(res){
+ this.$emit("submit", this.detail);
+ }
+ });
+ },
+ off() {
+ this.$emit("off");
+ },
+ getCode() {
+ if (this.isCounting) {
+ return;
+ }
+ const paymentMethod = uni.getStorageSync("payBindCardInf")[1]['paymentMethod'];
+ const _this = this;
+ const pro = uni.getStorageSync("params");
+ xinheyuanBankcardSave({
+ customerId: pro.customerId,
+ nameReal: uni.getStorageSync("userName"),
+ cardNo: _this.detail.bankId,
+ idCard: uni.getStorageSync("idCardNo"),
+ bankId: "1",
+ bankPhoneNo: _this.detail.bankPhoneNo,
+ bankCode: _this.bankCode,
+ bankName: _this.detail.bankName,
+ phone: _this.detail.bankPhoneNo,
+ preBindType: "prebc",
+ }).then((res) => {
+ payBindCardToCodes({
+ cardIdNo: uni.getStorageSync("idCardNo"),
+ customerId: pro.customerId,
+ cardName: uni.getStorageSync("userName"),
+ cardNo: _this.detail.bankId,
+ cardPhone: _this.detail.bankPhoneNo,
+ cardNumber: _this.detail.bankId,
+ bankName: _this.detail.bankName,
+ preBindType: "prebc",
+ paymentMethod:paymentMethod,
+ againType : true
+ }).then((res) => {
+ if(!res){
+ return
+ }
+ _this.callbackData = res;
+ uni.setStorageSync("callbackData", res);
+
+ uni.showToast({
+ title: "验证码已发送",
+ icon: "none",
+ });
+ uni.setStorageSync("YF", 1);
+
+ // 设置倒计时功能
+ let count = 60;
+ const timer = setInterval(() => {
+ count--;
+ if (count <= 0) {
+ clearInterval(timer);
+ _this.$set(_this, "isCounting", false);
+ } else {
+ _this.$set(_this, "isCounting", true);
+ _this.$set(_this, "countText", `${count}秒后重新获取`);
+ }
+ }, 1000);
+ });
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <view>
+ <view class="head">
+ <view
+ style="
+ text-align: center;
+ font-weight: bold;
+ font-size: 36rpx;
+ position: relative;
+ height: 50rpx;
+ "
+ >
+
+ <image
+ @click="off"
+ src="@/static/img/close.png"
+ style="
+ width: 50rpx;
+ height: 50rpx;
+ position: absolute;
+ right: 0;
+ top: -2rpx;
+ "
+ ></image>
+ </view>
+ </view>
+ <view style="
+ font-weight: bold;
+ font-size: 36rpx;
+ position: relative;
+ margin-bottom: 12rpx;
+ padding-left: 38rpx;
+ ">短信确认</view>
+ <view style="font-size: 28rpx;color: #252938; padding-left: 38rpx;">为确保安全到账,需再次对您的收款账户进行短信验证</view>
+ <view class="con">
+ <u-form
+ :labelStyle="lstyle"
+ labelPosition="left"
+ :model="detail"
+ ref="form"
+ labelWidth="100"
+ >
+
+ <u-form-item
+ class="item"
+ label="手机号"
+ prop="repayTermStr"
+ borderBottom
+ >
+ <u-input
+ :adjustPosition="true"
+ maxlength="11"
+ :customStyle="inputStyle"
+ disabledColor="none"
+ inputAlign="right"
+ v-model="detail.bankPhoneNo"
+ border="none"
+ placeholder="请输入手机号"
+ ></u-input>
+ </u-form-item>
+
+ <u-form-item
+ class="item"
+ label="短信验证码"
+ prop="needVerify"
+ >
+ <u-input
+ :adjustPosition="true"
+ maxlength="6"
+ inputAlign="right"
+ v-model="detail.verificationCode"
+ border="none"
+ placeholder="请输入验证码"
+ >
+ <template #suffix>
+ <text
+ class="code"
+ :class="isCounting ? 'ks' : ''"
+ @click="getCode"
+ >{{ isCounting ? countText : "获取验证码" }}</text
+ >
+ </template>
+ </u-input>
+ </u-form-item>
+ <u-form-item >
+ <view class="but">
+ <u-button
+ :customStyle="customStyle"
+ shape="circle"
+ @click="submitPlan"
+ >确认</u-button
+ >
+ </view>
+ </u-form-item>
+ </u-form>
+ </view>
+ </view>
+</template>
+
+<style scoped lang="scss">
+.ks {
+ color: #999999 !important;
+}
+.but {
+ width: 100%;
+ margin-top: 46rpx;
+ .pack {
+ text-align: center;
+ margin-top: 20rpx;
+ font-size: 30rpx;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ }
+}
+.head {
+ margin-top: 40rpx;
+ padding: 0 40rpx;
+
+ font-size: 32rpx;
+ color: #252938;
+ .title {
+ display: flex;
+ flex-direction: row;
+ text-align: center;
+ color: #252938;
+ font-size: 32rpx;
+ font-weight: bold;
+ .num {
+ color: #4766fe;
+ display: contents;
+ }
+ }
+}
+
+.con {
+ padding: 0 38rpx;
+ .pt40 {
+ padding-top: 40rpx;
+ }
+ .mr {
+ margin-right: 20rpx;
+ }
+ .code {
+ margin-left: 40rpx;
+ font-size: 24rpx;
+ color: #4766fe;
+ background: none;
+ border-radius: 8rpx 8rpx 8rpx 8rpx;
+ }
+}
+</style>
diff --git a/pages/quanyi/components/code/set.vue b/pages/quanyi/components/code/set.vue
new file mode 100644
index 0000000..569df8e
--- /dev/null
+++ b/pages/quanyi/components/code/set.vue
@@ -0,0 +1,323 @@
+<script>
+import {
+ payBindCardToCodes,
+ payVerifySMSCodes,
+ xinheyuanBankcardSave,
+} from "@/api/modules/user";
+export default {
+ props: {
+ bankDetail: {
+ type: Object,
+ default() {
+ return {};
+ },
+ },
+ queryBind: {
+ type: Object,
+ default() {
+ return {};
+ },
+ },
+ },
+ computed: {
+ customStyle() {
+ return {
+ height: "100rpx",
+ background: "#EE2626",
+ color: "#fff",
+ borderRadius: "50rpx",
+ fontSize: "34rpx",
+ };
+ },
+ inputStyle() {
+ return {
+ fontWeight: "400",
+ fontSize: "30rpx",
+ };
+ },
+ },
+
+ created() {
+ this.detail = this.bankDetail;
+ this.detail.verificationCode = "";
+ this.detail.bankName = this.queryBind.bankName;
+ this.detail.bankId = this.queryBind.cardNumber;
+ this.detail.bankPhoneNo = this.queryBind.cardPhone;
+ },
+
+ data() {
+ return {
+ lstyle: {
+ color: "#333333",
+ fontSize: "30rpx",
+ fontWeight: "bold",
+ },
+
+ isCounting: false,
+ countText: "",
+ detail: {
+ bankName: "",
+ verificationCode: "",
+ },
+ bankList: [],
+ bankCode: "",
+ callbackData: "",
+ };
+ },
+ methods: {
+
+ submitPlan() {
+ const _this = this;
+ const pro = uni.getStorageSync("params");
+
+ if (!this.detail.bankPhoneNo) {
+ uni.showToast({
+ title: "请输入手机号",
+ icon: "none",
+ });
+ return;
+ }
+ if (!this.detail.verificationCode) {
+ uni.showToast({
+ title: "请输入验证码",
+ icon: "none",
+ });
+ return;
+ }
+
+ const callbackData = uni.getStorageSync("callbackData");
+ const paymentMethod = uni.getStorageSync("payBindCardInf")[2]['paymentMethod'];
+ payVerifySMSCodes({
+ ...callbackData,
+ customerId: pro.customerId,
+ cardName: uni.getStorageSync("userName"), // 真实姓名
+ bankName: _this.detail.bankName,
+ cardIdNo: uni.getStorageSync("idCardNo"),
+
+ cardPhone: _this.detail.bankPhoneNo,
+ paymentMethod:paymentMethod,
+ cardNumber: _this.detail.bankId,
+ verificationCode: _this.detail.verificationCode, //短信
+ }).then((res) => {
+ if(res){
+ this.$emit("submit", this.detail);
+ }
+ });
+ },
+ off() {
+ this.$emit("off");
+ },
+ getCode() {
+ if (this.isCounting) {
+ return;
+ }
+ const paymentMethod = uni.getStorageSync("payBindCardInf")[2]['paymentMethod'];
+ const _this = this;
+ const pro = uni.getStorageSync("params");
+ xinheyuanBankcardSave({
+ customerId: pro.customerId,
+ nameReal: uni.getStorageSync("userName"),
+ cardNo: _this.detail.bankId,
+ idCard: uni.getStorageSync("idCardNo"),
+ bankId: "1",
+ bankPhoneNo: _this.detail.bankPhoneNo,
+ bankCode: _this.bankCode,
+ bankName: _this.detail.bankName,
+ phone: _this.detail.bankPhoneNo,
+ preBindType: "prebc",
+ }).then((res) => {
+ payBindCardToCodes({
+ cardIdNo: uni.getStorageSync("idCardNo"),
+ customerId: pro.customerId,
+ cardName: uni.getStorageSync("userName"),
+ cardNo: _this.detail.bankId,
+ cardPhone: _this.detail.bankPhoneNo,
+ cardNumber: _this.detail.bankId,
+ bankName: _this.detail.bankName,
+ preBindType: "prebc",
+ paymentMethod:paymentMethod,
+ againType : true
+ }).then((res) => {
+ if(!res){
+ return
+ }
+ _this.callbackData = res;
+ uni.setStorageSync("callbackData", res);
+
+ uni.showToast({
+ title: "验证码已发送",
+ icon: "none",
+ });
+ uni.setStorageSync("YF", 1);
+
+ // 设置倒计时功能
+ let count = 60;
+ const timer = setInterval(() => {
+ count--;
+ if (count <= 0) {
+ clearInterval(timer);
+ _this.$set(_this, "isCounting", false);
+ } else {
+ _this.$set(_this, "isCounting", true);
+ _this.$set(_this, "countText", `${count}秒后重新获取`);
+ }
+ }, 1000);
+ });
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <view>
+ <view class="head">
+ <view
+ style="
+ text-align: center;
+ font-weight: bold;
+ font-size: 36rpx;
+ position: relative;
+ height: 50rpx;
+ "
+ >
+
+ <image
+ @click="off"
+ src="@/static/img/close.png"
+ style="
+ width: 50rpx;
+ height: 50rpx;
+ position: absolute;
+ right: 0;
+ top: -2rpx;
+ "
+ ></image>
+ </view>
+ </view>
+ <view style="
+ font-weight: bold;
+ font-size: 36rpx;
+ position: relative;
+ margin-bottom: 12rpx;
+ padding-left: 38rpx;
+ ">本人提交确认</view>
+ <view style="font-size: 28rpx;color: #252938; padding-left: 38rpx;padding-right: 30rpx;">根据合规要求我们将进行本人提交意愿确认的短信校验,请注意查看短信验证码</view>
+ <view class="con">
+ <u-form
+ :labelStyle="lstyle"
+ labelPosition="left"
+ :model="detail"
+ ref="form"
+ labelWidth="100"
+ >
+
+ <u-form-item
+ class="item"
+ label="手机号"
+ prop="repayTermStr"
+ borderBottom
+ >
+ <u-input
+ :adjustPosition="true"
+ maxlength="11"
+ :customStyle="inputStyle"
+ disabledColor="none"
+ inputAlign="right"
+ v-model="detail.bankPhoneNo"
+ border="none"
+ placeholder="请输入手机号"
+ ></u-input>
+ </u-form-item>
+
+ <u-form-item
+ class="item"
+ label="短信验证码"
+ prop="needVerify"
+ >
+ <u-input
+ :adjustPosition="true"
+ maxlength="6"
+ inputAlign="right"
+ v-model="detail.verificationCode"
+ border="none"
+ placeholder="请输入验证码"
+ >
+ <template #suffix>
+ <text
+ class="code"
+ :class="isCounting ? 'ks' : ''"
+ @click="getCode"
+ >{{ isCounting ? countText : "获取验证码" }}</text
+ >
+ </template>
+ </u-input>
+ </u-form-item>
+ <u-form-item >
+ <view class="but">
+ <u-button
+ :customStyle="customStyle"
+ shape="circle"
+ @click="submitPlan"
+ >确认提交</u-button
+ >
+ </view>
+ </u-form-item>
+ </u-form>
+ </view>
+ </view>
+</template>
+
+<style scoped lang="scss">
+.ks {
+ color: #999999 !important;
+}
+.but {
+ width: 100%;
+ margin-top: 46rpx;
+ .pack {
+ text-align: center;
+ margin-top: 20rpx;
+ font-size: 30rpx;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ }
+}
+.head {
+ margin-top: 40rpx;
+ padding: 0 40rpx;
+
+ font-size: 32rpx;
+ color: #252938;
+ .title {
+ display: flex;
+ flex-direction: row;
+ text-align: center;
+ color: #252938;
+ font-size: 32rpx;
+ font-weight: bold;
+ .num {
+ color: #4766fe;
+ display: contents;
+ }
+ }
+}
+
+.con {
+ padding: 0 38rpx;
+ .pt40 {
+ padding-top: 40rpx;
+ }
+ .mr {
+ margin-right: 20rpx;
+ }
+ .code {
+ margin-left: 40rpx;
+ font-size: 24rpx;
+ color: #4766fe;
+ background: none;
+ border-radius: 8rpx 8rpx 8rpx 8rpx;
+ }
+}
+</style>
diff --git a/pages/quanyi/index.vue b/pages/quanyi/index.vue
index adaa1e6..009a426 100644
--- a/pages/quanyi/index.vue
+++ b/pages/quanyi/index.vue
@@ -1,27 +1,20 @@
<script>
-
import textBox from "@/components/textBox/index.vue";
-import tehuichongzhi from "@/pages/quanshou/components/tehuichongzhi.vue"; //虚拟
-import lanyaerji from "@/pages/quanshou/components/lanyaerji.vue"; // 实物
+
+
export default {
components: {
- textBox,
- tehuichongzhi,
- lanyaerji,
+ textBox
},
async created() {
const _this = this;
-
await uni.request({
- url: _this.$webHost+'/api/v2/auth/qxgIosUnionLogin?phone='+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
+ // url: _this.$webHost+'/api/v2/auth/ygxLogin?phone='+_this.$util.getUserInfo().phone+'&appId='+'47be9559-6b1b-42dd-84ed-25d028065013',
+ url: _this.$webHost + '/api/v2/auth/qxgIosUnionLogin?phone=' + _this.$util.getUserInfo().phone,
success: function (res) {
- _this.qxgLogin = res.data.data;
- uni.setStorageSync('qxgLogin',_this.qxgLogin)
+ _this.ygxLogin = res.data.data;
+ uni.setStorageSync('ygxLogin',_this.ygxLogin)
uni.request({
url: _this.$webHost+'/api/v2/buryingPoint/record',
method:'PUT',
@@ -42,6 +35,9 @@
},
success:function (info){
_this.memberPeriodConfigInfo = info.data.data;
+ if(_this.memberPeriodConfigInfo['totalBenefitsAmount']) {
+ _this.memberPeriodConfigInfo['totalBenefitsAmount'] = _this.memberPeriodConfigInfo['totalBenefitsAmount'].toFixed(2);
+ }
uni.setStorageSync('memberPeriodConfigInfo',_this.memberPeriodConfigInfo)
_this.showTextBox = true;
_this.$nextTick(() => {
@@ -51,62 +47,38 @@
})
}
})
-
- // 获取VIP值
- uni.request({
- url: _this.$webHost+'/api/v2/customer/getRyxBenefitsRecordList?phone='+_this.$util.getUserInfo().phone,
- data:{
- customerId: uni.getStorageSync('qxgLogin').customerId,
- appId: uni.getStorageSync('qxgLogin').appId
- },
- success: function (res) {
- if(res.data.code==200){
- _this.lst = res.data.data;
- _this.lst.forEach(item=>{
- if(item && item.isBuy === 1 && [1,2,3].includes(item.period)) {_this.VIP = item.period;}
- })
- } else {
-
-
- }
- }
- })
let mk = setInterval(()=>{
- this.text -=1;
- if(!this.text){
+ _this.text -=1;
+ if(!_this.text){
clearInterval( mk)
}
},1000)
- this.time= setTimeout(()=>{
- this.protocolChecked = [1];
- this.tm = true;
- this.isAgree = true;
- },10000)
+ _this.time= setTimeout(()=>{
+ _this.protocolChecked = [1];
+ _this.tm = true;
+ },3000)
},
data() {
return {
- active: 0,
- VIP: 1, // 1黄金 2铂金 3黑金
- qxgLogin:{},
+ ygxLogin:{},
protocolChecked: [0],
- isAgree: false,
showTextBox: false,
+ isAgree: false,
tm: null,
time:null,
- text:10,
+ text:3,
memberPeriodConfigInfo: {
totalBenefitsAmount: "",
installmentPeriods: "",
}
}
},
-
methods: {
goHome(){
- uni.switchTab({
+ uni.reLaunch({
url: '/pages/index/index'
});
},
@@ -157,127 +129,162 @@
</script>
<template>
- <view class="box" style="">
+ <view style="background: url('/static/img/qyBg.png') no-repeat top center; background-size: 100%;background-color: #210E0E;">
<view class="head" style="position: relative">
+ <view style="display: flex;justify-content: flex-start;position: absolute;left: 0;top: 65rpx;padding-left: 30rpx;z-index: 9;color: #FFFFFF;">
+ <view @tap="goHome" id="home" class="home acea-row row-center-wrapper iconfont icon-fanhui2 h5_back icon-shouye4" :style="{ height: '80rpx',lineHeight: '80rpx'}" style="margin-top: 20rpx;">
+ <uv-icon name="home" color="#ffffff" size="20"></uv-icon>
+ </view>
+ </view>
+<!-- <view style="text-align: center;font-weight: bold;font-size: 36rpx;position: relative;">-->
+<!-- <view class="mm" style="text-align: center">会员权益说明</view>-->
+<!-- </view>-->
+ </view>
- <view style="margin-left: 20rpx;display: flex;justify-content: space-between;">
+ <view class="title">
+ <view style="margin-top:320rpx;text-align: center;font-weight: bold;line-height:90rpx;font-size:70rpx;background: -webkit-linear-gradient(#ffffff, #F3855B);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">
+ <p>入会有惊喜</p>
+ <p>尊享好福利</p>
+ </view>
+ <view style="font-size:32rpx;background: -webkit-linear-gradient(#ffffff, #F3855B);-webkit-background-clip: text;-webkit-text-fill-color: transparent;text-align: center;letter-spacing: 14rpx;margin-left: 16rpx;margin-top: 10rpx;">欢迎加入商城会员</view>
+ </view>
- <view>
- <view style="margin-top: 60rpx;"><image src="/static/imgs/SMTTA.png" style="width: 378rpx;height: 88rpx;"></image></view>
-
- <view style="margin-top: 10rpx;">
- <image src="/static/imgs/QQXGLK.png" style="width: 178rpx;height: 54rpx;"></image>
+ <view class="content" style="overflow-y: scroll;padding: 30rpx;background: #ffffff;border-top-left-radius: 50rpx;border-top-right-radius: 50rpx;margin-top: 60rpx;padding-bottom: 140rpx;">
+ <view class="content-title" style="text-align: center;display: flex;align-items: center;justify-content: center;margin-top: 40rpx;margin-bottom: 10rpx;">
+ <image style="width: 127rpx;height: 18rpx;" src="/static/img/nion1.png"></image>
+ <view style="font-size: 36rpx;color: #370606;margin: 0 20rpx;">会员权益说明</view>
+ <image style="width: 127rpx;height: 18rpx;" src="/static/img/nion.png"></image>
+ </view>
+ <view style="color: #370606;text-align: center;font-size: 24rpx;">MEMBERSHIP BENEFIS DESCRIPTON</view>
+ <view class="tips" style="background-color: #FFEEE7;border-radius: 32rpx;margin-top: 20rpx;padding: 30rpx;color: #370606;font-size: 28rpx;">
+ <view style="margin-bottom: 20rpx;display: flex;">
+ <view style="width: 40rpx;height: 40rpx;background-color: #F4936F;border-radius: 50%;text-align: center;line-height: 40rpx;flex-shrink: 0;flex-grow: 0;font-size: 28rpx;color: #370606;margin-right: 20rpx;">
+ 1.
+ </view>
+ <view>
+ 星享购商城会员年卡是本商城为会员特别打造的专属服务。成为会员后,您可享受各类商品折扣、促销优惠、生活权益及专属特权,并根据会员等级获赠相应超值礼品,体验我们为您准备的尊享会员服务。
</view>
</view>
- <view>
- <image src="/static/imgs/VIPSOA.png" style="width: 272rpx;height: 272rpx;"></image>
+ <view style="margin-bottom: 20rpx;display: flex;">
+ <view style="width: 40rpx;height: 40rpx;background-color: #F4936F;border-radius: 50%;text-align: center;line-height: 40rpx;flex-shrink: 0;flex-grow: 0;font-size: 28rpx;color: #370606;margin-right: 20rpx;">
+ 2.
+ </view>
+ <view>
+ 保险权益赠送:星享购商城会员年卡用户有机会获赠涵盖重疾险、意外险、事故险等的综合保障服务。尚未享受持牌机构服务的会员,亦有机会获取此项权益,具体详情请查阅会员中心相关说明。
+ </view>
+ </view>
+ <view style="margin-bottom: 20rpx;display: flex;">
+ <view style="width: 40rpx;height: 40rpx;background-color: #F4936F;border-radius: 50%;text-align: center;line-height: 40rpx;flex-shrink: 0;flex-grow: 0;font-size: 28rpx;color: #370606;margin-right: 20rpx;">
+ 3.
+ </view>
+ <view>
+ 会员服务一经开通即表示知晓并同意以上内容,自愿加入星享购商城会员年卡会员,并同意平台及平台权益合作方在账单日收入会员费,可在个人中心查看订单详情。
+ </view>
+ </view>
+ <view style="margin-bottom: 20rpx;display: flex;">
+ <view style="width: 40rpx;height: 40rpx;background-color: #F4936F;border-radius: 50%;text-align: center;line-height: 40rpx;flex-shrink: 0;flex-grow: 0;font-size: 28rpx;color: #370606;margin-right: 20rpx;">
+ 4.
+ </view>
+ <view>
+ 平台有权根据实际情况优化调整礼品内容和会员权益,最终解释权归星享购商城所有。
+ </view>
+ </view>
+ </view>
+ <view style="background: url('/static/img/qyBg2.png') no-repeat top center; background-size: 100%;width: 100%;height: 290rpx;margin-top: 20rpx;padding: 40rpx;">
+ <view style="margin-bottom:20rpx;font-size: 44rpx;font-weight: bold;background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">尊享会员年卡</view>
+ <view style="font-size: 28rpx;">
+ <p style="background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">年卡金额:{{ memberPeriodConfigInfo.totalBenefitsAmount }}元</p>
+ <p style="background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">扣款期数:{{ memberPeriodConfigInfo.installmentPeriods }}期(先享后付,免息)</p>
+ <p style="background: -webkit-linear-gradient(#FFEEE7, #F4936F);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">有效期:365天</p>
</view>
</view>
- </view>
- <view class="content" style="overflow-y: scroll;padding: 30rpx;padding-top: 0;margin-top: -30rpx;">
- <view class="tips" style="font-size: 28rpx;padding: 28rpx 32rpx;background-image: url('/static/imgs/TIPSBACK.png');background-repeat: no-repeat;background-size: 100% 100%;padding-bottom: 32rpx;">
- <view style="font-size: 28rpx;color: #333333">① {{memberPeriodConfigInfo.showTitle}}商城会员年卡是本商城为会员特别打造的专属服务。成为会员后,您可享受各类商品折扣、促销优惠、生活权益及专属特权,并根据会员等级获赠相应超值礼品,体验我们为您准备的尊享会员服务。</view>
- <view style="font-size: 28rpx;color: #333333">② 保险权益赠送:{{memberPeriodConfigInfo.showTitle}}商城会员年卡用户有机会获赠涵盖重疾险、意外险、事故险等的综合保障服务。尚未享受持牌机构服务的会员,亦有机会获取此项权益,具体详情请查阅会员中心相关说明。</view>
- <view style="font-size: 28rpx;color: #333333">③ 平台保留根据实际情况新增或优化会员权益内容的权利。</view>
+<!-- <view style="margin-top: 30rpx;" class="back">-->
+<!-- <view style="flex: 1">-->
+<!-- <view class="tips" style="font-size: 36rpx;font-weight: bold;color: #794007;margin-bottom: 20rpx;">专享特权</view>-->
+<!-- <view class="tips" style="font-size: 26rpx;color: #794007;margin-bottom: 20rpx;line-height: 44rpx;">星享购会员年卡会员 {{ memberPeriodConfigInfo.totalBenefitsAmount }}元/年</view>-->
+<!-- <view class="tips" style="font-size: 26rpx;margin-bottom: 20rpx;color: #794007;line-height: 44rpx;">分期支付:先享后付(提供的实物商品为指定的价值888元的红酒一瓶),分{{ memberPeriodConfigInfo.installmentPeriods }}期支付包年会员服务、免息</view>-->
+<!-- <view class="tips" style="font-size: 26rpx;margin-bottom: 3rpx;color: #794007;line-height: 44rpx;">购买成功后,可在个人中心查看分期订单详情</view>-->
+
+<!-- </view>-->
+<!-- <view style="display: flex;margin-top: 150rpx;">-->
+<!-- <checkbox-group activeColor="#fff" @change='isAgree=!isAgree' style="height: 100px;">-->
+<!-- <checkbox :checked="isAgree ? true : false" iconColor="#794007" iconSize="30" size="38" :name="1" shape="circle">-->
+<!-- </checkbox>-->
+<!-- </checkbox-group>-->
+<!-- </view>-->
+<!-- </view>-->
+<!-- <view class="tips" style="color: #666666;margin-top: 30rpx;font-size: 26rpx">点击按钮即表示知晓并同意以上内容,自愿加入本商城星享购会员年卡会员,并同意平台及平台权益合作方在账单日收入会员费</view>-->
+ <view style="font-size: 24rpx;color: #666666;display: flex;align-items: center;margin: 40rpx 0;height: 50rpx;justify-content: center;">
+ <checkbox-group activeColor="#fff" @change='isAgree=!isAgree' style="">
+ <checkbox :checked="isAgree ? true : false" iconColor="#794007" iconSize="30" size="38" :name="1" shape="circle">
+ </checkbox>
+ </checkbox-group>
+ <text style="color: #20110E;">我已阅读并同意</text>
+ <!-- <text style="display: inline;font-size: 24rpx;color: #4766FE" @click="link('/pages/protocol/protocol?code=shop_ai_service&title=会员服务协议-YXKJ')">《会员服务协议-YXKJ》</text>
+ <text style="display: inline;font-size: 24rpx;color: #4766FE" @click="link('/pages/protocol/protocol?code=shop_auto_service&title=委托扣款确认书-YXKJ')">《委托扣款确认书-YXKJ》</text>-->
+ <text style="display: inline;font-size: 24rpx;color: #4766FE" @click="link(`/pages/web/xieyi?code=${memberPeriodConfigInfo.protocolCode}&title=${memberPeriodConfigInfo.protocolName}`)">《会员权益服务协议-星享购》</text>
</view>
-
- <view class="QXFJH" style="background-image: url('/static/imgs/QXFJH.png');background-size: 100% 100%;text-align: center;background-repeat: no-repeat;padding: 28rpx 0;margin-top: 18rpx;margin-bottom: 16rpx;color: #333333;line-height: 48rpx;">
- <view style="font-weight: 600;">{{memberPeriodConfigInfo.showTitle}}商城会员专享特权</view>
-
- <view>年卡费用:{{memberPeriodConfigInfo.totalBenefitsAmount }}元/年</view>
- <view>支付期数:{{memberPeriodConfigInfo.installmentPeriods }}期</view>
- <!-- <view>支付期数:{{memberPeriodConfigInfo.installmentPeriods }}期(每期{{memberPeriodConfigInfo.totalBenefitsAmount / memberPeriodConfigInfo.installmentPeriods}}元)</view>-->
-
-
- </view>
-
- <view style="font-size: 22rpx;padding: 36rpx 32rpx 24rpx 32rpx;background-image: url('/static/imgs/CLOSAN.png');background-repeat: no-repeat;background-size: 100% 100%;margin-bottom: 32rpx;">
- <view style="font-size: 24rpx;margin-bottom: 30rpx;color: #333333">会员先享后付,包年会员服务、免息,购买成功后可在个人中心查看订单详情。会员服务开通成功不支持取消、退款。</view>
- <view style="font-size: 24rpx;color: #333333">点击按钮即表示知晓并同意以上内容,自愿加入本商城年卡会员,且同意平台及平台权益合作方在账单日收入会员费。</view>
- </view>
-
-
-
-
- <view style="display: flex;gap: 20rpx;">
+<!-- <view class="tips" style="color: #666666;margin-top: 24rpx;margin-bottom: 20rpx;font-size: 26rpx">*会员服务一经开通不支持取消、退款,会员服务到期前,用户可以选择续订或取消续费。</view>-->
+ <view style="position: fixed;bottom: 40rpx;left: 30rpx;width: 690rpx;display: flex;gap: 20rpx;z-index: 2;">
+ <button @click="goHome" style="width: 200rpx;height: 100rpx;padding: 10rpx 0;border-radius: 50rpx;font-size: 34rpx">暂不开通</button>
<button
- style="height: 100rpx;color: #666666;border-radius: 24rpx;font-size: 34rpx;line-height: 100rpx;width: 170rpx;background-color: unset;"
- @click="goHome"
- >暂不开通</button>
- <button
- style="height: 100rpx;color: white;border-radius: 24rpx;font-size: 34rpx;line-height: 100rpx;flex: 1;"
- :disabled="!tm"
+ style="height: 100rpx;padding: 10rpx 0;color: #370606;border-radius: 50rpx;font-size: 34rpx;flex: 1;"
+
@click="submit"
:class="tm &&protocolChecked.length?'activityBut':'disabledBut'"
- >同意并开通<text v-if="!tm">({{text}}秒)</text></button>
+ >同意并开通<text v-if="!tm">({{text}}秒)</text></button>
</view>
- <view style="display: flex;margin-top: 20rpx;display: flex;font-weight: 600;">
- <uv-checkbox-group activeColor="#FF1472" @change='isAgree=!isAgree' >
- <uv-checkbox :checked="isAgree ? true : false" activeColor="#FF1472" iconSize="14" size="16" :name="1" shape="circle">
- </uv-checkbox>
- <view style="font-size: 24rpx;color: #8B8D96">我已阅读并同意<text style="display: inline;font-size: 24rpx;color: #4766FE" @click="link(`/pages/web/xieyi?code=${memberPeriodConfigInfo.protocolCode}&title=${memberPeriodConfigInfo.protocolName}`)">{{ memberPeriodConfigInfo.protocolName }}</text></view>
- </uv-checkbox-group>
+ <!-- 专享会员生活权益 -->
+ <view>
+ <view class="content-title" style="text-align: center;display: flex;align-items: center;justify-content: center;margin-top: 50rpx;margin-bottom: 40rpx;">
+ <image style="width: 127rpx;height: 18rpx;" src="/static/img/nion1.png"></image>
+ <view style="font-size: 36rpx;color: #370606;margin: 0 20rpx;">专享会员生活权益</view>
+ <image style="width: 127rpx;height: 18rpx;" src="/static/img/nion.png"></image>
+ </view>
+ <view class="life" style="background: url('/static/img/qsLife.png') no-repeat top center; background-size: 100%;width: 100%;height: 1506rpx;">
+
+ </view>
+ <view style="width: 690rpx;margin-top: 20rpx;text-align: center;">
+<!-- <button @click="toHuiyuan" style="height: 100rpx;padding: 25rpx 0;color: #370606;border-radius: 50rpx;font-size: 34rpx;background-image: linear-gradient(to right, #FFEAE2, #FF6F55);">兑换生活权益</button>-->
+ <view style="color: #333;font-size: 24rpx;padding-top: 0rpx;padding-bottom: 10rpx;">以上权益仅为预览展示,实际以兑换页面为准</view>
+ </view>
+ <view class="content-title" style="text-align: center;display: flex;align-items: center;justify-content: center;margin-top: 50rpx;margin-bottom: 40rpx;">
+ <image style="width: 127rpx;height: 18rpx;" src="/static/img/nion1.png"></image>
+ <view style="font-size: 36rpx;margin: 0 20rpx;color: #370606;">专享会员实物权益</view>
+ <image style="width: 127rpx;height: 18rpx;" src="/static/img/nion.png"></image>
+ </view>
+ <view class="physical" style="background: url('/static/img/qsPhysical.png') no-repeat top center; background-size: 100%;width: 100%;height: 1580rpx;">
+
+ </view>
+ <view style="width: 690rpx;margin-top: 20rpx;text-align: center;">
+<!-- <button @click="getTips" style="height: 100rpx;padding: 25rpx 0;color: #370606;border-radius: 50rpx;font-size: 34rpx;background-image: linear-gradient(to right, #FFEAE2, #FF6F55);">查询实物权益</button>-->
+ <view style="color: #333;font-size: 24rpx;padding-top: 0rpx;padding-bottom: 40rpx;">以上权益仅为预览展示,实际以商城发出为准</view>
+ </view>
</view>
- <view class="tab-wrap">
- <text @click="active=0" :class="active==0?'active':''">实物权益</text>
- <text @click="active=1" :class="active==1?'active':''">虚拟权益</text>
- </view>
- <view class="note" style="margin-bottom: 20rpx;text-align: center;color: #999;font-size: 24rpx;" v-if="active==0">页面展示为实物样例,实际以用户收到的商品为准</view>
- <view class="note" style="margin-bottom: 20rpx;text-align: center;color: #999;font-size: 24rpx;" v-if="active==1">页面为虚拟权益预览,实际用户以当日兑换内容为准</view>
- <lanyaerji v-if="active==0" style="margin:0 30rpx;"/>
- <tehuichongzhi v-else :VIP="VIP" style="margin-top: 50rpx;" />
+
+ <!-- <view style="text-align: center;display: flex;flex-direction: row;justify-content: center"><text @click="submit(false)" style="color: #666666;margin-bottom: 20rpx;font-size: 28rpx">不同意</text></view>-->
</view>
<textBox v-if="showTextBox" ref="textBox" :code="memberPeriodConfigInfo.protocolCode" :title="memberPeriodConfigInfo.protocolName"></textBox>
</view>
</template>
<style scoped lang="scss">
-/* tab */
-.tab-wrap {
- display: flex;
- justify-content: space-evenly;
- margin-bottom: 50rpx;
- margin-top: 50rpx;
-}
-.tab-wrap text {
- font-weight: 600;
- font-size: 36rpx;
- color: #666;
-}
-.active {
- color: #333 !important;
- position: relative;
- &::after {
- content: "";
- display: block;
- width: 50rpx;
- height: 8rpx;
- background: linear-gradient( 91deg, #FDFAFF 0%, #F9C969 100%);
- border-radius: 32rpx;
- position: absolute;
- bottom: -20rpx;
- left: calc(50% - 25rpx);
- }
-}
.activityBut{
- background: #FF1472;
- font-weight: 600;
+ //background: #EE2626;
+ background-image: linear-gradient(to right, #FFEAE2, #FF6F55);
+ font-weight: 500;
}
.disabledBut{
- background: #666666;
- font-weight: 600;
-}
-.disabledBut{
- background: #999 !important;
+ background: #dddddd;
}
.head{
- padding: 0rpx 10rpx 0 30rpx;
+ padding: 30rpx 30rpx 0 30rpx;
font-size: 32rpx;
color: #252938;
@@ -308,74 +315,16 @@
.mr{
margin-right: 20rpx;
}
- .tips{
- position: relative;
- &::after{
- content: '';
- display: block;
- position: absolute;
- bottom: -38rpx;
- right: 22rpx;
- width: 14rpx;
- height: 62rpx;
- background: #FFD3DD;
- border-radius: 78rpx 78rpx 78rpx 78rpx;
- z-index: 2;
- }
- &::before{
- content: '';
- display: block;
- position: absolute;
- bottom: -38rpx;
- width: 14rpx;
- height: 62rpx;
- background: #FFD3DD;
- border-radius: 78rpx 78rpx 78rpx 78rpx;
- z-index: 2;
- }
- }
-}
-.QXFJH{
- position: relative;
- &::after{
- content: '';
- display: block;
- position: absolute;
- bottom: -38rpx;
- right: 22rpx;
- width: 14rpx;
- height: 62rpx;
- background: #FFD3DD;
- border-radius: 78rpx 78rpx 78rpx 78rpx;
- z-index: 2;
- }
- &::before{
- content: '';
- display: block;
- position: absolute;
- bottom: -38rpx;
- width: 14rpx;
- height: 62rpx;
- left: 32rpx;
- background: #FFD3DD;
- border-radius: 78rpx 78rpx 78rpx 78rpx;
- z-index: 2;
- }
+
}
.back{
- background-image: url("/static/imgs/ba.png");
+ // background-image: url("/static/img/ba.png");
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 22rpx 30rpx;
display: flex;
flex-direction: row;
}
-.box{
- background-image: url('/static/imgs/QYBACKA.png');
- background-repeat: no-repeat;
- background-size: 100% 100%;
- background-color: #FFF4FA;
- min-height: 100vh;
-}
+
</style>
diff --git a/pages/vip/index.vue b/pages/vip/index.vue
new file mode 100644
index 0000000..10c9e93
--- /dev/null
+++ b/pages/vip/index.vue
@@ -0,0 +1,403 @@
+<script>
+// import {isBuy, niankas, preOrder} from "@/api/api";
+// import {orderCreate} from "@/api/order";
+// import xianlianghaojjuab from "@/pages/quanshou/components/xianlianghaojjuab.vue";
+// import tehuichongzhi from "@/pages/quanshou/components/tehuichongzhi.vue";
+import lanyaerji from "@/pages/quanshou/components/lanyaerji.vue";
+// import list from "@/pages/quanshou/components/list.vue";
+// import heijing from "@/pages/quanshou/components/heijing.vue";
+// import agreement from "@/pages/users/login/components/agreement/index.vue";
+import {mapGetters} from "vuex";
+export default {
+ name: "index",
+ components: {
+ // xianlianghaojjuab,
+ // tehuichongzhi,
+ lanyaerji,
+ // list,
+ // heijing,
+ // agreement
+ },
+ data() {
+ return {
+ active: 0,
+ ls:[],
+ checked: false,
+ tips:'',
+ // active: 0,
+ VIP: 2, // 1黄金 2铂金 3黑金
+ lst:[],
+ vipConfig: {
+ 1: {
+ bg: "/static/img/quanshou/TBBG1.png",
+ box: "/static/img/quanshou/VIPBOX1.png",
+ title: "/static/img/quanshou/VIPTXT1.png",
+ level: "/static/img/quanshou/VIPLVL1.png",
+ textClass: "vip1-text",
+ progressBg: "#BBA767",
+ growText: " 88800/100000",
+ benefitText: "297.00"
+ },
+ 2: {
+ bg: "/static/img/quanshou/TBBG2.png",
+ box: "/static/img/quanshou/VIPBOX2.png",
+ title: "/static/img/quanshou/VIPTXT2.png",
+ level: "/static/img/quanshou/VIPLVL2.png",
+ textClass: "vip2-text",
+ progressBg: "#8094A6",
+ growText: " 88800/100000",
+ benefitText: "594.00"
+ },
+ 3: {
+ bg: "/static/img/quanshou/TBBG3.png",
+ box: "/static/img/quanshou/VIPBOX3.png",
+ title: "/static/img/quanshou/VIPTXT3.png",
+ level: "/static/img/quanshou/VIPLVL3.png",
+ textClass: "vip3-text",
+ progressBg: "#7B776E",
+ growText: " 88800/100000",
+ benefitText: "891.00"
+ }
+ },
+ total: 0,
+ };
+ },
+ computed: {
+ // ...mapGetters(['isLogin']),
+ currentVip() {
+ return this.vipConfig[this.VIP] || this.vipConfig[1];
+ }
+ },
+ onShow() {
+ // if(!this.isLogin){
+ // niankas().then(res=>{
+ // this.ls = res.data
+ // })
+ // return;
+ // }
+ // isBuy(uni.getStorageSync('phone')).then(res=>{
+ // if(!res.data && res.code===200){
+ // niankas().then(res=>{
+ // this.ls = res.data
+ // })
+ // }
+ // })
+
+
+ },
+
+
+ methods: {
+ userAgree() {
+ this.$refs.agreement.open()
+ },
+ goumaiba(){
+ const _this = this;
+ console.log(_this.checked);
+ if(!_this.checked){
+ return uni.showToast( {
+ icon: 'none',
+ title: '请先阅读并同意《会员服务协议》',
+ duration: 2000
+ })
+ }
+
+ const orderDetails =
+ {
+ "attrValueId": _this.ls[0].attrValueId,
+ "productId": _this.ls[0].productId,
+ "productNum": _this.ls[0].productNum,
+ }
+ preOrder({
+ "preOrderType": "buyNow",
+ "orderDetails":[orderDetails]
+ }).then(res=>{
+ if(res.code == 200){
+ // /pages/goods/order_payment/index?orderNo=PT160175859249666679688&payPrice=115.01
+
+ const pro = {
+ "addressId": 1,
+ "orderMerchantRequestList": [
+ {
+ "shippingType": 1,
+ "merId": 3,
+ "remark": "",
+ "userCouponId": 0
+ }
+ ],
+ "isUseIntegral": false,
+ "preOrderNo": res.data.orderNo,
+ "platUserCouponId": 0
+ }
+
+ orderCreate( pro).then(rp=>{
+ uni.navigateTo({
+ url: '/pages/goods/order_payment/index?orderNo='+rp.data.orderNo+'&payPrice='+rp.data.payPrice
+ })
+ })
+
+
+
+
+ }
+ }).catch( res=>{
+ uni.showToast( {
+ icon: 'none',
+ title: res,
+ duration: 2000
+ })
+ })
+ }
+ },
+}
+</script>
+
+<template>
+ <view style="background: white;background-image: url('/static/img/BNM.png');background-repeat: no-repeat;background-size: 100% 420rpx;background-color: #F4F4F4">
+
+ <view style="padding-top: 30rpx;">
+
+
+ <view style="background-image: url('/static/img/A0.png');background-repeat: no-repeat;background-size: 100% 100%;height: 230rpx;padding: 0 30rpx;display: flex;justify-content: space-between;position: relative;margin-left: 17rpx;margin-right: 17rpx;">
+ <view style="font-weight: 400;font-size: 24rpx;color: #FBFFE0;padding-left: 10rpx;position: absolute;bottom: 60rpx;"></view>
+ </view>
+
+
+ <view style="text-align: center;margin-top: 40rpx;">
+ <image style="width: 127rpx;height: 18rpx;margin-right: 20rpx;" src="@/static/img/nion1.png"></image>
+ <text style="font-size: 36rpx;color: #333333;font-weight: 600;">会员尊享特权</text>
+ <image style="width: 127rpx;height: 18rpx;margin-left: 20rpx;" src="@/static/img/nion.png"></image>
+ </view>
+ <view style="margin-top: 20rpx;display: flex;font-weight: 600;justify-content: space-around">
+ <view>
+ <image style="height: 88rpx;width: 88rpx;" src="@/static/img/GG1.png"></image>
+ <view style="font-size: 24rpx;color: #333333;font-weight: 600;">甄选好物</view>
+ </view>
+ <view>
+ <image style="height: 88rpx;width: 88rpx;" src="@/static/img/GG2.png"></image>
+ <view style="font-size: 24rpx;color: #333333;font-weight: 600;">优选权益</view>
+ </view>
+ <view>
+ <image style="height: 88rpx;width: 88rpx;" src="@/static/img/GG3.png"></image>
+ <view style="font-size: 24rpx;color: #333333;font-weight: 600;">专享价格</view>
+ </view>
+ </view>
+ <!-- 会员购买 + 权益部分 -->
+ <view class="content-wrap">
+ <view class="card-wrap">
+ <view class="card-item" v-for="i in lst">
+ <view class="card-title">{{numbs(i.period)}}期会员</view>
+ <view class="card-price">
+ <text>¥</text>
+ <text class="card-num">{{ i.amount }}</text>
+ <text>/{{i.dateType}}</text>
+ </view>
+ <view class="card-date">{{ i.buyDate }}</view>
+ <view class="has-buy">{{ i.isBuy?'已扣款' : '待扣款' }}</view>
+ </view>
+ </view>
+
+ <view class="tab-wrap">
+ <text @click="active=0" :class="active==0?'active':''">实物权益</text>
+<!-- <text @click="active=1" :class="active==1?'active':''">虚拟权益</text>-->
+ </view>
+
+ <view class="note" style="margin-bottom: 20rpx;text-align: center;color: #666666" v-if="active==0">
+<!-- 提供的实物商品为指定的价值888元的红酒一瓶-->
+ 为会员提供的实物商品以平台发出商品为准
+ </view>
+
+ <lanyaerji v-if="active==0" />
+
+ <template v-else>
+ <xianlianghaojjuab v-show="VIP==1" />
+ <tehuichongzhi v-show="VIP==1" style="margin-top: 50rpx;" />
+ <list clssD="kk" v-show="VIP==2" style="margin-top: 10rpx;" />
+ <heijing v-show="VIP==3" style="margin-top: 10rpx;" />
+ </template>
+ </view>
+
+
+
+
+
+
+ </view>
+ <agreement ref="agreement"></agreement>
+ </view>
+</template>
+
+<style scoped lang="scss" >
+.li{
+ height: 314rpx;padding: 40rpx 20rpx;text-align: center;border-radius: 24rpx;border: 4rpx solid #EEEEEE;
+ background: white;
+ position: relative;
+}
+
+</style>
+
+<style scoped lang="scss">
+/* 顶部 VIP 样式 */
+.vip-bg {
+ background-size: 100%;
+ background-repeat: no-repeat;
+ padding-top: 60rpx;
+ padding-bottom: 150rpx;
+}
+.vip-box {
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ height: 280rpx;
+ margin: 0 30rpx;
+ padding: 35rpx;
+ position: relative;
+}
+.gradient-text {
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ font-weight: 600;
+}
+.big-text {
+ margin-top: 20rpx;
+ font-size: 28rpx;
+}
+.small-text {
+ margin-top: 20rpx;
+ font-size: 24rpx;
+}
+.vip1-text {
+ background: linear-gradient(90deg, #754200, #ff9900);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+.vip2-text {
+ background: linear-gradient(90deg, #425268, #8094A6);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+.vip3-text {
+ background: linear-gradient(90deg, #ECD4C6, #C79372);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+.progress-bar {
+ height: 16rpx;
+ border-radius: 20rpx;
+ width: 100%;
+ margin-top: 24rpx;
+ position: relative;
+}
+.progress-inner {
+ position: absolute;
+ background: #fff;
+ border-radius: 6rpx;
+ left: 0;
+ top: 0;
+ width: 50%;
+ height: 16rpx;
+}
+
+/* 内容区 */
+.content-wrap {
+ padding: 30rpx;
+ background: #F4F4F4;
+ // border-radius: 24rpx 24rpx 0 0;
+ position: relative;
+ // top: -100rpx;
+}
+.card-wrap {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 40rpx;
+}
+.card-item {
+ background: #fff;
+ border-radius: 24rpx;
+ padding-top: 60rpx;
+ text-align: center;
+ padding-bottom: 48rpx;
+ width: calc(33% - 14rpx);
+ position: relative;
+}
+.card-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ margin-bottom: 34rpx;
+}
+.card-price {
+ color: #333;
+ font-size: 28rpx;
+ font-weight: 600;
+}
+.card-num {
+ font-size: 48rpx;
+ font-weight: bold;
+ margin: 0 8rpx;
+}
+.card-date {
+ font-size: 20rpx;
+ color: #666;
+ margin-top: 18rpx;
+}
+.has-buy {
+ background: #FF922C;
+ border-radius: 0 24rpx 0 24rpx;
+ height: 42rpx;
+ width: 92rpx;
+ font-size: 24rpx;
+ color: #fff;
+ position: absolute;
+ top: 0;
+ right: 0;
+ line-height: 42rpx;
+}
+
+/* tab */
+.tab-wrap {
+ display: flex;
+ justify-content: space-evenly;
+ margin-bottom: 50rpx;
+}
+.tab-wrap text {
+ font-weight: 600;
+ font-size: 36rpx;
+ color: #666;
+}
+.active {
+ color: #333 !important;
+ position: relative;
+ &::after {
+ content: "";
+ display: block;
+ width: 50rpx;
+ height: 8rpx;
+ background: linear-gradient(271deg, #FFE4A1 0%, #FFC57A 51%, #FFE1B9 100%);
+ border-radius: 32rpx;
+ position: absolute;
+ bottom: -20rpx;
+ left: calc(50% - 25rpx);
+ }
+}
+
+/* 按钮 */
+.exchange-btn {
+ font-weight: 600;
+ font-size: 36rpx;
+ color: #FFD39A;
+ padding: 26rpx;
+ background: linear-gradient(271deg, #2A1601 0%, #3B2106 51%);
+ border-radius: 200rpx;
+}
+.note {
+ font-size: 24rpx;
+ color: #999;
+ margin-top: 20rpx;
+}
+
+::v-deep .li-bg{
+ background: linear-gradient(138deg, #FFE4A1 0%, #FFC57A 68%, #FFE1B9 100%);
+ .ttis{
+ color: #8B3D00;
+ }
+}
+</style>
diff --git a/pages/web/alipay.vue b/pages/web/alipay.vue
new file mode 100644
index 0000000..b60622a
--- /dev/null
+++ b/pages/web/alipay.vue
@@ -0,0 +1,42 @@
+<script>
+export default {
+ name: "alipay",
+ data() {
+ return {
+ // webviewUrl: `${this.$rongbeiUrl}/?appId=${uni.getStorageSync('userAppId')}&phone=${uni.getStorageSync('phone')}&appName=rongyouxuan`, // 默认地址
+ webviewUrl: '', // H5地址
+ webviewStyles: {
+ progress: {
+ color: 'transparent'
+ }
+ }
+ }
+ },
+ onLoad(option) {
+ // 如果页面传了参数,可以拼接到 url
+ if (option && option.url) {
+ this.webviewUrl = option.url;
+ }
+ }
+}
+</script>
+
+<template>
+ <view class="container">
+ <web-view
+ :webview-styles="webviewStyles"
+ :src="webviewUrl"
+ />
+ </view>
+</template>
+
+<style scoped lang="scss">
+.container {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+}
+web-view {
+ flex: 1; // 自适应填满
+}
+</style>
diff --git a/pages/web/authorization.vue b/pages/web/authorization.vue
new file mode 100644
index 0000000..1da563b
--- /dev/null
+++ b/pages/web/authorization.vue
@@ -0,0 +1,106 @@
+<script>
+export default {
+ name: "authorization",
+ data() {
+ return {
+ checkboxValue: false
+ }
+ },
+ created() {
+
+ },
+ methods: {
+ checkboxChange(e) {
+ this.checkboxValue = e.detail.value.length > 0
+ },
+ agree(type){
+
+ const obj = {
+ '注册协议': 'REGISTRATION_AGREEMENT_APP_HWD',
+ '隐私政策': 'PRIVACY_POLICY_APP_HWD',
+ '个人信息查询及使用授权': 'PERSONAL_INFORMATION_PROCESSING_LETTER_YOUGOUXUAN'
+ }
+
+ uni.navigateTo({
+ url: `/pages/web/xieyi?code=${obj[type]}&title=${type}`
+ })
+ },
+ submit(){
+ if(!this.checkboxValue){
+ uni.showToast({
+ title: '请先阅读并同意相关协议',
+ icon: 'none'
+ })
+ return
+ }
+
+ window.sessionStorage.setItem('LoginAuthorization', 'agree');
+ uni.setStorageSync('LoginAuthorization', 'agree')
+ const phone = uni.getStorageSync('phone')
+ uni.request({
+ url:this.$webHost+`/api/v2/sign/ygxSigned/${phone}`,
+ method: 'POST',
+ })
+
+ uni.navigateTo({
+ url: '/pages/web/index'
+ })
+
+ }
+ }
+}
+</script>
+
+<template>
+
+ <view style="display: flex;height: 100%;">
+ <view style="background: white;flex: 1;display: flex;justify-content: end;flex-direction: column;padding-bottom: 50rpx;">
+
+
+ <view style="text-align: center;margin: 0 54rpx;">
+ <view style="margin-bottom: 40rpx; display: flex;justify-content: center">
+ <view> <image src="/static/img/web/SQ1.png" style="width: 120rpx;height: 120rpx"></image></view>
+
+ <view style="display: flex;justify-content: center;flex-direction: column"> <image src="/static/img/web/SQ2.png" style="width: 85rpx;height: 32rpx;margin: 0 50rpx;"></image></view>
+ <view> <image src="/static/img/web/SQ3.png" style="width: 120rpx;height: 120rpx"></image></view>
+
+
+
+
+
+ </view>
+ <view style="font-size: 32rpx; color: #333333;font-weight: 600;margin-bottom: 16rpx;">正在进入第三方合作服务平台</view>
+ <view style="color: #666666;font-size: 28rpx;text-align: left">
+ 以下服务与本应用无关,您点击确认会跳转到第三方平台的服务页面。为保障您的合法权益,请您仔细阅读第三方用户注册服务协议、隐私政策等内容。如有疑问请联系第三方平台热线:<text style="color: #333333;font-weight: 500">400-996 -0191</text>
+ </view>
+ </view>
+
+ <view style="margin-top: 350rpx;">
+
+ <view style="margin: 0 54rpx;color: #666666">
+ <checkbox-group @change="checkboxChange">
+ <checkbox value="1" :checked="checkboxValue" activeBorderColor="#EE2626" iconColor="#EE2626">已同意阅读并知晓</checkbox>
+ <text style="color: #4766FE;" @click="agree('注册协议')">《用户协议》</text>和
+ <text style="color: #4766FE;" @click="agree('隐私政策')">《隐私政策》</text>和
+ <text style="color: #4766FE;" @click="agree('个人信息查询及使用授权')">《个人信息查询及使用授权》</text>
+ </checkbox-group>
+ </view>
+
+ <view style="margin: 20rpx 54rpx;">
+ <button @click="submit" style="font-weight: 600;color: #FFFFFF;font-size: 34rpx;text-align: center;background: #EE2626;border-radius: 50rpx ;padding: 26rpx;" >我知道了</button>
+ </view>
+ </view>
+
+
+ </view>
+ </view>
+</template>
+<style scoped>
+page{
+ height: calc( 100vh - calc(44px + env(safe-area-inset-top)) );
+ background: white;
+}
+
+</style>
+
+
diff --git a/pages/web/ceshixz.vue b/pages/web/ceshixz.vue
new file mode 100644
index 0000000..558c970
--- /dev/null
+++ b/pages/web/ceshixz.vue
@@ -0,0 +1,41 @@
+<script>
+export default {
+ name: "index",
+ data() {
+ return {
+ webviewUrl: `https://test-half.zrgy-bbg.com?orderNo=2017082991934296064&customerId=2017082994761068546&appId=94e898c4-7eba-4c7d-88c5-852b4f2e4c6e&unControl=true&returnUrl=null&showImage=1&items=60&title=星享购&protocol=0`, // 默认地址
+ webviewStyles: {
+ progress: {
+ color: 'transparent'
+ }
+ }
+ }
+ },
+ onLoad(option) {
+ // 如果页面传了参数,可以拼接到 url
+ if (option && option.path) {
+ this.webviewUrl = `${this.webviewUrl}${option.path}`;
+ }
+ }
+}
+</script>
+
+<template>
+ <view class="container">
+ <web-view
+ :webview-styles="webviewStyles"
+ :src="webviewUrl"
+ />
+ </view>
+</template>
+
+<style scoped lang="scss">
+.container {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+}
+web-view {
+ flex: 1; // 自适应填满
+}
+</style>
diff --git a/pages/web/index.vue b/pages/web/index.vue
index 9d3dae4..bebc6f6 100644
--- a/pages/web/index.vue
+++ b/pages/web/index.vue
@@ -1,35 +1,41 @@
-<template>
- <web-view class="web-view" :webview-styles="webviewStyles" :src="url" :style="{width: windowW + 'px', height: windowH + 'px'}"></web-view>
-</template>
-
<script>
export default {
+ name: "index",
data() {
return {
- windowH: 0,
- windowW: 0,
+ webviewUrl: `${this.$rongbeiUrl}/?appId=${uni.getStorageSync('userAppId')}&phone=${uni.getStorageSync('phone')}&appName=rongyouxuan`, // 默认地址
webviewStyles: {
progress: {
color: 'transparent'
}
- },
- url: ''
+ }
}
},
onLoad(option) {
- if(option.webUel) this.url = decodeURIComponent(option.webUel);
- // 蚂蚁智能客服场景参数
- if(option.scene) this.url += `&scene=${option.scene}`;
- uni.setNavigationBarTitle({
- title: option.title
- })
- try {
- const res = uni.getSystemInfoSync();
- this.windowW = res.windowWidth;
- this.windowH = res.windowHeight;
- } catch (e) {
- // error
+ // 如果页面传了参数,可以拼接到 url
+ if (option && option.path) {
+ this.webviewUrl = `${this.webviewUrl}${option.path}`;
}
}
}
</script>
+
+<template>
+ <view class="container">
+ <web-view
+ :webview-styles="webviewStyles"
+ :src="webviewUrl"
+ />
+ </view>
+</template>
+
+<style scoped lang="scss">
+.container {
+ width: 100%;
+ height: 100vh;
+ display: flex;
+}
+web-view {
+ flex: 1; // 自适应填满
+}
+</style>
diff --git a/pages/web/xieyi.vue b/pages/web/xieyi.vue
index 3856640..b0a3a0f 100644
--- a/pages/web/xieyi.vue
+++ b/pages/web/xieyi.vue
@@ -10,11 +10,10 @@
console.log(pro);
let code =pro.code;
let title = pro.title;
- const _this = this;
uni.request( {
- url: _this.$webHost+`/api/v2/protocolInfo/getProtocol?appId=b2e24d04-6a97-40a0-9894-a44237c674ac&code=${code}`,
+ url: this.$webHost+`/api/v2/protocolInfo/getProtocol?appId=b2e24d04-6a97-40a0-9894-a44237c674ac&code=${code}`,
success: function (res) {
- _this.text = res.data.data.content;
+ this.text = res.data.data.content;
uni.setNavigationBarTitle({
title: title
})
diff --git a/static/css/carts.css b/static/css/carts.css
index 636c465..dc8aefd 100644
--- a/static/css/carts.css
+++ b/static/css/carts.css
@@ -132,7 +132,8 @@
.c-title{
position: fixed;
left: 0;
- top: var(--status-bar-height);
+ top: 44px;
+ /* top: var(--status-bar-height); */
z-index: 99;
width: 100%;
background-color: #ebebeb;
diff --git a/static/css/collect.css b/static/css/collect.css
index df7dfd3..9d3b773 100644
--- a/static/css/collect.css
+++ b/static/css/collect.css
@@ -1,7 +1,7 @@
.collect-title{
position: fixed;
left: 0;
- top: var(--status-bar-height);
+ top: 0;
z-index: 99;
width: 100%;
background-color: #ebebeb;
diff --git a/static/css/ginfo.css b/static/css/ginfo.css
index 2cba224..ff8f79d 100644
--- a/static/css/ginfo.css
+++ b/static/css/ginfo.css
@@ -12,8 +12,8 @@
width: 60upx;
height: 60upx;
left: 30upx;
- top: 30upx;
- z-index: 999;
+ top: 60upx;
+ z-index: 9999999;
}
.bannerIndex{
display: flex;
diff --git a/static/css/index.css b/static/css/index.css
index 9380c7b..f0282ec 100644
--- a/static/css/index.css
+++ b/static/css/index.css
@@ -1,553 +1,634 @@
page{
- background-color: #EEEBEC;
- font-size: 24upx;
+ background-color: #ffffff;
+ font-size: 24upx;
}
-.status_bar {
- height: var(--status-bar-height);
+.content{
+ position: absolute;
+ width: 100%;
+ padding-bottom: calc(var(--window-bottom));
+}
+.page-container {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ position: relative;
+ padding-top: 50upx;
+}
+
+.page-bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 784rpx;
+ z-index: 0;
+}
+.content {
+
+}
+.status-bar {
width: 100%;
}
-.index-top{
- background: url(@/static/imgs/index-bg.png) no-repeat;
- background-size: 100% 100%;
- width: 100%;
+
+.btn-group {
+ display: flex;
+ margin-top: 30rpx;
}
-.index-title{
- padding: 30rpx 0;
- display: flex;
+.cancel-btn {
+ flex: 1;
+ margin-right: 20rpx;
+ background-color: #f5f5f5;
}
-.index-title-view{
- display: flex;
- align-items: center;
-}
-.index-title-view-lf{
- margin: 0 32rpx;
-}
-.index-title-view-lf image{
- width: 144rpx;
- height: 56rpx;
-}
-.index-title-view-lf2{
- width: 200rpx;
- height: 40rpx;
- background: #FF1472;
- border-radius: 8rpx 8rpx 8rpx 8rpx;
- text-align: center;
- color: #ffffff;
- line-height: 40rpx;
-}
-.index-title-view1{
- width: 62%;
-}
-.index-title-view2{
- width: 38%;
-}
-.index-title-view-rf {
- display: flex;
- flex: 1;
- justify-content: center;
- align-items: center;
-}
-.index-title-view-rf image{
- width: 40rpx;
- height: 40rpx;
- display: block;
- margin: 0 auto;
+.allow-btn {
+ flex: 1;
+ background-color: #007aff;
+ color: white;
}
.search{
- width: 100%;
- display: flex;
- margin-top: 20upx;
- margin-bottom: 30upx;
+ position: fixed;
+ left: 0;
+ width: 100%;
+ top: var(--status-bar-height);
+ z-index: 9999;
+ background: #fbf8f8;
+ height: 110upx;
}
-.search-1{
- width: 76%;
- display: flex;
- justify-content: center;
- align-items: center;
+.search-v{
+ width: 94%;
+ margin-left: 3%;
+ display: flex;
+ height: 70upx;
+ margin-top: 20upx;
+ align-items: center;
+ font-size: 24upx;
+ justify-content: center;
+ color: #9e9d9d;
+ background-color: #ffffff;
+ border-radius: 30upx;
}
-.search-1 input{
- width: 76%;
- height: 72rpx;
- background: #FFFFFF;
- border-radius: 50rpx;
- padding-left: 14%;
- font-size: 26upx;
+.search-icon{
+ width: 40upx;
+ height: 40upx;
+ margin-right: 20upx;
}
-.search-1 image{
- position: absolute;
- width: 40rpx;
- height: 40rpx;
- left: 8%;
-}
-.search-2{
- width: 20%;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-.search-2 view{
- width: 112rpx;
- height: 72rpx;
- text-align: center;
- line-height: 72rpx;
- color: #ffffff;
- background: #FF1472;
- border-radius: 200rpx;
-}
-
-.tabs{
- display: flex;
- margin-bottom: 20upx;
-}
-.tabs view{
- padding: 0 35rpx;
- margin-left: 35rpx;
- text-align: center;
- line-height: 56rpx;
- height: 56rpx;
- background: #F3F4F6;
- border-radius: 200rpx;
- color: #364153;
-}
-.tabs .active {
- color: #ffffff;
- background: #FF1472;
-}
-
.banner{
- width: 94%;
- margin: 40upx auto 30upx;
-}
-uni-swiper-item{
- border-radius: 32rpx;
+ width: 94%;
+ margin: 20upx auto 20upx;
}
.swiper, .swiper image{
- width: 100%;
- height: 278rpx;
- border-radius: 32rpx;
+ width: 100%;
+ height: 400upx;
+ border-radius: 16upx;
}
-.comm{
- width: 94%;
- margin: 20upx 3% 40upx;
- display: flex;
-}
-.comm .comm-1{
- width: 48%;
- height: 278rpx;
- border-radius: 32rpx;
- background-color: #ffffff;
-}
-.comm .comm-2{
- width: 4%;
- height: 278rpx;
-}
-.comm .comm-3{
- width: 48%;
- height: 278rpx;
- border-radius: 32rpx;
- background-color: #ffffff;
-}
-.comm-title image{
- width: 128rpx;
- height: 40rpx;
- margin: 24rpx 26rpx;
-}
-.comm-goods{
- display: flex;
-}
-.comm-goods .comm-goods-v{
- flex: 1;
- display: flex;
- text-align: center;
- justify-content: center;
-}
-.comm-goods-v text{
- font-family: PingFang SC, PingFang SC;
- font-weight: 600;
- font-size: 24rpx;
- color: #FB2C36;
- text-align: left;
- font-style: normal;
- text-transform: none;
-}
-.comm-goods image{
- width: 128rpx;
- height: 128rpx;
- border-radius: 20rpx;
-}
-.hots{
- width: 94%;
- margin: 20upx auto 40rpx;
- height: 280rpx;
- background: #FFFFFF;
- border-radius: 32rpx 32rpx 32rpx 32rpx;
-}
-.hots-title{
- padding: 24rpx 26rpx;
-}
-.hots-title .hots-icon{
- width: 192rpx;
- height: 40rpx;
- border-radius: 0rpx 0rpx 0rpx 0rpx;
-}
-.hots-rgt{
- float: right;
- display: flex;
- align-items: center;
-}
-.hots-rgt text{
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 24rpx;
- color: #666666;
- text-align: left;
- font-style: normal;
- text-transform: none;
- margin-right: 10rpx;
-}
-.hots-rgt image{
- width: 32rpx;
- height: 32rpx;
-}
-.hots-goods{
- display: flex;
-}
-.hots-goods .hots-goods-v{
- flex: 1;
- display: flex;
- text-align: center;
- justify-content: center;
-}
-.hots-goods-v text{
- font-family: PingFang SC, PingFang SC;
- font-weight: 600;
- font-size: 24rpx;
- color: #FB2C36;
- text-align: left;
- font-style: normal;
- text-transform: none;
-}
-.hots-goods image{
- width: 136rpx;
- height: 136rpx;
- border-radius: 20rpx;
-}
-
.cates{
- width: 94%;
- margin: 0upx 3%;
- display: inline-block;
- border-top-left-radius: 10upx;
- border-top-right-radius: 10upx;
- padding: 0upx 0 0upx 0;
+ width: 690rpx;
+ margin: 0 auto;
+ border-radius: 30rpx;
+ background-color: #ffffff;
+ padding-bottom: 30rpx;
+ margin-top: 300rpx;
+ display: flex;
+ flex-wrap: wrap; /* 确保子元素换行 */
}
.cates-v{
- float: left;
- width: 20%;
- border-radius: 50%;
- margin-bottom: 0upx;
+ width: 25%;
+ margin-bottom: 0upx;
}
.cates-v-icon{
- width: 80upx;
- height: 80upx;
- display: block;
- margin: 40upx auto 20upx;
- border-radius: 50%;
+ width: 90rpx;
+ height: 90rpx;
+ display: block;
+ margin: 40upx auto 20upx;
+ border-radius: 10upx;
}
.cates-v view{
- text-align: center;
- font-size: 24upx;
+ text-align: center;
+ font-size: 24upx;
color: #364153;
}
.goods-main{
- width: 94%;
- margin: 0upx auto 0upx;
- border-bottom-left-radius: 10upx;
- border-bottom-right-radius: 10upx;
- border-top: 1px solid #eeeeee;
+ width: 96%;
+ margin: 10upx auto 30upx;
+ background-color: #ffffff;
+ border-radius: 10upx;
}
.goods-main-title{
- padding: 20upx;
- display: flex;
- align-items: center;
- margin-bottom: 10upx;
+ padding: 20upx;
+ display: flex;
+ align-items: center;
+ margin-bottom: 10upx;
}
.goods-main-icon{
- width: 70upx;
- height: 70upx;
- margin-right: 20upx;
- border-radius: 10upx;
+ width: 70upx;
+ height: 70upx;
+ margin-right: 20upx;
+ border-radius: 10upx;
}
.goods-main-title text{
- font-size: 30upx;
- color: #333333;
- font-weight: 700;
+ font-size: 30upx;
+ color: #333333;
+ font-weight: 700;
}
.goods-icon{
- width: auto;
- height: 340upx;
+ width: auto;
+ height: 340upx;
}
.goods-list{
- width: 100%;
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
-}
-.hyj{
- display: flex;
- justify-content: center;
- margin-left: 10rpx;
- margin-right: 10rpx;
-}
-.hyj image{
- width: 74rpx;
- height: 32rpx;
+ width: 96%;
+ display: flex;
+ margin-left: 2%;
+ flex-wrap: wrap;
+ justify-content: space-between;
}
.goods-item{
- width: 48%;
- border-radius: 18upx;
- background-color: #fbf8f8;
- margin-bottom: 30upx;
+ width: 48%;
+ border-radius: 18upx;
+ background-color: #fbf8f8;
+ margin-bottom: 30upx;
}
.goods-icon{
- width: 100%;
- border-radius: 10upx;
- margin-bottom: 20upx;
+ width: 100%;
+ border-radius: 10upx;
+ margin-bottom: 20upx;
}
.goods-price{
- margin-top: 16upx;
- display: flex;
- align-items: center;
- color: #F75451;
- font-size: 26upx;
- padding: 0 14upx;
- padding-bottom: 20upx;
+ margin-top: 0upx;
+ display: flex;
+ align-items: center;
+ color: #F75451;
+ font-size: 26upx;
+ padding: 0 14upx;
+ padding-bottom: 20upx;
}
.goods-price .red{
- font-size: 26upx !important;
- font-weight: 600;
+ margin-left: 10upx;
+ font-size: 24rpx !important;
+ font-weight: 600;
}
.goods-title{
- padding: 0 14upx;
- font-size: 26upx;
- line-height: 42upx;
- font-weight: 700;
- color: #333;
- display: -webkit-box; /* 设置为WebKit内核的弹性盒子模型 */
- -webkit-box-orient: vertical; /* 垂直排列 */
- -webkit-line-clamp: 1; /* 限制显示两行 */
- overflow: hidden; /* 隐藏超出范围的内容 */
- text-overflow: ellipsis; /* 使用省略号 */
+ padding: 0 14upx;
+ font-size: 26upx;
+ line-height: 42upx;
+ font-weight: 700;
+ color: #333;
+ display: -webkit-box; /* 设置为WebKit内核的弹性盒子模型 */
+ -webkit-box-orient: vertical; /* 垂直排列 */
+ -webkit-line-clamp: 1; /* 限制显示两行 */
+ overflow: hidden; /* 隐藏超出范围的内容 */
+ text-overflow: ellipsis; /* 使用省略号 */
}
.more{
- position: absolute;
- right: 10upx;
- display: flex;
- justify-content: center;
- align-items: center;
+ position: absolute;
+ right: 10upx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
.more image{
- width: 36upx;
- height: 36upx;
- margin-right: 30rpx;
+ width: 36upx;
+ height: 36upx;
+ margin-right: 30rpx;
}
.lingqu{
- position: fixed;
- left: 0;
- width: 100%;
- height: 100%;
- top: 0;
- z-index: 99999;
+ position: fixed;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ z-index: 99999;
}
.lingqu .msk{
- position: absolute;
- width: 100%;
- height: 100%;
- background-color: #000000;
- opacity: .5;
- left: 0;
- top: 0;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background-color: #000000;
+ opacity: .5;
+ left: 0;
+ top: 0;
}
.lingquMain{
- width: 70%;
- height: auto;
- position: absolute;
- top: 35%;
- left: 15%;
+ width: 70%;
+ height: auto;
+ position: absolute;
+ top: 35%;
+ left: 15%;
}
.lingquMain image{
- width: 100%;
- border-radius: 20upx;
+ width: 100%;
+ border-radius: 20upx;
}
.auth{
- width: 94%;
- margin: 30upx auto;
- height: 240upx;
+ width: 94%;
+ margin: 110upx auto 0;
+ height: 240upx;
}
.auth image{
- width: 100%;
- height: 100%;
- border-radius: 20upx;
+ width: 100%;
+ height: 100%;
+ border-radius: 20upx;
}
@keyframes floatUp {
- from {
- transform: translateY(100%); /* 从元素原始位置向下移动100% */
- opacity: 0; /* 初始透明度为0 */
- }
- to {
- transform: translateY(0); /* 移动到原始位置 */
- opacity: 1; /* 完全不透明 */
- }
+ from {
+ transform: translateY(100%); /* 从元素原始位置向下移动100% */
+ opacity: 0; /* 初始透明度为0 */
+ }
+ to {
+ transform: translateY(0); /* 移动到原始位置 */
+ opacity: 1; /* 完全不透明 */
+ }
}
.popup-content {
- animation: floatUp 1s ease-out; /* 应用动画,持续时间为1秒,缓动函数为ease-out */
+ animation: floatUp 1s ease-out; /* 应用动画,持续时间为1秒,缓动函数为ease-out */
}
-.edu-view{
- padding: 40upx 0 10upx;
- width: 560upx;
- border-radius: 50upx;
- background-color: #ffffff;
- background-size: 100% 100%;
+.edu-view{
+ padding: 40upx 0 10upx;
+ width: 560upx;
+ border-radius: 50upx;
+ background-color: #ffffff;
+ background-size: 100% 100%;
}
.edu-view2{
- border-radius: 30upx !important;
+ border-radius: 30upx !important;
}
.edu-icon-succ{
- width: 140upx;
- height: 140upx;
- display: block;
- margin: 50upx auto;
+ width: 140upx;
+ height: 140upx;
+ display: block;
+ margin: 50upx auto;
}
.edu-icon-fail{
- width: 200upx;
- height: 100upx;
- display: block;
- margin: 50upx auto;
+ width: 200upx;
+ height: 100upx;
+ display: block;
+ margin: 50upx auto;
}
.edu-view-desc2{
- font-size: 36upx;
- font-weight: 700;
- text-align: center;
+ font-size: 36upx;
+ font-weight: 700;
+ text-align: center;
}
.edu-view-desc3{
- width: 80%;
- margin: 30upx auto;
- text-align: center;
- font-size: 26upx;
- color: #666666;
- line-height: 40upx;
+ width: 80%;
+ margin: 30upx auto;
+ text-align: center;
+ font-size: 26upx;
+ color: #666666;
+ line-height: 40upx;
}
.edu-btns{
- width: 94%;
- border-radius: 50upx;
- margin: 30upx auto 30upx;
- z-index: 99;
- height: 90upx;
- background-color: #e3b400;
- color: #ffffff;
- letter-spacing: 10upx;
- display: flex;
- justify-content: center;
- align-items: center;
- font-weight: 700;
- font-size: 28upx;
+ width: 94%;
+ border-radius: 50upx;
+ margin: 30upx auto 30upx;
+ z-index: 99;
+ height: 90upx;
+ background-color: #e3b400;
+ color: #ffffff;
+ letter-spacing: 10upx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-weight: 700;
+ font-size: 28upx;
}
-
-.auth{
+.hLogo{
+ width: 96%;
+ padding: 40upx 0;
+ margin: 0 auto;
+}
+.hLogoView{
+ /* display: flex;
+ align-items: center; */
+}
+.h-logo{
+ width: 128rpx;
+ height: 41rpx;
+}
+.hLogo-1{
+ margin: 0 30upx;
+}
+.hLogo-2{
+ position: relative;
+ width: 100%;
+ height: 72rpx;
+ background-color: #ffffff;
+ border-radius: 50rpx;
+ margin-top: 20rpx;
+ /* background: url(@/static/imgs/input-bg.png) no-repeat;
+ background-size: 100% 100%; */
+}
+.hLogo-2 .s-btn {
+ position: absolute;
+ right: 30rpx;
+ top: 13rpx;
+ text-align: center;
+ width: 96rpx;
+ height: 50rpx;
+ line-height: 50rpx;
+ border-radius: 25rpx;
+ background-color: #4766FE;
+ color: #ffffff;
+}
+.hLogo-2 .s-icon{
+ position: absolute;
+ left: 30rpx;
+ top: 20rpx;
+ width: 30rpx;
+ height: 30rpx;
+}
+.hLogo-2 input{
+ width: 83%;
+ height: 70rpx;
+ line-height: 70rpx;
+ font-size: 28rpx;
+ background: transparent;
+ padding-left: 70rpx;
+}
+.xxhh-v{
+ width: 96%;
+ margin: 0 auto;
+ padding: 10upx 0;
+ height: 195rpx;
position: relative;
}
-.auth-over{
- background: linear-gradient(#e58585, #d19323);
+.xxhh-v-icon{
+ position: absolute;
width: 100%;
height: 100%;
+}
+.xxhh-title{
+ display: flex;
+ padding: 20upx;
+ align-items: center;
+}
+.xxk-icon{
+ width: 201rpx;
+ height: 47rpx;
+ margin-right: 23rpx;
+}
+.xxhh-title text{
+ z-index: 99;
+ font-size: 22rpx;
+ color: #261B18;
+}
+.xxhh-edu{
+ padding-top: 16upx;
+ padding-bottom: 16upx;
+ z-index: 99;
+}
+.xxhh-edu text{
+ margin-left: 20upx;
+ color: #AEA3A2;
+ font-size: 24rpx;
+}
+.xxhh-money{
+ padding-top: 0;
+ margin-left: 10upx;
+}
+.xxhh-money text{
+ font-size: 48rpx;
+ color: #A18685;
+ font-weight: 700;
+}
+
+.xxhf{
+ width: 96%;
+ margin: 0 auto 30upx;
+ height: 325rpx;
+ background: url(@/static/imgs/xxhf.png) no-repeat;
+ background-size: 100%;
border-radius: 20upx;
}
-.auth-over-edu{
- padding: 30upx 50upx;
- color: #ffffff;
-}
-.auth-over-edu text{
- display: block;
-}
-.auth-over-edu-til{
- font-weight: 500;
- font-size: 30upx;
- letter-spacing: 4upx;
- margin-bottom: 14upx;
-}
-.auth-over-edu-price{
+.xxhf-title{
+ padding: 24upx 80upx;
+ color: #5B2D2C;
+ font-size: 32rpx;
font-weight: 700;
- font-size: 80upx;
- letter-spacing: 5upx;
- margin-bottom: 14upx;
}
-.auth-over-btn{
+.xxhf-v{
+ width: 96%;
+ height: 220rpx;
+ margin: 0 auto 5%;
+ display: flex;
+ background-color: #ffffff;
+ border-radius: 20upx;
+}
+.xxhv-v{
+ flex: 1;
+ height: 100%;
+}
+.deb-icon{
+ width: 100%;
+ height: 100%;
+}
+.xxhv-vv-1{
+ width: 180rpx;
+ height: 180rpx;
+ margin: 20rpx 0 20rpx 12%;
+ background: #EFEFEF;
+ border-radius: 14rpx;
+ border: 3px solid #FFABB5;
+}
+.xxhv-vv-2{
+ width: 180rpx;
+ height: 180rpx;
+ margin: 20rpx 0 20rpx 4%;
+ background: #EFEFEF;
+ border-radius: 14rpx;
+ border: 3px solid #FFABB5;
+}
+.xxhv-vv-t{
+ width: 90%;
+ height: 46rpx;
+ margin: -3rpx auto;
+ font-family: Source Han Sans CN;
+ font-weight: 400;
+ font-size: 26rpx;
+ color: #F8F9FD;
+ line-height: 52rpx;
+ background-size: 100%;
+ text-align: center;
+}
+.xxhv-icon{
+ width: 100%;
+ height: 134rpx;
+}
+.xx-icon{
+ width: 80upx;
+ height: 80upx;
+ display: block;
+ margin: 27rpx auto;
+ border-radius: 10rpx;
+}
+.notice{
+ width: 96%;
+ margin: 0 2% 20upx;
+ background: #ffffff;
+ height: 80upx;
+ display: flex;
+ border-radius: 20upx;
+}
+.n-icon{
+ width: 50upx;
+ height: 50upx;
+ margin: 15upx 20upx;
+}
+.marquee-container {
+ width: 100%;
+ overflow: hidden;
+ white-space: nowrap;
+ box-sizing: border-box;
+}
+.marquee-content {
+ height: 100%;
+ line-height: 80upx;
+ font-size: 28upx;
+ color: #e36900;
+ font-weight: 700;
+ display: inline-block;
+ padding-left: 100%; /* 初始偏移 */
+ animation: marquee 10s linear infinite; /* 动画名称、持续时间、速度曲线、重复次数 */
+}
+@keyframes marquee {
+ 0% { transform: translateX(0); }
+ 100% { transform: translateX(-100%); }
+}
+.kttv{
+ width: 156rpx;
+ height: 62rpx;
+ background-size: 100%;
position: absolute;
right: 30upx;
- top: 38%;
- padding: 20upx 50upx;
- border-radius: 30upx;
- background: #ffffff;
- color: #333333;
- font-weight: 700;
- font-size: 28upx;
- box-shadow: 2px 2px 2px #d1233c;
+ bottom: 10upx;
}
-.auth-over-edu-tip{
- font-weight: 700;
- font-size: 32upx;
- letter-spacing: 5upx;
- margin-bottom: 14upx;
-}
-.auth-2{
- height: auto;
+.kttv text{
+ color: #ffffff;
+ margin-left: 30upx;
+ line-height: 62rpx;
}
-.close-edu{
- position: absolute;
- width: 60upx;
- height: 60upx;
- right: 20upx;
- top: 20upx;
+.ad1{
+ width: 96%;
+ margin: 20upx auto 20upx;
+ height: 308rpx;
+}
+.ad1 image{
+ width: 100%;
+ height: 100%;
}
+.ad2{
+ width: 96%;
+ margin: 10upx auto 20upx;
+ height: 270rpx;
+}
+.ad2 image{
+ width: 100%;
+ height: 100%;
+}
+.d3-wrap {
+ width: 96%;
+ margin: 10upx auto 20upx;
+ height: 278rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+.ad3{
+ width: 50%;
+ height: 278rpx;
+}
+.ad3 image{
+ width: 100%;
+ height: 100%;
+}
+.ad4 image{
+ width: 100%;
+ height: 100%;
+}
+.ad4 {
+ width: 50%;
+ height: 278rpx;
+}
+
+.ad5{
+ width: 96%;
+ margin: 10upx auto 20upx;
+ height: 278rpx;
+}
+.ad5 image{
+ width: 100%;
+ height: 100%;
+}
+
+.ca-ad{
+ width: 96%;
+ margin: 10upx auto 20upx;
+ height: 200rpx;
+}
+.ca-ad image{
+ width: 100%;
+ height: 100%;
+}
+.cGoods{
+ width: 96%;
+ margin: 0 auto 20upx;
+ padding: 20rpx 0;
+}
.scroll-view_H {
white-space: nowrap;
width: 100%;
- background-color: #eeeeee;
}
-
.scroll-view-item_H {
display: inline-block;
- width: 25%;
+ width: 200rpx;
+ padding-top: 20rpx;
text-align: center;
- font-size: 26rpx;
- padding: 20upx 0;
+ font-size: 36rpx;
+ background-color: #ffffff;
+ margin-right: 20rpx;
+ border-radius: 20rpx;
}
-.scroll-view-v{
+.scroll-view-item_H image{
+ width: 118rpx;
+ height: 128rpx;
+}
+
+.login-desc{
display: flex;
align-items: center;
- justify-content: center;
+ width: 96%;
+ position: fixed;
+ left: 2%;
+ bottom: calc(var(--window-bottom) + 20rpx);
+ height: 100rpx;
+ background: rgba(0,0,0,0.7);
+ border-radius: 106rpx 106rpx 106rpx 106rpx;
}
-.scroll-icon{
- width: 40upx;
- height: 40upx;
- border-radius: 30upx;
- margin-right: 10upx;
+.login-icon{
+ margin-left: 20rpx;
+ width: 90rpx;
+ height: 90rpx;
+ margin-right: 30rpx;
}
-::-webkit-scrollbar {
- width: 0px !important; /* 滚动条宽度 */
-}
-/* 设置滚动条宽度 */
-.uni-scroll-view::-webkit-scrollbar {
- width: 0px !important; /* 滚动条宽度 */
-}
-.scroll-active{
- background-color: #ffb16e;
+.login-font{
+ width: 55%;
+ font-size: 26rpx;
color: #ffffff;
}
+.login-btn{
+ width: 156rpx;
+ height: 54rpx;
+ text-align: center;
+ color: #ffffff;
+ line-height: 54rpx;
+ background: #FF1472;
+ border-radius: 62rpx 62rpx 62rpx 62rpx;
+}
+.c-goods{
+ background-color: #f5f5f5;
+}
\ No newline at end of file
diff --git a/static/css/my2.css b/static/css/my2.css
new file mode 100644
index 0000000..43e8603
--- /dev/null
+++ b/static/css/my2.css
@@ -0,0 +1,171 @@
+.myView-bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 448rpx;
+ z-index: 0;
+}
+.avatar{
+ width: 112rpx;
+ height: 112rpx;
+ margin: 50rpx 30rpx 50rpx 50rpx;
+ border-radius: 50%;
+}
+.myContent{
+ width: 100%;
+ position: absolute;
+}
+.myHeader{
+ color: #ffffff;
+ display: flex;
+ align-items: center;
+ font-size: 28rpx;
+}
+.myNick{
+ margin-top: 10rpx;
+}
+.edu {
+ margin-top: 10rpx;
+ width: 108rpx;
+ height: 44rpx;
+ background: rgba(255,255,255,0.1);
+ border-radius: 66rpx 66rpx 66rpx 66rpx;
+ border: 2rpx solid #FFFFFF;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 20rpx;
+}
+.auth{
+ display: flex;
+ align-items: center;
+ width: 650rpx;
+ height: 86rpx;
+ margin: 0 auto;
+ background: #FFFFFF;
+ border-radius: 68rpx 68rpx 68rpx 68rpx;
+ font-size: 24rpx;
+ color: #333333;
+}
+.auth image{
+ width: 48rpx;
+ height: 48rpx;
+ margin: 0 10rpx;
+}
+.auth text{
+ margin-right: 30rpx;
+}
+.auth view{
+ width: 100rpx;
+ height: 44rpx;
+ background: #EE2626;
+ border-radius: 50rpx 50rpx 50rpx 50rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: #ffffff;
+}
+
+.navs{
+ width: 100%;
+ /* width: 650rpx; */
+ margin: 30rpx auto;
+ background-color: #ffffff;
+ height: 236rpx;
+ margin-top: 110rpx;
+ /* border-radius: 20rpx;
+ box-shadow: 0px 0px 0px 1px #f5f5f5; */
+}
+.navs .title{
+ padding: 30rpx 20rpx;
+ font-size: 24rpx;
+}
+.navs .title text{
+ font-size: 28rpx;
+ color: #333333;
+ font-weight: 700;
+}
+.navs-rgt{
+ display: flex;
+ align-items: center;
+ float: right;
+}
+.navs-rgt text{
+ font-size: 24rpx !important;
+ font-weight: 500 !important;
+}
+.navs .title image{
+ margin-left: 5rpx;
+ width: 28rpx;
+ height: 28rpx;
+}
+.navCon {
+ margin-top: 15rpx;
+ display: flex;
+}
+.navCon .navCon-v{
+ flex: 1;
+ text-align: center;
+ font-size: 24rpx;
+}
+.navCon image{
+ display: block;
+ width: 64rpx;
+ height: 64rpx;
+ margin: 0 auto 10rpx;
+}
+.mySer{
+ width: 100%;
+ /* width: 650rpx; */
+ margin: 30rpx auto;
+ background-color: #ffffff;
+ /* height: 402rpx; */
+ /* border-radius: 20rpx;
+ box-shadow: 0px 0px 0px 1px #f5f5f5; */
+}
+.mySer .title{
+ padding: 30rpx 20rpx;
+ font-size: 28rpx;
+ color: #333333;
+ font-weight: 700;
+}
+.mySerCon{
+ display: flex;
+ flex-wrap: wrap;
+}
+.mySerCon .mySerCon-v{
+ width: 100%;
+ margin-top: 30rpx;
+ margin-bottom: 30rpx;
+ font-size: 28rpx;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ padding: 0 20rpx;
+ gap: 20rpx;
+}
+.mySerCon .mySerCon-l{
+ width: 100%;
+ font-size: 28rpx;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ gap: 20rpx;
+}
+.mySerCon image{
+ width: 40rpx;
+ height: 40rpx;
+ display: block;
+ /* margin: 0 auto 10rpx; */
+}
+.myBeiAn{
+ margin-top: 50rpx;
+ text-align: center;
+ font-size: 24rpx;
+ color: #666666;
+ height: 300rpx;
+}
+.myBeiAn view{
+ margin-bottom: 10rpx;
+}
\ No newline at end of file
diff --git a/static/css/search.css b/static/css/search.css
index 4a67a06..4c9b3ab 100644
--- a/static/css/search.css
+++ b/static/css/search.css
@@ -38,7 +38,7 @@
font-size: 26upx;
}
.searchKey{
- margin-top: 120upx;
+ margin-top: 160upx;
padding: 0 30upx;
}
.searchKey .title{
@@ -60,7 +60,7 @@
margin-right: 20upx;
}
.goodsData{
- margin-top: 120upx;
+ margin-top: 160upx;
}
.goods-icon{
width: 400upx;
diff --git a/static/img/A0.png b/static/img/A0.png
new file mode 100644
index 0000000..4d500c6
--- /dev/null
+++ b/static/img/A0.png
Binary files differ
diff --git a/static/img/BNM.png b/static/img/BNM.png
new file mode 100644
index 0000000..ee53a79
--- /dev/null
+++ b/static/img/BNM.png
Binary files differ
diff --git a/static/img/GG1.png b/static/img/GG1.png
new file mode 100644
index 0000000..dea9d7c
--- /dev/null
+++ b/static/img/GG1.png
Binary files differ
diff --git a/static/img/GG2.png b/static/img/GG2.png
new file mode 100644
index 0000000..05a0e8b
--- /dev/null
+++ b/static/img/GG2.png
Binary files differ
diff --git a/static/img/GG3.png b/static/img/GG3.png
new file mode 100644
index 0000000..52e672f
--- /dev/null
+++ b/static/img/GG3.png
Binary files differ
diff --git a/static/img/MKL.png b/static/img/MKL.png
new file mode 100644
index 0000000..744b6fc
--- /dev/null
+++ b/static/img/MKL.png
Binary files differ
diff --git a/static/img/back.png b/static/img/back.png
new file mode 100644
index 0000000..23b64fa
--- /dev/null
+++ b/static/img/back.png
Binary files differ
diff --git a/static/img/kk.png b/static/img/kk.png
new file mode 100644
index 0000000..5f74d06
--- /dev/null
+++ b/static/img/kk.png
Binary files differ
diff --git a/static/img/left.png b/static/img/left.png
new file mode 100644
index 0000000..3ec519e
--- /dev/null
+++ b/static/img/left.png
Binary files differ
diff --git a/static/img/nion.png b/static/img/nion.png
new file mode 100644
index 0000000..854fd59
--- /dev/null
+++ b/static/img/nion.png
Binary files differ
diff --git a/static/img/nion1.png b/static/img/nion1.png
new file mode 100644
index 0000000..9ed10c7
--- /dev/null
+++ b/static/img/nion1.png
Binary files differ
diff --git a/static/img/off.png b/static/img/off.png
new file mode 100644
index 0000000..2758512
--- /dev/null
+++ b/static/img/off.png
Binary files differ
diff --git a/static/img/qsBg.png b/static/img/qsBg.png
new file mode 100644
index 0000000..6a3f742
--- /dev/null
+++ b/static/img/qsBg.png
Binary files differ
diff --git a/static/img/qsBg2.png b/static/img/qsBg2.png
new file mode 100644
index 0000000..c38306b
--- /dev/null
+++ b/static/img/qsBg2.png
Binary files differ
diff --git a/static/img/qsLeft.png b/static/img/qsLeft.png
new file mode 100644
index 0000000..9e4addc
--- /dev/null
+++ b/static/img/qsLeft.png
Binary files differ
diff --git a/static/img/qsLife.png b/static/img/qsLife.png
new file mode 100644
index 0000000..99559b7
--- /dev/null
+++ b/static/img/qsLife.png
Binary files differ
diff --git a/static/img/qsLifeIcon.png b/static/img/qsLifeIcon.png
new file mode 100644
index 0000000..d53f50b
--- /dev/null
+++ b/static/img/qsLifeIcon.png
Binary files differ
diff --git a/static/img/qsPhysical.png b/static/img/qsPhysical.png
new file mode 100644
index 0000000..ca0de17
--- /dev/null
+++ b/static/img/qsPhysical.png
Binary files differ
diff --git a/static/img/qsRight.png b/static/img/qsRight.png
new file mode 100644
index 0000000..b56fd7b
--- /dev/null
+++ b/static/img/qsRight.png
Binary files differ
diff --git a/static/img/quanshou/1.png b/static/img/quanshou/1.png
new file mode 100644
index 0000000..699ed2e
--- /dev/null
+++ b/static/img/quanshou/1.png
Binary files differ
diff --git a/static/img/quanshou/10.png b/static/img/quanshou/10.png
new file mode 100644
index 0000000..ea5cc0d
--- /dev/null
+++ b/static/img/quanshou/10.png
Binary files differ
diff --git a/static/img/quanshou/11.png b/static/img/quanshou/11.png
new file mode 100644
index 0000000..cb50ffe
--- /dev/null
+++ b/static/img/quanshou/11.png
Binary files differ
diff --git a/static/img/quanshou/12.png b/static/img/quanshou/12.png
new file mode 100644
index 0000000..49a98ee
--- /dev/null
+++ b/static/img/quanshou/12.png
Binary files differ
diff --git a/static/img/quanshou/2.png b/static/img/quanshou/2.png
new file mode 100644
index 0000000..f37e0f7
--- /dev/null
+++ b/static/img/quanshou/2.png
Binary files differ
diff --git a/static/img/quanshou/3.png b/static/img/quanshou/3.png
new file mode 100644
index 0000000..899c43f
--- /dev/null
+++ b/static/img/quanshou/3.png
Binary files differ
diff --git a/static/img/quanshou/3A.png b/static/img/quanshou/3A.png
new file mode 100644
index 0000000..25be9ef
--- /dev/null
+++ b/static/img/quanshou/3A.png
Binary files differ
diff --git a/static/img/quanshou/4.png b/static/img/quanshou/4.png
new file mode 100644
index 0000000..2a917b1
--- /dev/null
+++ b/static/img/quanshou/4.png
Binary files differ
diff --git a/static/img/quanshou/5.png b/static/img/quanshou/5.png
new file mode 100644
index 0000000..9e37d0e
--- /dev/null
+++ b/static/img/quanshou/5.png
Binary files differ
diff --git a/static/img/quanshou/6.png b/static/img/quanshou/6.png
new file mode 100644
index 0000000..e04b35c
--- /dev/null
+++ b/static/img/quanshou/6.png
Binary files differ
diff --git a/static/img/quanshou/6A.png b/static/img/quanshou/6A.png
new file mode 100644
index 0000000..c083486
--- /dev/null
+++ b/static/img/quanshou/6A.png
Binary files differ
diff --git a/static/img/quanshou/7.png b/static/img/quanshou/7.png
new file mode 100644
index 0000000..478fdd1
--- /dev/null
+++ b/static/img/quanshou/7.png
Binary files differ
diff --git a/static/img/quanshou/8.png b/static/img/quanshou/8.png
new file mode 100644
index 0000000..24eab49
--- /dev/null
+++ b/static/img/quanshou/8.png
Binary files differ
diff --git a/static/img/quanshou/8A.png b/static/img/quanshou/8A.png
new file mode 100644
index 0000000..38c039f
--- /dev/null
+++ b/static/img/quanshou/8A.png
Binary files differ
diff --git a/static/img/quanshou/9.png b/static/img/quanshou/9.png
new file mode 100644
index 0000000..8743b31
--- /dev/null
+++ b/static/img/quanshou/9.png
Binary files differ
diff --git a/static/img/quanshou/BG1.png b/static/img/quanshou/BG1.png
new file mode 100644
index 0000000..3d76ad1
--- /dev/null
+++ b/static/img/quanshou/BG1.png
Binary files differ
diff --git a/static/img/quanshou/C1.png b/static/img/quanshou/C1.png
new file mode 100644
index 0000000..4f6e2e1
--- /dev/null
+++ b/static/img/quanshou/C1.png
Binary files differ
diff --git a/static/img/quanshou/C10.png b/static/img/quanshou/C10.png
new file mode 100644
index 0000000..e420f5d
--- /dev/null
+++ b/static/img/quanshou/C10.png
Binary files differ
diff --git a/static/img/quanshou/C11.png b/static/img/quanshou/C11.png
new file mode 100644
index 0000000..f61625d
--- /dev/null
+++ b/static/img/quanshou/C11.png
Binary files differ
diff --git a/static/img/quanshou/C12.png b/static/img/quanshou/C12.png
new file mode 100644
index 0000000..9d28cba
--- /dev/null
+++ b/static/img/quanshou/C12.png
Binary files differ
diff --git a/static/img/quanshou/C13.png b/static/img/quanshou/C13.png
new file mode 100644
index 0000000..5a10dbc
--- /dev/null
+++ b/static/img/quanshou/C13.png
Binary files differ
diff --git a/static/img/quanshou/C14.png b/static/img/quanshou/C14.png
new file mode 100644
index 0000000..9aeed22
--- /dev/null
+++ b/static/img/quanshou/C14.png
Binary files differ
diff --git a/static/img/quanshou/C15.png b/static/img/quanshou/C15.png
new file mode 100644
index 0000000..21ae755
--- /dev/null
+++ b/static/img/quanshou/C15.png
Binary files differ
diff --git a/static/img/quanshou/C2.png b/static/img/quanshou/C2.png
new file mode 100644
index 0000000..ab56831
--- /dev/null
+++ b/static/img/quanshou/C2.png
Binary files differ
diff --git a/static/img/quanshou/C3.png b/static/img/quanshou/C3.png
new file mode 100644
index 0000000..aecc8b9
--- /dev/null
+++ b/static/img/quanshou/C3.png
Binary files differ
diff --git a/static/img/quanshou/C4.png b/static/img/quanshou/C4.png
new file mode 100644
index 0000000..85f5fb5
--- /dev/null
+++ b/static/img/quanshou/C4.png
Binary files differ
diff --git a/static/img/quanshou/C5.png b/static/img/quanshou/C5.png
new file mode 100644
index 0000000..64ff2e9
--- /dev/null
+++ b/static/img/quanshou/C5.png
Binary files differ
diff --git a/static/img/quanshou/C6.png b/static/img/quanshou/C6.png
new file mode 100644
index 0000000..5d5d442
--- /dev/null
+++ b/static/img/quanshou/C6.png
Binary files differ
diff --git a/static/img/quanshou/C7.png b/static/img/quanshou/C7.png
new file mode 100644
index 0000000..c2c639f
--- /dev/null
+++ b/static/img/quanshou/C7.png
Binary files differ
diff --git a/static/img/quanshou/C8.png b/static/img/quanshou/C8.png
new file mode 100644
index 0000000..964cf54
--- /dev/null
+++ b/static/img/quanshou/C8.png
Binary files differ
diff --git a/static/img/quanshou/C9.png b/static/img/quanshou/C9.png
new file mode 100644
index 0000000..55d684a
--- /dev/null
+++ b/static/img/quanshou/C9.png
Binary files differ
diff --git a/static/img/quanshou/CY1.png b/static/img/quanshou/CY1.png
new file mode 100644
index 0000000..867d983
--- /dev/null
+++ b/static/img/quanshou/CY1.png
Binary files differ
diff --git a/static/img/quanshou/CY2.png b/static/img/quanshou/CY2.png
new file mode 100644
index 0000000..f0b9e6e
--- /dev/null
+++ b/static/img/quanshou/CY2.png
Binary files differ
diff --git a/static/img/quanshou/CY3.png b/static/img/quanshou/CY3.png
new file mode 100644
index 0000000..29bd2ca
--- /dev/null
+++ b/static/img/quanshou/CY3.png
Binary files differ
diff --git a/static/img/quanshou/CY4.png b/static/img/quanshou/CY4.png
new file mode 100644
index 0000000..69f677e
--- /dev/null
+++ b/static/img/quanshou/CY4.png
Binary files differ
diff --git a/static/img/quanshou/CY5.png b/static/img/quanshou/CY5.png
new file mode 100644
index 0000000..562e617
--- /dev/null
+++ b/static/img/quanshou/CY5.png
Binary files differ
diff --git a/static/img/quanshou/DC1.png b/static/img/quanshou/DC1.png
new file mode 100644
index 0000000..4585b84
--- /dev/null
+++ b/static/img/quanshou/DC1.png
Binary files differ
diff --git a/static/img/quanshou/DC2.png b/static/img/quanshou/DC2.png
new file mode 100644
index 0000000..a81733b
--- /dev/null
+++ b/static/img/quanshou/DC2.png
Binary files differ
diff --git a/static/img/quanshou/DC3.png b/static/img/quanshou/DC3.png
new file mode 100644
index 0000000..0665141
--- /dev/null
+++ b/static/img/quanshou/DC3.png
Binary files differ
diff --git a/static/img/quanshou/H1.png b/static/img/quanshou/H1.png
new file mode 100644
index 0000000..ea57802
--- /dev/null
+++ b/static/img/quanshou/H1.png
Binary files differ
diff --git a/static/img/quanshou/H2.png b/static/img/quanshou/H2.png
new file mode 100644
index 0000000..6c85c70
--- /dev/null
+++ b/static/img/quanshou/H2.png
Binary files differ
diff --git a/static/img/quanshou/H3.png b/static/img/quanshou/H3.png
new file mode 100644
index 0000000..d1a989e
--- /dev/null
+++ b/static/img/quanshou/H3.png
Binary files differ
diff --git a/static/img/quanshou/H4.png b/static/img/quanshou/H4.png
new file mode 100644
index 0000000..343049d
--- /dev/null
+++ b/static/img/quanshou/H4.png
Binary files differ
diff --git a/static/img/quanshou/H5.png b/static/img/quanshou/H5.png
new file mode 100644
index 0000000..2da2705
--- /dev/null
+++ b/static/img/quanshou/H5.png
Binary files differ
diff --git a/static/img/quanshou/H6.png b/static/img/quanshou/H6.png
new file mode 100644
index 0000000..d1e0d06
--- /dev/null
+++ b/static/img/quanshou/H6.png
Binary files differ
diff --git a/static/img/quanshou/L6.png b/static/img/quanshou/L6.png
new file mode 100644
index 0000000..2e01e91
--- /dev/null
+++ b/static/img/quanshou/L6.png
Binary files differ
diff --git a/static/img/quanshou/LS1.png b/static/img/quanshou/LS1.png
new file mode 100644
index 0000000..cf81028
--- /dev/null
+++ b/static/img/quanshou/LS1.png
Binary files differ
diff --git a/static/img/quanshou/LX1.png b/static/img/quanshou/LX1.png
new file mode 100644
index 0000000..430d7e7
--- /dev/null
+++ b/static/img/quanshou/LX1.png
Binary files differ
diff --git a/static/img/quanshou/LX2.png b/static/img/quanshou/LX2.png
new file mode 100644
index 0000000..8ad5200
--- /dev/null
+++ b/static/img/quanshou/LX2.png
Binary files differ
diff --git a/static/img/quanshou/LX3.png b/static/img/quanshou/LX3.png
new file mode 100644
index 0000000..8f5be65
--- /dev/null
+++ b/static/img/quanshou/LX3.png
Binary files differ
diff --git a/static/img/quanshou/LX4.png b/static/img/quanshou/LX4.png
new file mode 100644
index 0000000..1c96fd3
--- /dev/null
+++ b/static/img/quanshou/LX4.png
Binary files differ
diff --git a/static/img/quanshou/LX5.png b/static/img/quanshou/LX5.png
new file mode 100644
index 0000000..f10b3b4
--- /dev/null
+++ b/static/img/quanshou/LX5.png
Binary files differ
diff --git a/static/img/quanshou/LX6.png b/static/img/quanshou/LX6.png
new file mode 100644
index 0000000..2e01e91
--- /dev/null
+++ b/static/img/quanshou/LX6.png
Binary files differ
diff --git a/static/img/quanshou/QL.png b/static/img/quanshou/QL.png
new file mode 100644
index 0000000..18d899d
--- /dev/null
+++ b/static/img/quanshou/QL.png
Binary files differ
diff --git a/static/img/quanshou/Star.png b/static/img/quanshou/Star.png
new file mode 100644
index 0000000..2f9450f
--- /dev/null
+++ b/static/img/quanshou/Star.png
Binary files differ
diff --git a/static/img/quanshou/TBBG1.png b/static/img/quanshou/TBBG1.png
new file mode 100644
index 0000000..a35c126
--- /dev/null
+++ b/static/img/quanshou/TBBG1.png
Binary files differ
diff --git a/static/img/quanshou/TBBG2.png b/static/img/quanshou/TBBG2.png
new file mode 100644
index 0000000..debb466
--- /dev/null
+++ b/static/img/quanshou/TBBG2.png
Binary files differ
diff --git a/static/img/quanshou/TBBG3.png b/static/img/quanshou/TBBG3.png
new file mode 100644
index 0000000..a97d233
--- /dev/null
+++ b/static/img/quanshou/TBBG3.png
Binary files differ
diff --git a/static/img/quanshou/TY1.png b/static/img/quanshou/TY1.png
new file mode 100644
index 0000000..0e2654e
--- /dev/null
+++ b/static/img/quanshou/TY1.png
Binary files differ
diff --git a/static/img/quanshou/TY2.png b/static/img/quanshou/TY2.png
new file mode 100644
index 0000000..ddc53f1
--- /dev/null
+++ b/static/img/quanshou/TY2.png
Binary files differ
diff --git a/static/img/quanshou/TY3.png b/static/img/quanshou/TY3.png
new file mode 100644
index 0000000..97d6713
--- /dev/null
+++ b/static/img/quanshou/TY3.png
Binary files differ
diff --git a/static/img/quanshou/TY4.png b/static/img/quanshou/TY4.png
new file mode 100644
index 0000000..c10fc76
--- /dev/null
+++ b/static/img/quanshou/TY4.png
Binary files differ
diff --git a/static/img/quanshou/TY5.png b/static/img/quanshou/TY5.png
new file mode 100644
index 0000000..8a31e62
--- /dev/null
+++ b/static/img/quanshou/TY5.png
Binary files differ
diff --git a/static/img/quanshou/TY99.png b/static/img/quanshou/TY99.png
new file mode 100644
index 0000000..3fe5fb8
--- /dev/null
+++ b/static/img/quanshou/TY99.png
Binary files differ
diff --git a/static/img/quanshou/VIPBOX1.png b/static/img/quanshou/VIPBOX1.png
new file mode 100644
index 0000000..f75f343
--- /dev/null
+++ b/static/img/quanshou/VIPBOX1.png
Binary files differ
diff --git a/static/img/quanshou/VIPBOX2.png b/static/img/quanshou/VIPBOX2.png
new file mode 100644
index 0000000..5a780d6
--- /dev/null
+++ b/static/img/quanshou/VIPBOX2.png
Binary files differ
diff --git a/static/img/quanshou/VIPBOX3.png b/static/img/quanshou/VIPBOX3.png
new file mode 100644
index 0000000..e5d07fb
--- /dev/null
+++ b/static/img/quanshou/VIPBOX3.png
Binary files differ
diff --git a/static/img/quanshou/VIPLVL1.png b/static/img/quanshou/VIPLVL1.png
new file mode 100644
index 0000000..8fe864b
--- /dev/null
+++ b/static/img/quanshou/VIPLVL1.png
Binary files differ
diff --git a/static/img/quanshou/VIPLVL2.png b/static/img/quanshou/VIPLVL2.png
new file mode 100644
index 0000000..39c18ab
--- /dev/null
+++ b/static/img/quanshou/VIPLVL2.png
Binary files differ
diff --git a/static/img/quanshou/VIPLVL3.png b/static/img/quanshou/VIPLVL3.png
new file mode 100644
index 0000000..bbdbf19
--- /dev/null
+++ b/static/img/quanshou/VIPLVL3.png
Binary files differ
diff --git a/static/img/quanshou/VIPTXT1.png b/static/img/quanshou/VIPTXT1.png
new file mode 100644
index 0000000..0aafc27
--- /dev/null
+++ b/static/img/quanshou/VIPTXT1.png
Binary files differ
diff --git a/static/img/quanshou/VIPTXT2.png b/static/img/quanshou/VIPTXT2.png
new file mode 100644
index 0000000..5c10dd8
--- /dev/null
+++ b/static/img/quanshou/VIPTXT2.png
Binary files differ
diff --git a/static/img/quanshou/VIPTXT3.png b/static/img/quanshou/VIPTXT3.png
new file mode 100644
index 0000000..5045607
--- /dev/null
+++ b/static/img/quanshou/VIPTXT3.png
Binary files differ
diff --git a/static/img/quanshou/Y1.png b/static/img/quanshou/Y1.png
new file mode 100644
index 0000000..377434b
--- /dev/null
+++ b/static/img/quanshou/Y1.png
Binary files differ
diff --git a/static/img/quanshou/Y2.png b/static/img/quanshou/Y2.png
new file mode 100644
index 0000000..1c154fb
--- /dev/null
+++ b/static/img/quanshou/Y2.png
Binary files differ
diff --git a/static/img/quanshou/Y3.png b/static/img/quanshou/Y3.png
new file mode 100644
index 0000000..0445b7d
--- /dev/null
+++ b/static/img/quanshou/Y3.png
Binary files differ
diff --git a/static/img/quanshou/Y4.png b/static/img/quanshou/Y4.png
new file mode 100644
index 0000000..977c0be
--- /dev/null
+++ b/static/img/quanshou/Y4.png
Binary files differ
diff --git a/static/img/quanshou/ZQ1.png b/static/img/quanshou/ZQ1.png
new file mode 100644
index 0000000..b566551
--- /dev/null
+++ b/static/img/quanshou/ZQ1.png
Binary files differ
diff --git a/static/img/quanshou/ZQ2.png b/static/img/quanshou/ZQ2.png
new file mode 100644
index 0000000..f12221b
--- /dev/null
+++ b/static/img/quanshou/ZQ2.png
Binary files differ
diff --git a/static/img/quanshou/ZQ3.png b/static/img/quanshou/ZQ3.png
new file mode 100644
index 0000000..9c4cbcf
--- /dev/null
+++ b/static/img/quanshou/ZQ3.png
Binary files differ
diff --git a/static/img/qyBg.png b/static/img/qyBg.png
new file mode 100644
index 0000000..4f45e50
--- /dev/null
+++ b/static/img/qyBg.png
Binary files differ
diff --git a/static/img/qyBg2.png b/static/img/qyBg2.png
new file mode 100644
index 0000000..6f69900
--- /dev/null
+++ b/static/img/qyBg2.png
Binary files differ
diff --git a/static/img/smsBg.png b/static/img/smsBg.png
new file mode 100644
index 0000000..1e6fdf7
--- /dev/null
+++ b/static/img/smsBg.png
Binary files differ
diff --git a/static/img/yifa.png b/static/img/yifa.png
new file mode 100644
index 0000000..d19255a
--- /dev/null
+++ b/static/img/yifa.png
Binary files differ
diff --git a/static/imgs/ad1.png b/static/imgs/ad1.png
new file mode 100644
index 0000000..848f443
--- /dev/null
+++ b/static/imgs/ad1.png
Binary files differ
diff --git a/static/imgs/ad2.png b/static/imgs/ad2.png
new file mode 100644
index 0000000..d0f0840
--- /dev/null
+++ b/static/imgs/ad2.png
Binary files differ
diff --git a/static/imgs/ad3.png b/static/imgs/ad3.png
new file mode 100644
index 0000000..4652cff
--- /dev/null
+++ b/static/imgs/ad3.png
Binary files differ
diff --git a/static/imgs/ad4.png b/static/imgs/ad4.png
new file mode 100644
index 0000000..555968e
--- /dev/null
+++ b/static/imgs/ad4.png
Binary files differ
diff --git a/static/imgs/ad5.png b/static/imgs/ad5.png
new file mode 100644
index 0000000..bbe9a12
--- /dev/null
+++ b/static/imgs/ad5.png
Binary files differ
diff --git a/static/imgs/c1.png b/static/imgs/c1.png
new file mode 100644
index 0000000..cc5481e
--- /dev/null
+++ b/static/imgs/c1.png
Binary files differ
diff --git a/static/imgs/c2.png b/static/imgs/c2.png
new file mode 100644
index 0000000..50fd419
--- /dev/null
+++ b/static/imgs/c2.png
Binary files differ
diff --git a/static/imgs/deb.png b/static/imgs/deb.png
new file mode 100644
index 0000000..4bc1f8f
--- /dev/null
+++ b/static/imgs/deb.png
Binary files differ
diff --git a/static/imgs/index-logo.png b/static/imgs/index-logo.png
index 647bc75..e6b5d2b 100644
--- a/static/imgs/index-logo.png
+++ b/static/imgs/index-logo.png
Binary files differ
diff --git a/static/imgs/index-top.png b/static/imgs/index-top.png
new file mode 100644
index 0000000..3b5399c
--- /dev/null
+++ b/static/imgs/index-top.png
Binary files differ
diff --git a/static/imgs/input-bg.png b/static/imgs/input-bg.png
new file mode 100644
index 0000000..1920a2f
--- /dev/null
+++ b/static/imgs/input-bg.png
Binary files differ
diff --git a/static/imgs/logo-icon.png b/static/imgs/logo-icon.png
new file mode 100644
index 0000000..5f6a7af
--- /dev/null
+++ b/static/imgs/logo-icon.png
Binary files differ
diff --git a/static/imgs/mf1.png b/static/imgs/mf1.png
new file mode 100644
index 0000000..ea07c55
--- /dev/null
+++ b/static/imgs/mf1.png
Binary files differ
diff --git a/static/imgs/mf2.png b/static/imgs/mf2.png
new file mode 100644
index 0000000..19e1471
--- /dev/null
+++ b/static/imgs/mf2.png
Binary files differ
diff --git a/static/imgs/mf3.png b/static/imgs/mf3.png
new file mode 100644
index 0000000..65551c5
--- /dev/null
+++ b/static/imgs/mf3.png
Binary files differ
diff --git a/static/imgs/mf4.png b/static/imgs/mf4.png
new file mode 100644
index 0000000..3cb7a5e
--- /dev/null
+++ b/static/imgs/mf4.png
Binary files differ
diff --git a/static/imgs/mf5.png b/static/imgs/mf5.png
new file mode 100644
index 0000000..2390bc0
--- /dev/null
+++ b/static/imgs/mf5.png
Binary files differ
diff --git a/static/imgs/mf6.png b/static/imgs/mf6.png
new file mode 100644
index 0000000..e441b7f
--- /dev/null
+++ b/static/imgs/mf6.png
Binary files differ
diff --git a/static/imgs/more.png b/static/imgs/more.png
new file mode 100644
index 0000000..3caf0ab
--- /dev/null
+++ b/static/imgs/more.png
Binary files differ
diff --git a/static/imgs/my-bg.png b/static/imgs/my-bg.png
new file mode 100644
index 0000000..f45f6e3
--- /dev/null
+++ b/static/imgs/my-bg.png
Binary files differ
diff --git a/static/imgs/notice.png b/static/imgs/notice.png
new file mode 100644
index 0000000..d965912
--- /dev/null
+++ b/static/imgs/notice.png
Binary files differ
diff --git a/static/imgs/nv1.png b/static/imgs/nv1.png
new file mode 100644
index 0000000..bcb39f2
--- /dev/null
+++ b/static/imgs/nv1.png
Binary files differ
diff --git a/static/imgs/nv2.png b/static/imgs/nv2.png
new file mode 100644
index 0000000..c37454d
--- /dev/null
+++ b/static/imgs/nv2.png
Binary files differ
diff --git a/static/imgs/nv3.png b/static/imgs/nv3.png
new file mode 100644
index 0000000..e8f7f8d
--- /dev/null
+++ b/static/imgs/nv3.png
Binary files differ
diff --git a/static/imgs/nv4.png b/static/imgs/nv4.png
new file mode 100644
index 0000000..16aacab
--- /dev/null
+++ b/static/imgs/nv4.png
Binary files differ
diff --git a/static/imgs/nv5.png b/static/imgs/nv5.png
new file mode 100644
index 0000000..d1ace31
--- /dev/null
+++ b/static/imgs/nv5.png
Binary files differ
diff --git a/static/imgs/quanshou.png b/static/imgs/quanshou.png
index 18baf3d..f63b984 100644
--- a/static/imgs/quanshou.png
+++ b/static/imgs/quanshou.png
Binary files differ
diff --git a/static/imgs/right.png b/static/imgs/right.png
index f35efc9..43d1977 100644
--- a/static/imgs/right.png
+++ b/static/imgs/right.png
Binary files differ
diff --git a/static/imgs/tb1.png b/static/imgs/tb1.png
new file mode 100644
index 0000000..d9fb213
--- /dev/null
+++ b/static/imgs/tb1.png
Binary files differ
diff --git a/static/imgs/tb2.png b/static/imgs/tb2.png
new file mode 100644
index 0000000..159a854
--- /dev/null
+++ b/static/imgs/tb2.png
Binary files differ
diff --git a/static/imgs/tb3.png b/static/imgs/tb3.png
new file mode 100644
index 0000000..4e7d16d
--- /dev/null
+++ b/static/imgs/tb3.png
Binary files differ
diff --git a/static/imgs/tb4.png b/static/imgs/tb4.png
new file mode 100644
index 0000000..76f0eda
--- /dev/null
+++ b/static/imgs/tb4.png
Binary files differ
diff --git a/static/imgs/xxhf.png b/static/imgs/xxhf.png
new file mode 100644
index 0000000..6f519ab
--- /dev/null
+++ b/static/imgs/xxhf.png
Binary files differ
diff --git a/static/imgs/xxhh.png b/static/imgs/xxhh.png
new file mode 100644
index 0000000..9750541
--- /dev/null
+++ b/static/imgs/xxhh.png
Binary files differ
diff --git a/uni_modules/uni-popup/changelog.md b/uni_modules/uni-popup/changelog.md
index bc59f07..20b1e8c 100644
--- a/uni_modules/uni-popup/changelog.md
+++ b/uni_modules/uni-popup/changelog.md
@@ -1,3 +1,35 @@
+## 1.9.10(2025-07-18)
+- 修复 nvue 下弹窗样式错乱的问题 ,更新依赖 uni-transition 组件
+- 更新 示例取消 borderRadius 属性 ,如需内容圆角,用户应该直接在内容插槽中实现
+## 1.9.9(2025-06-11)
+- 修复 uni-popup-dialog 中 setVal 方法报错的问题
+- 修复 uni-popup-dialog 数据双向绑定问题。
+## 1.9.8(2025-04-16)
+- 修复 更新组件示例 ,解决更新数据或保存项目导致弹窗消失的问题
+## 1.9.7(2025-04-14)
+- 修复 uni-popup-dialog 弹出框在vue3中双向绑定问题
+## 1.9.6(2025-01-08)
+- 修复 示例中过期图片地址
+## 1.9.5(2024-10-15)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.9.2(2024-09-21)
+- 修复 uni-popup在android上的重复点击弹出位置不正确的bug
+## 1.9.1(2024-04-02)
+- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
+## 1.9.0(2024-03-28)
+- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正
+## 1.8.9(2024-03-20)
+- 修复 uni-popup-dialog 数据输入时修正为双向绑定
+## 1.8.8(2024-02-20)
+- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug
+## 1.8.7(2024-02-02)
+- 新增 uni-popup-dialog 新增属性focus:input模式下,是否自动自动聚焦
+## 1.8.6(2024-01-30)
+- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数
+## 1.8.5(2024-01-26)
+- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示
+## 1.8.4(2023-11-15)
+- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close`
## 1.8.3(2023-04-17)
- 修复 uni-popup 重复打开时的 bug
## 1.8.2(2023-02-02)
diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
index b5eee79..12ce2c9 100644
--- a/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
+++ b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue
@@ -10,14 +10,15 @@
</view>
<view v-else class="uni-dialog-content">
<slot>
- <input class="uni-dialog-input" v-model="val" :type="inputType" :placeholder="placeholderText" :focus="focus" >
+ <input class="uni-dialog-input" :maxlength="maxlength" v-model="val" :type="inputType"
+ :placeholder="placeholderText" :focus="focus">
</slot>
</view>
<view class="uni-dialog-button-group">
- <view class="uni-dialog-button" @click="closeDialog">
+ <view class="uni-dialog-button" v-if="showClose" @click="closeDialog">
<text class="uni-dialog-button-text">{{closeText}}</text>
</view>
- <view class="uni-dialog-button uni-border-left" @click="onOk">
+ <view class="uni-dialog-button" :class="showClose?'uni-border-left':''" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
</view>
</view>
@@ -28,16 +29,19 @@
<script>
import popup from '../uni-popup/popup.js'
import {
- initVueI18n
+ initVueI18n
} from '@dcloudio/uni-i18n'
import messages from '../uni-popup/i18n/index.js'
- const { t } = initVueI18n(messages)
+ const {
+ t
+ } = initVueI18n(messages)
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
+ * @property {Boolean} focus input模式下是否自动聚焦,默认为true
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
@@ -46,8 +50,10 @@
* @property {String} mode = [base|input] 模式、
* @value base 基础对话框
* @value input 可输入对话框
+ * @showClose {Boolean} 是否显示关闭按钮
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
+ * @property {Number} maxlength 输入
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
@@ -55,16 +61,30 @@
export default {
name: "uniPopupDialog",
mixins: [popup],
- emits:['confirm','close'],
+ emits: ['confirm', 'close', 'update:modelValue', 'input'],
props: {
- inputType:{
+ inputType: {
type: String,
default: 'text'
},
+ showClose: {
+ type: Boolean,
+ default: true
+ },
+ // #ifdef VUE2
value: {
type: [String, Number],
default: ''
},
+ // #endif
+ // #ifdef VUE3
+ modelValue: {
+ type: [Number, String],
+ default: ''
+ },
+ // #endif
+
+
placeholder: {
type: [String, Number],
default: ''
@@ -89,19 +109,26 @@
type: Boolean,
default: false
},
- cancelText:{
+ cancelText: {
type: String,
default: ''
},
- confirmText:{
+ confirmText: {
type: String,
default: ''
+ },
+ maxlength: {
+ type: Number,
+ default: -1,
+ },
+ focus: {
+ type: Boolean,
+ default: true,
}
},
data() {
return {
dialogType: 'error',
- focus: false,
val: ""
}
},
@@ -129,7 +156,22 @@
}
},
value(val) {
- this.val = val
+ this.setVal(val)
+ },
+ // #ifdef VUE3
+ modelValue(val) {
+ this.setVal(val)
+ },
+ // #endif
+ val(val) {
+ // #ifdef VUE2
+ // TODO 兼容 vue2
+ this.$emit('input', val);
+ // #endif
+ // #ifdef VUE3
+ // TODO 兼容 vue3
+ this.$emit('update:modelValue', val);
+ // #endif
}
},
created() {
@@ -138,25 +180,35 @@
// this.popup.closeMask()
if (this.mode === 'input') {
this.dialogType = 'info'
- this.val = this.value
+ this.val = this.value;
+ // #ifdef VUE3
+ this.val = this.modelValue;
+ // #endif
} else {
this.dialogType = this.type
}
},
- mounted() {
- this.focus = true
- },
methods: {
+ /**
+ * 给val属性赋值
+ */
+ setVal(val) {
+ if (this.maxlength != -1 && this.mode === 'input') {
+ this.val = val.slice(0, this.maxlength);
+ } else {
+ this.val = val
+ }
+ },
/**
* 点击确认按钮
*/
onOk() {
- if (this.mode === 'input'){
+ if (this.mode === 'input') {
this.$emit('confirm', this.val)
- }else{
+ } else {
this.$emit('confirm')
}
- if(this.beforeClose) return
+ if (this.beforeClose) return
this.popup.close()
},
/**
@@ -164,17 +216,17 @@
*/
closeDialog() {
this.$emit('close')
- if(this.beforeClose) return
+ if (this.beforeClose) return
this.popup.close()
},
- close(){
+ close() {
this.popup.close()
}
}
}
</script>
-<style lang="scss" >
+<style lang="scss">
.uni-popup-dialog {
width: 300px;
border-radius: 11px;
diff --git a/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
index 5be7624..c8945d5 100644
--- a/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
+++ b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue
@@ -39,24 +39,25 @@
},
data() {
return {
+ // TODO 替换为自己的图标
bottomData: [{
text: '微信',
- icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
+ icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'wx'
},
{
text: '支付宝',
- icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
- name: 'wx'
+ icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
+ name: 'ali'
},
{
text: 'QQ',
- icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
+ icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'qq'
},
{
text: '新浪',
- icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
+ icon: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
name: 'sina'
},
// {
diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
new file mode 100644
index 0000000..5eb8d5b
--- /dev/null
+++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
@@ -0,0 +1,90 @@
+<template>
+ <view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
+ <view @click.stop>
+ <slot></slot>
+ </view>
+ </view>
+</template>
+
+<script>
+ type CloseCallBack = ()=> void;
+ let closeCallBack:CloseCallBack = () :void => {};
+ export default {
+ emits:["close","clickMask"],
+ data() {
+ return {
+ isShow:false,
+ isOpen:false
+ }
+ },
+ props: {
+ maskClick: {
+ type: Boolean,
+ default: true
+ },
+ },
+ watch: {
+ // 设置show = true 时,如果没有 open 需要设置为 open
+ isShow:{
+ handler(isShow) {
+ // console.log("isShow",isShow)
+ if(isShow && this.isOpen == false){
+ this.isOpen = true
+ }
+ },
+ immediate:true
+ },
+ // 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow
+ isOpen:{
+ handler(isOpen) {
+ // console.log("isOpen",isOpen)
+ if(isOpen && this.isShow == false){
+ this.isShow = true
+ }
+ },
+ immediate:true
+ }
+ },
+ methods:{
+ open(){
+ // ...funs : CloseCallBack[]
+ // if(funs.length > 0){
+ // closeCallBack = funs[0]
+ // }
+ this.isOpen = true;
+ },
+ clickMask(){
+ if(this.maskClick == true){
+ this.$emit('clickMask')
+ this.close()
+ }
+ },
+ close(): void{
+ this.isOpen = false;
+ this.$emit('close')
+ closeCallBack()
+ },
+ hiden(){
+ this.isShow = false
+ },
+ show(){
+ this.isShow = true
+ }
+ }
+ }
+</script>
+
+<style>
+.popup-root {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 750rpx;
+ height: 100%;
+ flex: 1;
+ background-color: rgba(0, 0, 0, 0.3);
+ justify-content: center;
+ align-items: center;
+ z-index: 99;
+}
+</style>
\ No newline at end of file
diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.vue b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue
index 2487d13..5af55e0 100644
--- a/uni_modules/uni-popup/components/uni-popup/uni-popup.vue
+++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue
@@ -5,7 +5,7 @@
:duration="duration" :show="showTrans" @click="onTap" />
<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
:show="showTrans" @click="onTap">
- <view class="uni-popup__wrapper" :style="{ backgroundColor: bg }" :class="[popupstyle]" @click="clear">
+ <view class="uni-popup__wrapper" :style="getStyles" :class="[popupstyle]" @click="clear">
<slot />
</view>
</uni-transition>
@@ -39,6 +39,7 @@
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
* @property {String} backgroundColor 主窗口背景色
* @property {String} maskBackgroundColor 蒙版颜色
+ * @property {String} borderRadius 设置圆角(左上、右上、右下和左下) 示例:"10px 10px 10px 10px"
* @property {Boolean} safeArea 是否适配底部安全区
* @event {Function} change 打开关闭弹窗触发,e={show: false}
* @event {Function} maskClick 点击遮罩触发
@@ -86,6 +87,9 @@
type: String,
default: 'rgba(0, 0, 0, 0.4)'
},
+ borderRadius:{
+ type: String,
+ }
},
watch: {
@@ -157,16 +161,25 @@
backgroundColor: 'rgba(0, 0, 0, 0.4)'
},
transClass: {
+ backgroundColor: 'transparent',
+ borderRadius: this.borderRadius || "0",
position: 'fixed',
left: 0,
right: 0
},
maskShow: true,
mkclick: true,
- popupstyle: this.isDesktop ? 'fixforpc-top' : 'top'
+ popupstyle: 'top'
}
},
computed: {
+ getStyles() {
+ let res = { backgroundColor: this.bg };
+ if (this.borderRadius || "0") {
+ res = Object.assign(res, { borderRadius: this.borderRadius })
+ }
+ return res;
+ },
isDesktop() {
return this.popupWidth >= 500 && this.popupHeight >= 500
},
@@ -179,6 +192,17 @@
},
mounted() {
const fixSize = () => {
+ // #ifdef MP-WEIXIN
+ const {
+ windowWidth,
+ windowHeight,
+ windowTop,
+ safeArea,
+ screenHeight,
+ safeAreaInsets
+ } = uni.getWindowInfo()
+ // #endif
+ // #ifndef MP-WEIXIN
const {
windowWidth,
windowHeight,
@@ -187,6 +211,7 @@
screenHeight,
safeAreaInsets
} = uni.getSystemInfoSync()
+ // #endif
this.popupWidth = windowWidth
this.popupHeight = windowHeight + (windowTop || 0)
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
@@ -221,6 +246,12 @@
this.setH5Visible()
},
// #endif
+ activated() {
+ this.setH5Visible(!this.showPopup);
+ },
+ deactivated() {
+ this.setH5Visible(true);
+ },
created() {
// this.mkclick = this.isMaskClick || this.maskClick
if (this.isMaskClick === null && this.maskClick === null) {
@@ -240,10 +271,10 @@
this.maskClass.backgroundColor = this.maskBackgroundColor
},
methods: {
- setH5Visible() {
+ setH5Visible(visible = true) {
// #ifdef H5
// fix by mehaotian 处理 h5 滚动穿透的问题
- document.getElementsByTagName('body')[0].style.overflow = 'visible'
+ document.getElementsByTagName('body')[0].style.overflow = visible ? "visible" : "hidden";
// #endif
},
/**
@@ -323,13 +354,15 @@
position: 'fixed',
left: 0,
right: 0,
- backgroundColor: this.bg
+ backgroundColor: this.bg,
+ borderRadius:this.borderRadius || "0"
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
this.showPopup = true
this.showTrans = true
this.$nextTick(() => {
+ this.showPoptrans()
if (this.messageChild && this.type === 'message') {
this.messageChild.timerClose()
}
@@ -346,22 +379,26 @@
left: 0,
right: 0,
bottom: 0,
- //paddingBottom: 'constant(safe-area-inset-bottom)', ///兼容 IOS<11.2/
- //paddingBottom: 'env(safe-area-inset-bottom)', ///兼容 IOS>11.2/
- //paddingBottom: this.safeAreaInsets + 'px',
- backgroundColor: this.bg
+ paddingBottom: this.safeAreaInsets + 'px',
+ backgroundColor: this.bg,
+ borderRadius:this.borderRadius || "0",
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
- this.showPopup = true
- this.showTrans = true
+ this.showPoptrans()
},
/**
* 中间弹出样式处理
*/
center(type) {
this.popupstyle = 'center'
- this.ani = ['zoom-out', 'fade']
+ //微信小程序下,组合动画会出现文字向上闪动问题,再此做特殊处理
+ // #ifdef MP-WEIXIN
+ this.ani = ['fade']
+ // #endif
+ // #ifndef MP-WEIXIN
+ this.ani = ['zoom-out', 'fade']
+ // #endif
this.transClass = {
position: 'fixed',
/* #ifndef APP-NVUE */
@@ -373,12 +410,12 @@
right: 0,
top: 0,
justifyContent: 'center',
- alignItems: 'center'
+ alignItems: 'center',
+ borderRadius:this.borderRadius || "0"
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
- this.showPopup = true
- this.showTrans = true
+ this.showPoptrans()
},
left(type) {
this.popupstyle = 'left'
@@ -389,6 +426,7 @@
bottom: 0,
top: 0,
backgroundColor: this.bg,
+ borderRadius:this.borderRadius || "0",
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
@@ -396,8 +434,7 @@
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
- this.showPopup = true
- this.showTrans = true
+ this.showPoptrans()
},
right(type) {
this.popupstyle = 'right'
@@ -408,6 +445,7 @@
right: 0,
top: 0,
backgroundColor: this.bg,
+ borderRadius:this.borderRadius || "0",
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column'
@@ -415,8 +453,13 @@
}
// TODO 兼容 type 属性 ,后续会废弃
if (type) return
- this.showPopup = true
- this.showTrans = true
+ this.showPoptrans()
+ },
+ showPoptrans(){
+ this.$nextTick(()=>{
+ this.showPopup = true
+ this.showTrans = true
+ })
}
}
}
diff --git a/uni_modules/uni-popup/package.json b/uni_modules/uni-popup/package.json
index f40556b..ae01918 100644
--- a/uni_modules/uni-popup/package.json
+++ b/uni_modules/uni-popup/package.json
@@ -1,87 +1,107 @@
{
- "id": "uni-popup",
- "displayName": "uni-popup 弹出层",
- "version": "1.8.3",
- "description": " Popup 组件,提供常用的弹层",
- "keywords": [
- "uni-ui",
- "弹出层",
- "弹窗",
- "popup",
- "弹框"
+ "id": "uni-popup",
+ "displayName": "uni-popup 弹出层",
+ "version": "1.9.10",
+ "description": " Popup 组件,提供常用的弹层",
+ "keywords": [
+ "uni-ui",
+ "弹出层",
+ "弹窗",
+ "popup",
+ "弹框"
+],
+ "repository": "https://github.com/dcloudio/uni-ui",
+ "engines": {
+ "HBuilderX": "",
+ "uni-app": "^4.06",
+ "uni-app-x": ""
+ },
+ "directories": {
+ "example": "../../temps/example_temps"
+ },
+ "dcloudext": {
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+ "type": "component-vue",
+ "darkmode": "x",
+ "i18n": "x",
+ "widescreen": "x"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uni-scss",
+ "uni-transition"
],
- "repository": "https://github.com/dcloudio/uni-ui",
- "engines": {
- "HBuilderX": ""
- },
- "directories": {
- "example": "../../temps/example_temps"
- },
- "dcloudext": {
- "sale": {
- "regular": {
- "price": "0.00"
- },
- "sourcecode": {
- "price": "0.00"
- }
- },
- "contact": {
- "qq": ""
- },
- "declaration": {
- "ads": "无",
- "data": "无",
- "permissions": "无"
- },
- "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
- "type": "component-vue"
- },
- "uni_modules": {
- "dependencies": [
- "uni-scss",
- "uni-transition"
- ],
- "encrypt": [],
- "platforms": {
- "cloud": {
- "tcb": "y",
- "aliyun": "y"
- },
- "client": {
- "App": {
- "app-vue": "y",
- "app-nvue": "y"
- },
- "H5-mobile": {
- "Safari": "y",
- "Android Browser": "y",
- "微信浏览器(Android)": "y",
- "QQ浏览器(Android)": "y"
- },
- "H5-pc": {
- "Chrome": "y",
- "IE": "y",
- "Edge": "y",
- "Firefox": "y",
- "Safari": "y"
- },
- "小程序": {
- "微信": "y",
- "阿里": "y",
- "百度": "y",
- "字节跳动": "y",
- "QQ": "y"
- },
- "快应用": {
- "华为": "u",
- "联盟": "u"
- },
- "Vue": {
- "vue2": "y",
- "vue3": "y"
- }
- }
- }
- }
-}
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "x",
+ "aliyun": "x",
+ "alipay": "x"
+ },
+ "client": {
+ "uni-app": {
+ "vue": {
+ "vue2": "√",
+ "vue3": "√"
+ },
+ "web": {
+ "safari": "√",
+ "chrome": "√"
+ },
+ "app": {
+ "vue": "√",
+ "nvue": "√",
+ "android": "√",
+ "ios": "√",
+ "harmony": "√"
+ },
+ "mp": {
+ "weixin": "√",
+ "alipay": "√",
+ "toutiao": "√",
+ "baidu": "√",
+ "kuaishou": "-",
+ "jd": "-",
+ "harmony": "-",
+ "qq": "√",
+ "lark": "-"
+ },
+ "quickapp": {
+ "huawei": "-",
+ "union": "-"
+ }
+ },
+ "uni-app-x": {
+ "web": {
+ "safari": "√",
+ "chrome": "√"
+ },
+ "app": {
+ "android": "√",
+ "ios": "√",
+ "harmony": "√"
+ },
+ "mp": {
+ "weixin": "√"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-transition/changelog.md b/uni_modules/uni-transition/changelog.md
index b1a824b..01bfb58 100644
--- a/uni_modules/uni-transition/changelog.md
+++ b/uni_modules/uni-transition/changelog.md
@@ -1,3 +1,14 @@
+## 1.3.6(2025-07-18)
+- 修复 nvue 页面,样式错误问题
+## 1.3.5(2025-06-11)
+- 修复 第一次执行不显示动画的问题
+## 1.3.4(2025-04-16)
+- 修复 页面数据更新到底动画复原的问题
+- 修复 示例页面打开报错的问题
+## 1.3.3(2024-04-23)
+- 修复 当元素会受变量影响自动隐藏的bug
+## 1.3.2(2023-05-04)
+- 修复 NVUE 平台报错的问题
## 1.3.1(2021-11-23)
- 修复 init 方法初始化问题
## 1.3.0(2021-11-19)
diff --git a/uni_modules/uni-transition/components/uni-transition/createAnimation.js b/uni_modules/uni-transition/components/uni-transition/createAnimation.js
index 5f54365..8f89b18 100644
--- a/uni_modules/uni-transition/components/uni-transition/createAnimation.js
+++ b/uni_modules/uni-transition/components/uni-transition/createAnimation.js
@@ -10,7 +10,10 @@
class MPAnimation {
constructor(options, _this) {
this.options = options
- this.animation = uni.createAnimation(options)
+ // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
+ this.animation = uni.createAnimation({
+ ...options
+ })
this.currentStepAnimates = {}
this.next = 0
this.$ = _this
diff --git a/uni_modules/uni-transition/components/uni-transition/uni-transition.vue b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue
index 0d739bd..7677553 100644
--- a/uni_modules/uni-transition/components/uni-transition/uni-transition.vue
+++ b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue
@@ -1,277 +1,292 @@
<template>
- <view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
+ <!-- #ifndef APP-NVUE -->
+ <view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick">
+ <slot></slot>
+ </view>
+ <!-- #endif -->
+ <!-- #ifdef APP-NVUE -->
+ <view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick">
+ <slot></slot>
+ </view>
+ <!-- #endif -->
</template>
<script>
-import { createAnimation } from './createAnimation'
+ import { createAnimation } from './createAnimation'
-/**
- * Transition 过渡动画
- * @description 简单过渡动画组件
- * @tutorial https://ext.dcloud.net.cn/plugin?id=985
- * @property {Boolean} show = [false|true] 控制组件显示或隐藏
- * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
- * @value fade 渐隐渐出过渡
- * @value slide-top 由上至下过渡
- * @value slide-right 由右至左过渡
- * @value slide-bottom 由下至上过渡
- * @value slide-left 由左至右过渡
- * @value zoom-in 由小到大过渡
- * @value zoom-out 由大到小过渡
- * @property {Number} duration 过渡动画持续时间
- * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
- */
-export default {
- name: 'uniTransition',
- emits:['click','change'],
- props: {
- show: {
- type: Boolean,
- default: false
- },
- modeClass: {
- type: [Array, String],
- default() {
- return 'fade'
- }
- },
- duration: {
- type: Number,
- default: 300
- },
- styles: {
- type: Object,
- default() {
- return {}
- }
- },
- customClass:{
- type: String,
- default: ''
- }
- },
- data() {
- return {
- isShow: false,
- transform: '',
- opacity: 1,
- animationData: {},
- durationTime: 300,
- config: {}
- }
- },
- watch: {
- show: {
- handler(newVal) {
- if (newVal) {
- this.open()
- } else {
- // 避免上来就执行 close,导致动画错乱
- if (this.isShow) {
- this.close()
- }
+ /**
+ * Transition 过渡动画
+ * @description 简单过渡动画组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=985
+ * @property {Boolean} show = [false|true] 控制组件显示或隐藏
+ * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
+ * @value fade 渐隐渐出过渡
+ * @value slide-top 由上至下过渡
+ * @value slide-right 由右至左过渡
+ * @value slide-bottom 由下至上过渡
+ * @value slide-left 由左至右过渡
+ * @value zoom-in 由小到大过渡
+ * @value zoom-out 由大到小过渡
+ * @property {Number} duration 过渡动画持续时间
+ * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
+ */
+ export default {
+ name: 'uniTransition',
+ emits: ['click', 'change'],
+ props: {
+ show: {
+ type: Boolean,
+ default: false
+ },
+ modeClass: {
+ type: [Array, String],
+ default () {
+ return 'fade'
}
},
- immediate: true
- }
- },
- computed: {
- // 生成样式数据
- stylesObject() {
- let styles = {
- ...this.styles,
- 'transition-duration': this.duration / 1000 + 's'
- }
- let transform = ''
- for (let i in styles) {
- let line = this.toLine(i)
- transform += line + ':' + styles[i] + ';'
- }
- return transform
- },
- // 初始化动画条件
- transformStyles() {
- return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
- }
- },
- created() {
- // 动画默认配置
- this.config = {
- duration: this.duration,
- timingFunction: 'ease',
- transformOrigin: '50% 50%',
- delay: 0
- }
- this.durationTime = this.duration
- },
- methods: {
- /**
- * ref 触发 初始化动画
- */
- init(obj = {}) {
- if (obj.duration) {
- this.durationTime = obj.duration
- }
- this.animation = createAnimation(Object.assign(this.config, obj),this)
- },
- /**
- * 点击组件触发回调
- */
- onClick() {
- this.$emit('click', {
- detail: this.isShow
- })
- },
- /**
- * ref 触发 动画分组
- * @param {Object} obj
- */
- step(obj, config = {}) {
- if (!this.animation) return
- for (let i in obj) {
- try {
- if(typeof obj[i] === 'object'){
- this.animation[i](...obj[i])
- }else{
- this.animation[i](obj[i])
- }
- } catch (e) {
- console.error(`方法 ${i} 不存在`)
+ duration: {
+ type: Number,
+ default: 300
+ },
+ styles: {
+ type: Object,
+ default () {
+ return {}
}
+ },
+ customClass: {
+ type: String,
+ default: ''
+ },
+ onceRender: {
+ type: Boolean,
+ default: false
+ },
+ },
+ data() {
+ return {
+ isShow: false,
+ transform: '',
+ opacity: 0,
+ animationData: {},
+ durationTime: 300,
+ config: {}
}
- this.animation.step(config)
- return this
},
- /**
- * ref 触发 执行动画
- */
- run(fn) {
- if (!this.animation) return
- this.animation.run(fn)
- },
- // 开始过度动画
- open() {
- clearTimeout(this.timer)
- this.transform = ''
- this.isShow = true
- let { opacity, transform } = this.styleInit(false)
- if (typeof opacity !== 'undefined') {
- this.opacity = opacity
+ watch: {
+ show: {
+ handler(newVal) {
+ if (newVal) {
+ this.open()
+ } else {
+ // 避免上来就执行 close,导致动画错乱
+ if (this.isShow) {
+ this.close()
+ }
+ }
+ },
+ immediate: true
}
- this.transform = transform
- // 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
- this.$nextTick(() => {
- // TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
- this.timer = setTimeout(() => {
- this.animation = createAnimation(this.config, this)
- this.tranfromInit(false).step()
- this.animation.run()
- this.$emit('change', {
- detail: this.isShow
- })
- }, 20)
- })
},
- // 关闭过度动画
- close(type) {
- if (!this.animation) return
- this.tranfromInit(true)
- .step()
- .run(() => {
- this.isShow = false
- this.animationData = null
- this.animation = null
- let { opacity, transform } = this.styleInit(false)
- this.opacity = opacity || 1
- this.transform = transform
- this.$emit('change', {
- detail: this.isShow
- })
- })
- },
- // 处理动画开始前的默认样式
- styleInit(type) {
- let styles = {
- transform: ''
- }
- let buildStyle = (type, mode) => {
- if (mode === 'fade') {
- styles.opacity = this.animationType(type)[mode]
- } else {
- styles.transform += this.animationType(type)[mode] + ' '
+ computed: {
+ // 生成样式数据
+ stylesObject() {
+ let styles = {
+ ...this.styles,
+ 'transition-duration': this.duration / 1000 + 's'
}
- }
- if (typeof this.modeClass === 'string') {
- buildStyle(type, this.modeClass)
- } else {
- this.modeClass.forEach(mode => {
- buildStyle(type, mode)
- })
- }
- return styles
- },
- // 处理内置组合动画
- tranfromInit(type) {
- let buildTranfrom = (type, mode) => {
- let aniNum = null
- if (mode === 'fade') {
- aniNum = type ? 0 : 1
- } else {
- aniNum = type ? '-100%' : '0'
- if (mode === 'zoom-in') {
- aniNum = type ? 0.8 : 1
- }
- if (mode === 'zoom-out') {
- aniNum = type ? 1.2 : 1
- }
- if (mode === 'slide-right') {
- aniNum = type ? '100%' : '0'
- }
- if (mode === 'slide-bottom') {
- aniNum = type ? '100%' : '0'
- }
+ let transform = ''
+ for (let i in styles) {
+ let line = this.toLine(i)
+ transform += line + ':' + styles[i] + ';'
}
- this.animation[this.animationMode()[mode]](aniNum)
+ return transform
+ },
+ // 初始化动画条件
+ transformStyles() {
+ return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
}
- if (typeof this.modeClass === 'string') {
- buildTranfrom(type, this.modeClass)
- } else {
- this.modeClass.forEach(mode => {
- buildTranfrom(type, mode)
+ },
+ created() {
+ // 动画默认配置
+ this.config = {
+ duration: this.duration,
+ timingFunction: 'ease',
+ transformOrigin: '50% 50%',
+ delay: 0
+ }
+ this.durationTime = this.duration
+ },
+ methods: {
+ /**
+ * ref 触发 初始化动画
+ */
+ init(obj = {}) {
+ if (obj.duration) {
+ this.durationTime = obj.duration
+ }
+ this.animation = createAnimation(Object.assign(this.config, obj), this)
+ },
+ /**
+ * 点击组件触发回调
+ */
+ onClick() {
+ this.$emit('click', {
+ detail: this.isShow
})
- }
+ },
+ /**
+ * ref 触发 动画分组
+ * @param {Object} obj
+ */
+ step(obj, config = {}) {
+ if (!this.animation) return this
+ Object.keys(obj).forEach(key => {
+ const value = obj[key]
+ if (typeof this.animation[key] === 'function') {
+ Array.isArray(value) ?
+ this.animation[key](...value) :
+ this.animation[key](value)
+ }
+ })
+ this.animation.step(config)
+ return this
+ },
+ /**
+ * ref 触发 执行动画
+ */
+ run(fn) {
+ if (!this.animation) return
+ this.animation.run(fn)
+ },
+ // 开始过度动画
+ open() {
+ clearTimeout(this.timer)
+ this.isShow = true
+ // 新增初始状态重置逻辑(关键)
+ this.transform = this.styleInit(false).transform || ''
+ this.opacity = this.styleInit(false).opacity || 0
- return this.animation
- },
- animationType(type) {
- return {
- fade: type ? 1 : 0,
- 'slide-top': `translateY(${type ? '0' : '-100%'})`,
- 'slide-right': `translateX(${type ? '0' : '100%'})`,
- 'slide-bottom': `translateY(${type ? '0' : '100%'})`,
- 'slide-left': `translateX(${type ? '0' : '-100%'})`,
- 'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
- 'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
+ // 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
+ this.$nextTick(() => {
+ // TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
+ this.timer = setTimeout(() => {
+ this.animation = createAnimation(this.config, this)
+ this.tranfromInit(false).step()
+ this.animation.run(() => {
+ // #ifdef APP-NVUE
+ this.transform = this.styleInit(false).transform || ''
+ this.opacity = this.styleInit(false).opacity || 1
+ // #endif
+ // #ifndef APP-NVUE
+ this.transform = ''
+ this.opacity = this.styleInit(false).opacity || 1
+ // #endif
+ this.$emit('change', {
+ detail: this.isShow
+ })
+ })
+ }, 80)
+ })
+ },
+ // 关闭过度动画
+ close(type) {
+ if (!this.animation) return
+ this.tranfromInit(true)
+ .step()
+ .run(() => {
+ this.isShow = false
+ this.animationData = null
+ this.animation = null
+ let { opacity, transform } = this.styleInit(false)
+ this.opacity = opacity || 1
+ this.transform = transform
+ this.$emit('change', {
+ detail: this.isShow
+ })
+ })
+ },
+ // 处理动画开始前的默认样式
+ styleInit(type) {
+ let styles = { transform: '', opacity: 1 }
+ const buildStyle = (type, mode) => {
+ const value = this.animationType(type)[mode] // 直接使用 type 控制状态
+ if (mode.startsWith('fade')) {
+ styles.opacity = value
+ } else {
+ styles.transform += value + ' '
+ }
+ }
+
+ if (typeof this.modeClass === 'string') {
+ buildStyle(type, this.modeClass)
+ } else {
+ this.modeClass.forEach(mode => buildStyle(type, mode))
+ }
+ return styles
+ },
+ // 处理内置组合动画
+ tranfromInit(type) {
+ let buildTranfrom = (type, mode) => {
+ let aniNum = null
+ if (mode === 'fade') {
+ aniNum = type ? 0 : 1
+ } else {
+ aniNum = type ? '-100%' : '0'
+ if (mode === 'zoom-in') {
+ aniNum = type ? 0.8 : 1
+ }
+ if (mode === 'zoom-out') {
+ aniNum = type ? 1.2 : 1
+ }
+ if (mode === 'slide-right') {
+ aniNum = type ? '100%' : '0'
+ }
+ if (mode === 'slide-bottom') {
+ aniNum = type ? '100%' : '0'
+ }
+ }
+ this.animation[this.animationMode()[mode]](aniNum)
+ }
+ if (typeof this.modeClass === 'string') {
+ buildTranfrom(type, this.modeClass)
+ } else {
+ this.modeClass.forEach(mode => {
+ buildTranfrom(type, mode)
+ })
+ }
+
+ return this.animation
+ },
+ animationType(type) {
+ return {
+ fade: type ? 1 : 0,
+ 'slide-top': `translateY(${type ? '0' : '-100%'})`,
+ 'slide-right': `translateX(${type ? '0' : '100%'})`,
+ 'slide-bottom': `translateY(${type ? '0' : '100%'})`,
+ 'slide-left': `translateX(${type ? '0' : '-100%'})`,
+ 'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
+ 'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
+ }
+ },
+ // 内置动画类型与实际动画对应字典
+ animationMode() {
+ return {
+ fade: 'opacity',
+ 'slide-top': 'translateY',
+ 'slide-right': 'translateX',
+ 'slide-bottom': 'translateY',
+ 'slide-left': 'translateX',
+ 'zoom-in': 'scale',
+ 'zoom-out': 'scale'
+ }
+ },
+ // 驼峰转中横线
+ toLine(name) {
+ return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
- },
- // 内置动画类型与实际动画对应字典
- animationMode() {
- return {
- fade: 'opacity',
- 'slide-top': 'translateY',
- 'slide-right': 'translateX',
- 'slide-bottom': 'translateY',
- 'slide-left': 'translateX',
- 'zoom-in': 'scale',
- 'zoom-out': 'scale'
- }
- },
- // 驼峰转中横线
- toLine(name) {
- return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
}
-}
</script>
<style></style>
diff --git a/uni_modules/uni-transition/package.json b/uni_modules/uni-transition/package.json
index d15fdf0..0542c52 100644
--- a/uni_modules/uni-transition/package.json
+++ b/uni_modules/uni-transition/package.json
@@ -1,7 +1,7 @@
{
"id": "uni-transition",
"displayName": "uni-transition 过渡动画",
- "version": "1.3.1",
+ "version": "1.3.6",
"description": "元素的简单过渡动画",
"keywords": [
"uni-ui",
@@ -12,16 +12,14 @@
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
- "HBuilderX": ""
+ "HBuilderX": "",
+ "uni-app": "^4.12",
+ "uni-app-x": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
- "category": [
- "前端组件",
- "通用组件"
- ],
"sale": {
"regular": {
"price": "0.00"
@@ -38,48 +36,75 @@
"data": "无",
"permissions": "无"
},
- "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
+ "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+ "type": "component-vue",
+ "darkmode": "x",
+ "i18n": "x",
+ "widescreen": "x"
},
"uni_modules": {
- "dependencies": ["uni-scss"],
+ "dependencies": [
+ "uni-scss"
+ ],
"encrypt": [],
"platforms": {
"cloud": {
- "tcb": "y",
- "aliyun": "y"
+ "tcb": "x",
+ "aliyun": "x",
+ "alipay": "x"
},
"client": {
- "App": {
- "app-vue": "y",
- "app-nvue": "y"
+ "uni-app": {
+ "vue": {
+ "vue2": "√",
+ "vue3": "√"
+ },
+ "web": {
+ "safari": "√",
+ "chrome": "√"
+ },
+ "app": {
+ "vue": "√",
+ "nvue": "√",
+ "android": "√",
+ "ios": "√",
+ "harmony": "√"
+ },
+ "mp": {
+ "weixin": {
+ },
+ "alipay": {
+ },
+ "toutiao": {
+ },
+ "baidu": {
+ },
+ "kuaishou": {
+ },
+ "jd": {
+ },
+ "harmony": "-",
+ "qq": "√",
+ "lark": "-"
+ },
+ "quickapp": {
+ "huawei": "√",
+ "union": "√"
+ }
},
- "H5-mobile": {
- "Safari": "y",
- "Android Browser": "y",
- "微信浏览器(Android)": "y",
- "QQ浏览器(Android)": "y"
- },
- "H5-pc": {
- "Chrome": "y",
- "IE": "y",
- "Edge": "y",
- "Firefox": "y",
- "Safari": "y"
- },
- "小程序": {
- "微信": "y",
- "阿里": "y",
- "百度": "y",
- "字节跳动": "y",
- "QQ": "y"
- },
- "快应用": {
- "华为": "u",
- "联盟": "u"
- },
- "Vue": {
- "vue2": "y",
- "vue3": "y"
+ "uni-app-x": {
+ "web": {
+ "safari": "-",
+ "chrome": "-"
+ },
+ "app": {
+ "android": "-",
+ "ios": "-",
+ "harmony": "-"
+ },
+ "mp": {
+ "weixin": "-"
+ }
}
}
}
diff --git a/uni_modules/uv-album/changelog.md b/uni_modules/uv-album/changelog.md
new file mode 100644
index 0000000..42ab5e2
--- /dev/null
+++ b/uni_modules/uv-album/changelog.md
@@ -0,0 +1,10 @@
+## 1.0.4(2023-12-06)
+1. 阻止事件冒泡处理
+## 1.0.3(2023-10-23)
+1. 修复报错的BUG
+## 1.0.2(2023-10-23)
+1. 修复设置singleSize、multipleSize、space等值带单位,存在不显示的BUG
+## 1.0.1(2023-09-13)
+1. 添加依赖
+## 1.0.0(2023-08-30)
+1. 新增uv-album相册组件
diff --git a/uni_modules/uv-album/components/uv-album/uv-album.vue b/uni_modules/uv-album/components/uv-album/uv-album.vue
new file mode 100644
index 0000000..835d792
--- /dev/null
+++ b/uni_modules/uv-album/components/uv-album/uv-album.vue
@@ -0,0 +1,312 @@
+<template>
+ <view class="uv-album">
+ <view
+ class="uv-album__row"
+ ref="uv-album__row"
+ v-for="(arr, index) in showUrls"
+ :forComputedUse="albumWidth"
+ :key="index"
+ >
+ <view
+ class="uv-album__row__wrapper"
+ v-for="(item, index1) in arr"
+ :key="index1"
+ :style="[imageStyle(index + 1, index1 + 1)]"
+ @tap.stop="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
+ >
+ <image
+ :src="getSrc(item)"
+ :mode="
+ urls.length === 1
+ ? imageHeight > 0
+ ? singleMode
+ : 'widthFix'
+ : multipleMode
+ "
+ :style="[
+ {
+ width: imageWidth,
+ height: imageHeight
+ }
+ ]"
+ ></image>
+ <view
+ v-if="
+ showMore &&
+ urls.length > rowCount * showUrls.length &&
+ index === showUrls.length - 1 &&
+ index1 === showUrls[showUrls.length - 1].length - 1
+ "
+ class="uv-album__row__wrapper__text"
+ >
+ <uv-text
+ :text="`+${urls.length - maxCount}`"
+ color="#fff"
+ :size="$uv.getPx(multipleSize) * 0.3"
+ align="center"
+ customStyle="justify-content: center"
+ ></uv-text>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+
+ /**
+ * Album 相册
+ * @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
+ * @tutorial https://www.uvui.cn/components/album.html
+ * @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
+ * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
+ * @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180 )
+ * @property {String | Number} multipleSize 多图时,图片边长 (默认 70 )
+ * @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6 )
+ * @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill' )
+ * @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill' )
+ * @property {String | Number} maxCount 取消按钮的提示文字 (默认 9 )
+ * @property {Boolean} previewFullImage 是否可以预览图片 (默认 true )
+ * @property {String | Number} rowCount 每行展示图片数量,如设置,singleSize和multipleSize将会无效 (默认 3 )
+ * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
+ *
+ * @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width )
+ * @example <uv-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></uv-album>
+ */
+ export default {
+ name: 'uv-album',
+ mixins: [mpMixin, mixin],
+ emits: ['albumWidth'],
+ props: {
+ // 图片地址,Array<String>|Array<Object>形式
+ urls: {
+ type: Array,
+ default: () => []
+ },
+ // 指定从数组的对象元素中读取哪个属性作为图片地址
+ keyName: {
+ type: String,
+ default: ''
+ },
+ // 单图时,图片长边的长度
+ singleSize: {
+ type: [String, Number],
+ default: 180
+ },
+ // 多图时,图片边长
+ multipleSize: {
+ type: [String, Number],
+ default: 70
+ },
+ // 多图时,图片水平和垂直之间的间隔
+ space: {
+ type: [String, Number],
+ default: 6
+ },
+ // 单图时,图片缩放裁剪的模式
+ singleMode: {
+ type: String,
+ default: 'scaleToFill'
+ },
+ // 多图时,图片缩放裁剪的模式
+ multipleMode: {
+ type: String,
+ default: 'aspectFill'
+ },
+ // 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
+ maxCount: {
+ type: [String, Number],
+ default: 9
+ },
+ // 是否可以预览图片
+ previewFullImage: {
+ type: Boolean,
+ default: true
+ },
+ // 每行展示图片数量,如设置,singleSize和multipleSize将会无效
+ rowCount: {
+ type: [String, Number],
+ default: 3
+ },
+ // 超出maxCount时是否显示查看更多的提示
+ showMore: {
+ type: Boolean,
+ default: true
+ },
+ ...uni.$uv?.props?.album
+ },
+ data() {
+ return {
+ // 单图的宽度
+ singleWidth: 0,
+ // 单图的高度
+ singleHeight: 0,
+ // 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
+ singlePercent: 0.6
+ }
+ },
+ watch: {
+ urls: {
+ immediate: true,
+ handler(newVal) {
+ if (newVal.length === 1) {
+ this.getImageRect()
+ }
+ }
+ }
+ },
+ computed: {
+ imageStyle() {
+ return (index1, index2) => {
+ const { space, rowCount, multipleSize, urls } = this;
+ const rowLen = this.showUrls.length;
+ const allLen = this.urls.length;
+ const style = {
+ marginRight: this.$uv.addUnit(space),
+ marginBottom: this.$uv.addUnit(space)
+ }
+ // 如果为最后一行,则每个图片都无需下边框
+ if (index1 === rowLen) style.marginBottom = 0
+ // 每行的最右边一张和总长度的最后一张无需右边框
+ if (
+ index2 === rowCount ||
+ (index1 === rowLen &&
+ index2 === this.showUrls[index1 - 1].length)
+ )
+ style.marginRight = 0
+ return style
+ }
+ },
+ // 将数组划分为二维数组
+ showUrls() {
+ const arr = []
+ this.urls.map((item, index) => {
+ // 限制最大展示数量
+ if (index + 1 <= this.maxCount) {
+ // 计算该元素为第几个素组内
+ const itemIndex = Math.floor(index / this.rowCount)
+ // 判断对应的索引是否存在
+ if (!arr[itemIndex]) {
+ arr[itemIndex] = []
+ }
+ arr[itemIndex].push(item)
+ }
+ })
+ return arr
+ },
+ imageWidth() {
+ return this.$uv.addUnit(
+ this.urls.length === 1 ? this.singleWidth : this.multipleSize
+ )
+ },
+ imageHeight() {
+ return this.$uv.addUnit(
+ this.urls.length === 1 ? this.singleHeight : this.multipleSize
+ )
+ },
+ // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
+ // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
+ albumWidth() {
+ let width = 0
+ if (this.urls.length === 1) {
+ width = this.singleWidth
+ } else {
+ width =
+ this.showUrls[0].length * this.$uv.getPx(this.multipleSize) +
+ this.$uv.getPx(this.space) * (this.showUrls[0].length - 1)
+ }
+ this.$emit('albumWidth', width)
+ return width
+ }
+ },
+ methods: {
+ // 预览图片
+ onPreviewTap(url) {
+ const urls = this.urls.map((item) => {
+ return this.getSrc(item)
+ })
+ uni.previewImage({
+ current: url,
+ urls
+ })
+ },
+ // 获取图片的路径
+ getSrc(item) {
+ return this.$uv.test.object(item) ?
+ (this.keyName && item[this.keyName]) || item.src :
+ item
+ },
+ // 单图时,获取图片的尺寸
+ // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
+ // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+ getImageRect() {
+ const src = this.getSrc(this.urls[0])
+ uni.getImageInfo({
+ src,
+ success: (res) => {
+ // 判断图片横向还是竖向展示方式
+ const isHorizotal = res.width >= res.height
+ this.singleWidth = isHorizotal ?
+ this.singleSize :
+ (res.width / res.height) * this.$uv.getPx(this.singleSize)
+ this.singleHeight = !isHorizotal ?
+ this.singleSize :
+ (res.height / res.width) * this.singleWidth
+ },
+ fail: () => {
+ this.getComponentWidth()
+ }
+ })
+ },
+ // 获取组件的宽度
+ async getComponentWidth() {
+ // 延时一定时间,以获取dom尺寸
+ await this.$uv.sleep(30)
+ // #ifndef APP-NVUE
+ this.$uGetRect('.uv-album__row').then((size) => {
+ this.singleWidth = size.width * this.singlePercent
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ // 这里ref="uv-album__row"所在的标签为通过for循环出来,导致this.$refs['uv-album__row']是一个数组
+ const ref = this.$refs['uv-album__row'][0]
+ ref &&
+ dom.getComponentRect(ref, (res) => {
+ this.singleWidth = res.size.width * this.singlePercent
+ })
+ // #endif
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-album {
+ @include flex(column);
+ &__row {
+ @include flex(row);
+ flex-wrap: wrap;
+ &__wrapper {
+ position: relative;
+ &__text {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.3);
+ @include flex(row);
+ justify-content: center;
+ align-items: center;
+ }
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-album/package.json b/uni_modules/uv-album/package.json
new file mode 100644
index 0000000..700602f
--- /dev/null
+++ b/uni_modules/uv-album/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-album",
+ "displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.4",
+ "description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码",
+ "keywords": [
+ "album",
+ "uv-ui",
+ "uvui",
+ "相册",
+ "图片"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-text"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-album/readme.md b/uni_modules/uv-album/readme.md
new file mode 100644
index 0000000..edd2f0b
--- /dev/null
+++ b/uni_modules/uv-album/readme.md
@@ -0,0 +1,21 @@
+# Album 相册
+
+> **组件名:uv-album**
+
+本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。
+
+功能齐全,灵活配置可以,开箱即用。减少重复的模板代码。
+
+# <a href="https://www.uvui.cn/components/album.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-alert/changelog.md b/uni_modules/uv-alert/changelog.md
new file mode 100644
index 0000000..71cca03
--- /dev/null
+++ b/uni_modules/uv-alert/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-06-01)
+1. 修复点击触发两次实践的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-alert 警告提示组件
diff --git a/uni_modules/uv-alert/components/uv-alert/props.js b/uni_modules/uv-alert/components/uv-alert/props.js
new file mode 100644
index 0000000..cc8edc1
--- /dev/null
+++ b/uni_modules/uv-alert/components/uv-alert/props.js
@@ -0,0 +1,45 @@
+export default {
+ props: {
+ // 显示文字
+ title: {
+ type: String,
+ default: ''
+ },
+ // 主题,success/warning/info/error
+ type: {
+ type: String,
+ default: 'warning'
+ },
+ // 辅助性文字
+ description: {
+ type: String,
+ default: ''
+ },
+ // 是否可关闭
+ closable: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示图标
+ showIcon: {
+ type: Boolean,
+ default: false
+ },
+ // 浅或深色调,light-浅色,dark-深色
+ effect: {
+ type: String,
+ default: 'light'
+ },
+ // 文字是否居中
+ center: {
+ type: Boolean,
+ default: false
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ ...uni.$uv?.props?.alert
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-alert/components/uv-alert/uv-alert.vue b/uni_modules/uv-alert/components/uv-alert/uv-alert.vue
new file mode 100644
index 0000000..ba3965e
--- /dev/null
+++ b/uni_modules/uv-alert/components/uv-alert/uv-alert.vue
@@ -0,0 +1,246 @@
+<template>
+ <uv-transition
+ mode="fade"
+ :show="show"
+ >
+ <view
+ class="uv-alert"
+ :class="[`uv-alert--${type}--${effect}`]"
+ @tap.stop="clickHandler"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <view
+ class="uv-alert__icon"
+ v-if="showIcon"
+ >
+ <uv-icon
+ :name="iconName"
+ size="18"
+ :color="iconColor"
+ ></uv-icon>
+ </view>
+ <view
+ class="uv-alert__content"
+ :style="[{
+ paddingRight: closable ? '20px' : 0
+ }]"
+ >
+ <text
+ class="uv-alert__content__title"
+ v-if="title"
+ :style="[{
+ fontSize: $uv.addUnit(fontSize),
+ textAlign: center ? 'center' : 'left'
+ }]"
+ :class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
+ >{{ title }}</text>
+ <text
+ class="uv-alert__content__desc"
+ v-if="description"
+ :style="[{
+ fontSize: $uv.addUnit(fontSize),
+ textAlign: center ? 'center' : 'left'
+ }]"
+ :class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
+ >{{ description }}</text>
+ </view>
+ <view
+ class="uv-alert__close"
+ v-if="closable"
+ @tap.stop="closeHandler"
+ >
+ <uv-icon
+ name="close"
+ :color="iconColor"
+ size="15"
+ ></uv-icon>
+ </view>
+ </view>
+ </uv-transition>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Alert 警告提示
+ * @description 警告提示,展现需要关注的信息。
+ * @tutorial https://www.uvui.cn/components/alertTips.html
+ *
+ * @property {String} title 显示的文字
+ * @property {String} type 使用预设的颜色 (默认 'warning' )
+ * @property {String} description 辅助性文字,颜色比title浅一点,字号也小一点,可选
+ * @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false )
+ * @property {Boolean} showIcon 是否显示左边的辅助图标 ( 默认 false )
+ * @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light' )
+ * @property {Boolean} center 文字是否居中 (默认 false )
+ * @property {String | Number} fontSize 字体大小 (默认 14 )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ * @event {Function} click 点击组件时触发
+ * @example <uv-alert :title="title" type = "warning" :closable="closable" :description = "description"></uv-alert>
+ */
+ export default {
+ name: 'uv-alert',
+ mixins: [mpMixin, mixin, props],
+ emits: ['click'],
+ data() {
+ return {
+ show: true
+ }
+ },
+ computed: {
+ iconColor() {
+ return this.effect === 'light' ? this.type : '#fff'
+ },
+ // 不同主题对应不同的图标
+ iconName() {
+ switch (this.type) {
+ case 'success':
+ return 'checkmark-circle-fill';
+ break;
+ case 'error':
+ return 'close-circle-fill';
+ break;
+ case 'warning':
+ return 'error-circle-fill';
+ break;
+ case 'info':
+ return 'info-circle-fill';
+ break;
+ case 'primary':
+ return 'more-circle-fill';
+ break;
+ default:
+ return 'error-circle-fill';
+ }
+ }
+ },
+ methods: {
+ // 点击内容
+ clickHandler() {
+ this.$emit('click')
+ },
+ // 点击关闭按钮
+ closeHandler() {
+ this.show = false
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-alert {
+ position: relative;
+ background-color: $uv-primary;
+ padding: 8px 10px;
+ @include flex(row);
+ align-items: center;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+
+ &--primary--dark {
+ background-color: $uv-primary;
+ }
+
+ &--primary--light {
+ background-color: #ecf5ff;
+ }
+
+ &--error--dark {
+ background-color: $uv-error;
+ }
+
+ &--error--light {
+ background-color: #FEF0F0;
+ }
+
+ &--success--dark {
+ background-color: $uv-success;
+ }
+
+ &--success--light {
+ background-color: #f5fff0;
+ }
+
+ &--warning--dark {
+ background-color: $uv-warning;
+ }
+
+ &--warning--light {
+ background-color: #FDF6EC;
+ }
+
+ &--info--dark {
+ background-color: $uv-info;
+ }
+
+ &--info--light {
+ background-color: #f4f4f5;
+ }
+
+ &__icon {
+ margin-right: 5px;
+ }
+
+ &__content {
+ @include flex(column);
+ flex: 1;
+
+ &__title {
+ color: $uv-main-color;
+ font-size: 14px;
+ font-weight: bold;
+ color: #fff;
+ margin-bottom: 2px;
+ }
+
+ &__desc {
+ color: $uv-main-color;
+ font-size: 14px;
+ flex-wrap: wrap;
+ color: #fff;
+ }
+ }
+
+ &__title--dark,
+ &__desc--dark {
+ color: #FFFFFF;
+ }
+
+ &__text--primary--light,
+ &__text--primary--light {
+ color: $uv-primary;
+ }
+
+ &__text--success--light,
+ &__text--success--light {
+ color: $uv-success;
+ }
+
+ &__text--warning--light,
+ &__text--warning--light {
+ color: $uv-warning;
+ }
+
+ &__text--error--light,
+ &__text--error--light {
+ color: $uv-error;
+ }
+
+ &__text--info--light,
+ &__text--info--light {
+ color: $uv-info;
+ }
+
+ &__close {
+ position: absolute;
+ top: 11px;
+ right: 10px;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-alert/package.json b/uni_modules/uv-alert/package.json
new file mode 100644
index 0000000..34bec20
--- /dev/null
+++ b/uni_modules/uv-alert/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-alert",
+ "displayName": "uv-alert 警告提示 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "uv-alert 警告提示,展现需要关注的信息。灵活配置,功能齐全,兼容全端",
+ "keywords": [
+ "alert",
+ "uvui",
+ "uv-ui",
+ "警告提示"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-transition",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-alert/readme.md b/uni_modules/uv-alert/readme.md
new file mode 100644
index 0000000..63dda76
--- /dev/null
+++ b/uni_modules/uv-alert/readme.md
@@ -0,0 +1,15 @@
+## Alert 警告提示
+
+> **组件名:uv-alert**
+
+警告提示,展现需要关注的信息。
+
+当某个页面需要向用户显示警告的信息时。
+
+非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。
+
+### <a href="https://www.uvui.cn/components/alert.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-avatar/changelog.md b/uni_modules/uv-avatar/changelog.md
new file mode 100644
index 0000000..8631c86
--- /dev/null
+++ b/uni_modules/uv-avatar/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.5(2023-12-06)
+1. 优化
+## 1.0.4(2023-12-06)
+1. 优化
+## 1.0.3(2023-12-06)
+1. 阻止事件冒泡处理,单个头像模式
+## 1.0.2(2023-12-06)
+1. 阻止事件冒泡处理
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-avatar 头像组件
diff --git a/uni_modules/uv-avatar/components/uv-avatar-group/props.js b/uni_modules/uv-avatar/components/uv-avatar-group/props.js
new file mode 100644
index 0000000..a610ab4
--- /dev/null
+++ b/uni_modules/uv-avatar/components/uv-avatar-group/props.js
@@ -0,0 +1,53 @@
+export default {
+ props: {
+ // 头像图片组
+ urls: {
+ type: Array,
+ default: () => []
+ },
+ // 最多展示的头像数量
+ maxCount: {
+ type: [String, Number],
+ default: 5
+ },
+ // 头像形状
+ shape: {
+ type: String,
+ default: 'circle'
+ },
+ // 图片裁剪模式
+ mode: {
+ type: String,
+ default: 'scaleToFill'
+ },
+ // 超出maxCount时是否显示查看更多的提示
+ showMore: {
+ type: Boolean,
+ default: true
+ },
+ // 头像大小
+ size: {
+ type: [String, Number],
+ default: 40
+ },
+ // 指定从数组的对象元素中读取哪个属性作为图片地址
+ keyName: {
+ type: String,
+ default: ''
+ },
+ // 头像之间的遮挡比例
+ gap: {
+ type: [String, Number],
+ validator(value) {
+ return value >= 0 && value <= 1
+ },
+ default: 0.5
+ },
+ // 需额外显示的值
+ extraValue: {
+ type: [Number, String],
+ default: 0
+ },
+ ...uni.$uv?.props?.avatarGroup
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue b/uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue
new file mode 100644
index 0000000..59481f6
--- /dev/null
+++ b/uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue
@@ -0,0 +1,106 @@
+<template>
+ <view class="uv-avatar-group">
+ <view
+ class="uv-avatar-group__item"
+ v-for="(item, index) in showUrl"
+ :key="index"
+ :style="{
+ marginLeft: index === 0 ? 0 : $uv.addUnit(-size * gap)
+ }"
+ >
+ <uv-avatar
+ :size="size"
+ :shape="shape"
+ :mode="mode"
+ :src="$uv.test.object(item) ? keyName && item[keyName] || item.url : item"
+ ></uv-avatar>
+ <view
+ class="uv-avatar-group__item__show-more"
+ v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
+ @tap="clickHandler"
+ >
+ <uv-text
+ color="#ffffff"
+ :size="size * 0.4"
+ :text="`+${extraValue || urls.length - showUrl.length}`"
+ align="center"
+ customStyle="justify-content: center"
+ ></uv-text>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * AvatarGroup 头像组
+ * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+ * @tutorial https://www.uvui.cn/components/avatar.html
+ *
+ * @property {Array} urls 头像图片组 (默认 [] )
+ * @property {String | Number} maxCount 最多展示的头像数量 ( 默认 5 )
+ * @property {String} shape 头像形状( 'circle' (默认) | 'square' )
+ * @property {String} mode 图片裁剪模式(默认 'scaleToFill' )
+ * @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true )
+ * @property {String | Number} size 头像大小 (默认 40 )
+ * @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
+ * @property {String | Number} gap 头像之间的遮挡比例(0.4代表遮挡40%) (默认 0.5 )
+ * @property {String | Number} extraValue 需额外显示的值
+ * @event {Function} showMore 头像组更多点击
+ * @example <uv-avatar-group:urls="urls" size="35" gap="0.4" ></uv-avatar-group:urls=>
+ */
+ export default {
+ name: 'uv-avatar-group',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+
+ }
+ },
+ computed: {
+ showUrl() {
+ return this.urls.slice(0, this.maxCount)
+ }
+ },
+ methods: {
+ clickHandler() {
+ this.$emit('showMore')
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+
+ .uv-avatar-group {
+ @include flex;
+
+ &__item {
+ margin-left: -10px;
+ position: relative;
+
+ &--no-indent {
+ // 如果你想质疑作者不会使用:first-child,说明你太年轻,因为nvue不支持
+ margin-left: 0;
+ }
+
+ &__show-more {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: rgba(0, 0, 0, 0.3);
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 100px;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-avatar/components/uv-avatar/props.js b/uni_modules/uv-avatar/components/uv-avatar/props.js
new file mode 100644
index 0000000..6c8d725
--- /dev/null
+++ b/uni_modules/uv-avatar/components/uv-avatar/props.js
@@ -0,0 +1,80 @@
+import { range } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
+export default {
+ props: {
+ // 头像图片路径(不能为相对路径)
+ src: {
+ type: String,
+ default: ''
+ },
+ // 头像形状,circle-圆形,square-方形
+ shape: {
+ type: String,
+ default: 'circle'
+ },
+ // 头像尺寸
+ size: {
+ type: [String, Number],
+ default: 40
+ },
+ // 裁剪模式
+ mode: {
+ type: String,
+ default: 'scaleToFill'
+ },
+ // 显示的文字
+ text: {
+ type: String,
+ default: ''
+ },
+ // 背景色
+ bgColor: {
+ type: String,
+ default: '#c0c4cc'
+ },
+ // 文字颜色
+ color: {
+ type: String,
+ default: '#fff'
+ },
+ // 文字大小
+ fontSize: {
+ type: [String, Number],
+ default: 18
+ },
+ // 显示的图标
+ icon: {
+ type: String,
+ default: ''
+ },
+ // 显示小程序头像,只对百度,微信,QQ小程序有效
+ mpAvatar: {
+ type: Boolean,
+ default: false
+ },
+ // 是否使用随机背景色
+ randomBgColor: {
+ type: Boolean,
+ default: false
+ },
+ // 加载失败的默认头像(组件有内置默认图片)
+ defaultUrl: {
+ type: String,
+ default: ''
+ },
+ // 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+ colorIndex: {
+ type: [String, Number],
+ // 校验参数规则,索引在0-19之间
+ validator(n) {
+ return range(n, [0, 19]) || n === ''
+ },
+ default: ''
+ },
+ // 组件标识符
+ name: {
+ type: String,
+ default: ''
+ },
+ ...uni.$uv?.props?.avatar
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue b/uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue
new file mode 100644
index 0000000..0959d6a
--- /dev/null
+++ b/uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue
@@ -0,0 +1,175 @@
+<template>
+ <view
+ class="uv-avatar"
+ :class="[`uv-avatar--${shape}`]"
+ :style="[{
+ backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $uv.random(0, 19)] : bgColor) : 'transparent',
+ width: $uv.addUnit(size),
+ height: $uv.addUnit(size),
+ }, $uv.addStyle(customStyle)]"
+ @tap="clickHandler"
+ >
+ <slot>
+ <!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
+ <open-data
+ v-if="mpAvatar && allowMp"
+ type="userAvatarUrl"
+ :style="[{
+ width: $uv.addUnit(size),
+ height: $uv.addUnit(size)
+ }]"
+ />
+ <!-- #endif -->
+ <!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
+ <template v-if="mpAvatar && allowMp"></template>
+ <!-- #endif -->
+ <uv-icon
+ v-else-if="icon"
+ :name="icon"
+ :size="fontSize"
+ :color="color"
+ ></uv-icon>
+ <uv-text
+ v-else-if="text"
+ :text="text"
+ :size="fontSize"
+ :color="color"
+ align="center"
+ customStyle="justify-content: center"
+ ></uv-text>
+ <image
+ class="uv-avatar__image"
+ v-else
+ :class="[`uv-avatar__image--${shape}`]"
+ :src="avatarUrl || defaultUrl"
+ :mode="mode"
+ @error="errorHandler"
+ :style="[{
+ width: $uv.addUnit(size),
+ height: $uv.addUnit(size)
+ }]"
+ ></image>
+ </slot>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ const base64Avatar =
+ "data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
+ /**
+ * Avatar 头像
+ * @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+ * @tutorial https://www.uvui.cn/components/avatar.html
+ *
+ * @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
+ * @property {String} shape 头像形状 ( circle (默认) | square)
+ * @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40 )
+ * @property {String} mode 头像图片的裁剪类型,与uni的image组件的mode参数一致,如效果达不到需求,可尝试传widthFix值 (默认 'scaleToFill' )
+ * @property {String} text 用文字替代图片,级别优先于src
+ * @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc' )
+ * @property {String} color 文字颜色 (默认 '#ffffff' )
+ * @property {String | Number} fontSize 文字大小 (默认 18 )
+ * @property {String} icon 显示的图标
+ * @property {Boolean} mpAvatar 显示小程序头像,只对百度,微信,QQ小程序有效 (默认 false )
+ * @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false )
+ * @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
+ * @property {String | Number} colorIndex 如果配置了randomBgColor为true,且配置了此值,则从默认的背景色数组中取出对应索引的颜色值,取值0-19之间
+ * @property {String} name 组件标识符 (默认 'level' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} click 点击组件时触发 index: 用户传递的标识符
+ * @example <uv-avatar :src="src" mode="square"></uv-avatar>
+ */
+ export default {
+ name: 'uv-avatar',
+ emits: ['click'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 如果配置randomBgColor参数为true,在图标或者文字的模式下,会随机从中取出一个颜色值当做背景色
+ colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
+ '#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
+ '#73d1f1',
+ '#80a7dc'
+ ],
+ avatarUrl: this.src,
+ allowMp: false
+ }
+ },
+ watch: {
+ // 监听头像src的变化,赋值给内部的avatarUrl变量,因为图片加载失败时,需要修改图片的src为默认值
+ // 而组件内部不能直接修改props的值,所以需要一个中间变量
+ src: {
+ immediate: true,
+ handler(newVal) {
+ this.avatarUrl = newVal
+ // 如果没有传src,则主动触发error事件,用于显示默认的头像,否则src为''空字符等的时候,会无内容展示
+ if(!newVal) {
+ this.errorHandler()
+ }
+ }
+ }
+ },
+ computed: {
+ imageStyle() {
+ const style = {}
+ return style
+ }
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 目前只有这几个小程序平台具有open-data标签
+ // 其他平台可以通过uni.getUserInfo类似接口获取信息,但是需要弹窗授权(首次),不合符组件逻辑
+ // 故目前自动获取小程序头像只支持这几个平台
+ // #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
+ this.allowMp = true
+ // #endif
+ },
+ // 判断传入的name属性,是否图片路径,只要带有"/"均认为是图片形式
+ isImg() {
+ return this.src.indexOf('/') !== -1
+ },
+ // 图片加载时失败时触发
+ errorHandler() {
+ this.avatarUrl = this.defaultUrl || base64Avatar
+ },
+ clickHandler() {
+ this.$emit('click', this.name)
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-avatar {
+ @include flex;
+ align-items: center;
+ justify-content: center;
+
+ &--circle {
+ border-radius: 100px;
+ }
+
+ &--square {
+ border-radius: 4px;
+ }
+
+ &__image {
+ &--circle {
+ border-radius: 100px;
+ }
+
+ &--square {
+ border-radius: 4px;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-avatar/package.json b/uni_modules/uv-avatar/package.json
new file mode 100644
index 0000000..e77ab68
--- /dev/null
+++ b/uni_modules/uv-avatar/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-avatar",
+ "displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.5",
+ "description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。",
+ "keywords": [
+ "uv-avatar",
+ "uvui",
+ "uv-ui",
+ "avatar",
+ "头像"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-text"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-avatar/readme.md b/uni_modules/uv-avatar/readme.md
new file mode 100644
index 0000000..1f068eb
--- /dev/null
+++ b/uni_modules/uv-avatar/readme.md
@@ -0,0 +1,11 @@
+## Avatar 头像
+
+> **组件名:uv-avatar**
+
+本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
+
+### <a href="https://www.uvui.cn/components/avatar.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-back-top/changelog.md b/uni_modules/uv-back-top/changelog.md
new file mode 100644
index 0000000..9772262
--- /dev/null
+++ b/uni_modules/uv-back-top/changelog.md
@@ -0,0 +1,8 @@
+## 1.0.2(2023-07-03)
+1. 优化插槽自定义内容部分
+2. 增加backToTop方法说明
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-back-top 返回顶部
diff --git a/uni_modules/uv-back-top/components/uv-back-top/props.js b/uni_modules/uv-back-top/components/uv-back-top/props.js
new file mode 100644
index 0000000..74daff4
--- /dev/null
+++ b/uni_modules/uv-back-top/components/uv-back-top/props.js
@@ -0,0 +1,58 @@
+export default {
+ props: {
+ // 返回顶部的形状,circle-圆形,square-方形
+ mode: {
+ type: String,
+ default: 'circle'
+ },
+ // 自定义图标
+ icon: {
+ type: String,
+ default: 'arrow-upward'
+ },
+ // 提示文字
+ text: {
+ type: String,
+ default: ''
+ },
+ // 返回顶部滚动时间
+ duration: {
+ type: [String, Number],
+ default: 100
+ },
+ // 滚动距离
+ scrollTop: {
+ type: [String, Number],
+ default: 0
+ },
+ // 距离顶部多少距离显示,单位px
+ top: {
+ type: [String, Number],
+ default: 400
+ },
+ // 返回顶部按钮到底部的距离,单位px
+ bottom: {
+ type: [String, Number],
+ default: 100
+ },
+ // 返回顶部按钮到右边的距离,单位px
+ right: {
+ type: [String, Number],
+ default: 20
+ },
+ // 层级
+ zIndex: {
+ type: [String, Number],
+ default: 9
+ },
+ // 图标的样式,对象形式
+ iconStyle: {
+ type: Object,
+ default: () => ({
+ color: '#909399',
+ fontSize: '19px'
+ })
+ },
+ ...uni.$uv?.props?.backtop
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-back-top/components/uv-back-top/uv-back-top.vue b/uni_modules/uv-back-top/components/uv-back-top/uv-back-top.vue
new file mode 100644
index 0000000..fad73d4
--- /dev/null
+++ b/uni_modules/uv-back-top/components/uv-back-top/uv-back-top.vue
@@ -0,0 +1,116 @@
+<template>
+ <uv-transition mode="fade" :customStyle="backTopStyle" :show="show">
+ <slot>
+ <view class="uv-back-top" :style="[contentStyle]" @click="backToTop">
+ <uv-icon :name="icon" :custom-style="iconStyle"></uv-icon>
+ <text v-if="text" class="uv-back-top__text">{{text}}</text>
+ </view>
+ </slot>
+ </uv-transition>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = weex.requireModule('dom')
+ // #endif
+ /**
+ * backTop 返回顶部
+ * @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+ * @tutorial https://www.uvui.cn/components/backTop.html
+ * @property {String} mode 返回顶部的形状,circle-圆形,square-方形 (默认 'circle' )
+ * @property {String} icon 自定义图标 (默认 'arrow-upward' ) 见官方文档示例
+ * @property {String} text 提示文字
+ * @property {String | Number} duration 返回顶部滚动时间 (默认 100)
+ * @property {String | Number} scrollTop 滚动距离 (默认 0 )
+ * @property {String | Number} top 距离顶部多少距离显示,单位px (默认 400 )
+ * @property {String | Number} bottom 返回顶部按钮到底部的距离,单位px (默认 100 )
+ * @property {String | Number} right 返回顶部按钮到右边的距离,单位px (默认 20 )
+ * @property {String | Number} zIndex 层级 (默认 9 )
+ * @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'})
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @example <uv-back-top :scrollTop="scrollTop"></uv-back-top>
+ */
+ export default {
+ name: 'uv-back-top',
+ emits: ['click'],
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ backTopStyle() {
+ // 动画组件样式
+ const style = {
+ bottom: this.$uv.addUnit(this.bottom),
+ right: this.$uv.addUnit(this.right),
+ width: '40px',
+ height: '40px',
+ position: 'fixed',
+ zIndex: 10,
+ }
+ return style
+ },
+ show() {
+ return this.$uv.getPx(this.scrollTop) > this.$uv.getPx(this.top)
+ },
+ contentStyle() {
+ const style = {}
+ let radius = 0
+ // 是否圆形
+ if (this.mode === 'circle') {
+ radius = '100px'
+ } else {
+ radius = '4px'
+ }
+ // 为了兼容安卓nvue,只能这么分开写
+ style.borderTopLeftRadius = radius
+ style.borderTopRightRadius = radius
+ style.borderBottomLeftRadius = radius
+ style.borderBottomRightRadius = radius
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ }
+ },
+ methods: {
+ backToTop() {
+ // #ifdef APP-NVUE
+ if (!this.$parent.$refs['uv-back-top']) {
+ this.$uv.error(`nvue页面需要给页面最外层元素设置"ref='uv-back-top'`)
+ }
+ dom.scrollToElement(this.$parent.$refs['uv-back-top'], {
+ offset: 0
+ })
+ // #endif
+
+ // #ifndef APP-NVUE
+ uni.pageScrollTo({
+ scrollTop: 0,
+ duration: this.duration
+ });
+ // #endif
+ this.$emit('click')
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ $uv-back-top-flex: 1 !default;
+ $uv-back-top-height: 100% !default;
+ $uv-back-top-background-color: #E1E1E1 !default;
+ $uv-back-top-tips-font-size: 12px !default;
+ .uv-back-top {
+ @include flex;
+ flex-direction: column;
+ align-items: center;
+ flex: $uv-back-top-flex;
+ height: $uv-back-top-height;
+ justify-content: center;
+ background-color: $uv-back-top-background-color;
+ &__tips {
+ font-size: $uv-back-top-tips-font-size;
+ transform: scale(0.8);
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-back-top/package.json b/uni_modules/uv-back-top/package.json
new file mode 100644
index 0000000..a2035a5
--- /dev/null
+++ b/uni_modules/uv-back-top/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-back-top",
+ "displayName": "uv-back-top 返回顶部 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "返回顶部 组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。",
+ "keywords": [
+ "uv-back-top",
+ "uvui",
+ "uv-ui",
+ "avatar",
+ "返回顶部"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-transition"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-back-top/readme.md b/uni_modules/uv-back-top/readme.md
new file mode 100644
index 0000000..d6a2aec
--- /dev/null
+++ b/uni_modules/uv-back-top/readme.md
@@ -0,0 +1,11 @@
+## BackTop 返回顶部
+
+> **组件名:uv-back-top**
+
+该组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
+
+### <a href="https://www.uvui.cn/components/backTop.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-badge/changelog.md b/uni_modules/uv-badge/changelog.md
new file mode 100644
index 0000000..b0ba104
--- /dev/null
+++ b/uni_modules/uv-badge/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-06-04)
+1. 修复type等属性为null的时候不显示徽标的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-badge 徽标数,数字角标
diff --git a/uni_modules/uv-badge/components/uv-badge/props.js b/uni_modules/uv-badge/components/uv-badge/props.js
new file mode 100644
index 0000000..c6369c3
--- /dev/null
+++ b/uni_modules/uv-badge/components/uv-badge/props.js
@@ -0,0 +1,73 @@
+export default {
+ props: {
+ // 是否显示圆点
+ isDot: {
+ type: Boolean,
+ default: false
+ },
+ // 显示的内容
+ value: {
+ type: [Number, String],
+ default: ''
+ },
+ // 是否显示
+ show: {
+ type: Boolean,
+ default: true
+ },
+ // 最大值,超过最大值会显示 '{max}+'
+ max: {
+ type: [Number, String],
+ default: 999
+ },
+ // 主题类型,error|warning|success|primary
+ type: {
+ type: [String,undefined,null],
+ default: 'error'
+ },
+ // 当数值为 0 时,是否展示 Badge
+ showZero: {
+ type: Boolean,
+ default: false
+ },
+ // 背景颜色,优先级比type高,如设置,type参数会失效
+ bgColor: {
+ type: [String, null],
+ default: null
+ },
+ // 字体颜色
+ color: {
+ type: [String, null],
+ default: null
+ },
+ // 徽标形状,circle-四角均为圆角,horn-左下角为直角
+ shape: {
+ type: [String,undefined,null],
+ default: 'circle'
+ },
+ // 设置数字的显示方式,overflow|ellipsis|limit
+ // overflow会根据max字段判断,超出显示`${max}+`
+ // ellipsis会根据max判断,超出显示`${max}...`
+ // limit会依据1000作为判断条件,超出1000,显示`${value/1000}K`,比如2.2k、3.34w,最多保留2位小数
+ numberType: {
+ type: [String,undefined,null],
+ default: 'overflow'
+ },
+ // 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+ offset: {
+ type: Array,
+ default: () => []
+ },
+ // 是否反转背景和字体颜色
+ inverted: {
+ type: Boolean,
+ default: false
+ },
+ // 是否绝对定位
+ absolute: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.badge
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-badge/components/uv-badge/uv-badge.vue b/uni_modules/uv-badge/components/uv-badge/uv-badge.vue
new file mode 100644
index 0000000..f7d3911
--- /dev/null
+++ b/uni_modules/uv-badge/components/uv-badge/uv-badge.vue
@@ -0,0 +1,176 @@
+<template>
+ <text
+ v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
+ :class="[isDot ? 'uv-badge--dot' : 'uv-badge--not-dot', inverted && 'uv-badge--inverted', shape === 'horn' && 'uv-badge--horn', `uv-badge--${propsType}${inverted ? '--inverted' : ''}`]"
+ :style="[$uv.addStyle(customStyle), badgeStyle]"
+ class="uv-badge"
+ >{{ isDot ? '' :showValue }}</text>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * badge 徽标数
+ * @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+ * @tutorial https://www.uvui.cn/components/badge.html
+ *
+ * @property {Boolean} isDot 是否显示圆点 (默认 false )
+ * @property {String | Number} value 显示的内容
+ * @property {Boolean} show 是否显示 (默认 true )
+ * @property {String | Number} max 最大值,超过最大值会显示 '{max}+' (默认999)
+ * @property {String} type 主题类型,error|warning|success|primary (默认 'error' )
+ * @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false )
+ * @property {String} bgColor 背景颜色,优先级比type高,如设置,type参数会失效
+ * @property {String} color 字体颜色 (默认 '#ffffff' )
+ * @property {String} shape 徽标形状,circle-四角均为圆角,horn-左下角为直角 (默认 'circle' )
+ * @property {String} numberType 设置数字的显示方式,overflow|ellipsis|limit (默认 'overflow' )
+ * @property {Array}} offset 设置badge的位置偏移,格式为 [x, y],也即设置的为top和right的值,absolute为true时有效
+ * @property {Boolean} inverted 是否反转背景和字体颜色(默认 false )
+ * @property {Boolean} absolute 是否绝对定位(默认 false )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ * @example <uv-badge :type="type" :count="count"></uv-badge>
+ */
+ export default {
+ name: 'uv-badge',
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ // 是否将badge中心与父组件右上角重合
+ boxStyle() {
+ let style = {};
+ return style;
+ },
+ // 整个组件的样式
+ badgeStyle() {
+ const style = {}
+ if(this.color) {
+ style.color = this.color
+ }
+ if (this.bgColor && !this.inverted) {
+ style.backgroundColor = this.bgColor
+ }
+ if (this.absolute) {
+ style.position = 'absolute'
+ // 如果有设置offset参数
+ if(this.offset.length) {
+ // top和right分为为offset的第一个和第二个值,如果没有第二个值,则right等于top
+ const top = this.offset[0]
+ const right = this.offset[1] || top
+ style.top = this.$uv.addUnit(top)
+ style.right = this.$uv.addUnit(right)
+ }
+ }
+ return style
+ },
+ showValue() {
+ switch (this.numberType) {
+ case "overflow":
+ return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
+ break;
+ case "ellipsis":
+ return Number(this.value) > Number(this.max) ? "..." : this.value
+ break;
+ case "limit":
+ return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
+ Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
+ 1e3 * 100) / 100 + "k" : this.value
+ break;
+ default:
+ return Number(this.value)
+ }
+ },
+ propsType(){
+ return this.type || 'error'
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-badge-primary: $uv-primary !default;
+ $uv-badge-error: $uv-error !default;
+ $uv-badge-success: $uv-success !default;
+ $uv-badge-info: $uv-info !default;
+ $uv-badge-warning: $uv-warning !default;
+ $uv-badge-dot-radius: 100px !default;
+ $uv-badge-dot-size: 8px !default;
+ $uv-badge-dot-right: 4px !default;
+ $uv-badge-dot-top: 0 !default;
+ $uv-badge-text-font-size: 11px !default;
+ $uv-badge-text-right: 10px !default;
+ $uv-badge-text-padding: 2px 5px !default;
+ $uv-badge-text-align: center !default;
+ $uv-badge-text-color: #FFFFFF !default;
+
+ .uv-badge {
+ border-top-right-radius: $uv-badge-dot-radius;
+ border-top-left-radius: $uv-badge-dot-radius;
+ border-bottom-left-radius: $uv-badge-dot-radius;
+ border-bottom-right-radius: $uv-badge-dot-radius;
+ @include flex;
+ line-height: $uv-badge-text-font-size;
+ text-align: $uv-badge-text-align;
+ font-size: $uv-badge-text-font-size;
+ color: $uv-badge-text-color;
+
+ &--dot {
+ height: $uv-badge-dot-size;
+ width: $uv-badge-dot-size;
+ }
+
+ &--inverted {
+ font-size: 13px;
+ }
+
+ &--not-dot {
+ padding: $uv-badge-text-padding;
+ }
+
+ &--horn {
+ border-bottom-left-radius: 0;
+ }
+
+ &--primary {
+ background-color: $uv-badge-primary;
+ }
+
+ &--primary--inverted {
+ color: $uv-badge-primary;
+ }
+
+ &--error {
+ background-color: $uv-badge-error;
+ }
+
+ &--error--inverted {
+ color: $uv-badge-error;
+ }
+
+ &--success {
+ background-color: $uv-badge-success;
+ }
+
+ &--success--inverted {
+ color: $uv-badge-success;
+ }
+
+ &--info {
+ background-color: $uv-badge-info;
+ }
+
+ &--info--inverted {
+ color: $uv-badge-info;
+ }
+
+ &--warning {
+ background-color: $uv-badge-warning;
+ }
+
+ &--warning--inverted {
+ color: $uv-badge-warning;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-badge/package.json b/uni_modules/uv-badge/package.json
new file mode 100644
index 0000000..5a81386
--- /dev/null
+++ b/uni_modules/uv-badge/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-badge",
+ "displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。",
+ "keywords": [
+ "uv-badge",
+ "uvui",
+ "uv-ui",
+ "徽标数",
+ "数字角标"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-badge/readme.md b/uni_modules/uv-badge/readme.md
new file mode 100644
index 0000000..cf597eb
--- /dev/null
+++ b/uni_modules/uv-badge/readme.md
@@ -0,0 +1,11 @@
+## Badge 徽标数
+
+> **组件名:uv-badge**
+
+该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
+
+### <a href="https://www.uvui.cn/components/badge.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-button/components/uv-button/uv-button.vue b/uni_modules/uv-button/components/uv-button/uv-button.vue
index e5a2c11..3a2470a 100644
--- a/uni_modules/uv-button/components/uv-button/uv-button.vue
+++ b/uni_modules/uv-button/components/uv-button/uv-button.vue
@@ -229,7 +229,7 @@
loadingColor() {
if (this.plain) {
// 如果有设置color值,则用color值,否则使用type主题颜色
- return this.color ? this.color : '#EE2626';
+ return this.color ? this.color : '#3c9cff';
}
if (this.type === "info") {
return "#c9c9c9";
diff --git a/uni_modules/uv-calendar/changelog.md b/uni_modules/uv-calendar/changelog.md
new file mode 100644
index 0000000..581562a
--- /dev/null
+++ b/uni_modules/uv-calendar/changelog.md
@@ -0,0 +1,16 @@
+## 1.0.6(2023-11-03)
+1. **该版本不再维护**,推荐使用新版本[https://ext.dcloud.net.cn/plugin?name=uv-calendars](https://ext.dcloud.net.cn/plugin?name=uv-calendars)
+## 1.0.5(2023-07-02)
+uv-calendar 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/calendar.html
+## 1.0.4(2023-06-15)
+1. formatter格式化中增加topInfo参数
+## 1.0.3(2023-06-08)
+1. 增加点击日期change回调
+2. 优化
+## 1.0.2(2023-06-05)
+1. 修改多个时间选择的时候存在反选的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-calendar 日历
diff --git a/uni_modules/uv-calendar/components/uv-calendar/calendar.js b/uni_modules/uv-calendar/components/uv-calendar/calendar.js
new file mode 100644
index 0000000..e006dea
--- /dev/null
+++ b/uni_modules/uv-calendar/components/uv-calendar/calendar.js
@@ -0,0 +1,546 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github https://github.com/jjonline/calendar.js
+* @Author Jea杨(JJonline@JJonline.Cn)
+* @Time 2014-7-21
+* @Time 2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+ /**
+ * 农历1900-2100的润大小信息表
+ * @Array Of Property
+ * @return Hex
+ */
+ lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+ 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+ 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+ 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+ 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+ 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+ 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+ 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+ 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+ 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+ 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+ 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+ 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+ 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+ 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+ /** Add By JJonline@JJonline.Cn**/
+ 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+ 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+ 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+ 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+ 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+ 0x0d520], // 2100
+
+ /**
+ * 公历每个月份的天数普通表
+ * @Array Of Property
+ * @return Number
+ */
+ solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+ /**
+ * 天干地支之天干速查表
+ * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+ * @return Cn string
+ */
+ Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+ /**
+ * 天干地支之地支速查表
+ * @Array Of Property
+ * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+ * @return Cn string
+ */
+ Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+ /**
+ * 天干地支之地支速查表<=>生肖
+ * @Array Of Property
+ * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+ * @return Cn string
+ */
+ Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+ /**
+ * 24节气速查表
+ * @Array Of Property
+ * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+ * @return Cn string
+ */
+ solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+ /**
+ * 1900-2100各年的24节气日期速查表
+ * @Array Of Property
+ * @return 0x string For splice
+ */
+ sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+ '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+ 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+ '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+ '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+ '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+ '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+ '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+ '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+ '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+ '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+ '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+ '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+ '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+ '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+ '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+ '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+ '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+ '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+ '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+ /**
+ * 数字转中文速查表
+ * @Array Of Property
+ * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+ * @return Cn string
+ */
+ nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+ /**
+ * 日期转农历称呼速查表
+ * @Array Of Property
+ * @trans ['初','十','廿','卅']
+ * @return Cn string
+ */
+ nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+ /**
+ * 月份转农历称呼速查表
+ * @Array Of Property
+ * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+ * @return Cn string
+ */
+ nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+ /**
+ * 返回农历y年一整年的总天数
+ * @param lunar Year
+ * @return Number
+ * @eg:var count = calendar.lYearDays(1987) ;//count=387
+ */
+ lYearDays: function (y) {
+ var i; var sum = 348
+ for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+ return (sum + this.leapDays(y))
+ },
+
+ /**
+ * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+ * @param lunar Year
+ * @return Number (0-12)
+ * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+ */
+ leapMonth: function (y) { // 闰字编码 \u95f0
+ return (this.lunarInfo[y - 1900] & 0xf)
+ },
+
+ /**
+ * 返回农历y年闰月的天数 若该年没有闰月则返回0
+ * @param lunar Year
+ * @return Number (0、29、30)
+ * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+ */
+ leapDays: function (y) {
+ if (this.leapMonth(y)) {
+ return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+ }
+ return (0)
+ },
+
+ /**
+ * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+ * @param lunar Year
+ * @return Number (-1、29、30)
+ * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+ */
+ monthDays: function (y, m) {
+ if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+ return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+ },
+
+ /**
+ * 返回公历(!)y年m月的天数
+ * @param solar Year
+ * @return Number (-1、28、29、30、31)
+ * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+ */
+ solarDays: function (y, m) {
+ if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+ var ms = m - 1
+ if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+ return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+ } else {
+ return (this.solarMonth[ms])
+ }
+ },
+
+ /**
+ * 农历年份转换为干支纪年
+ * @param lYear 农历年的年份数
+ * @return Cn string
+ */
+ toGanZhiYear: function (lYear) {
+ var ganKey = (lYear - 3) % 10
+ var zhiKey = (lYear - 3) % 12
+ if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+ if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+ return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+ },
+
+ /**
+ * 公历月、日判断所属星座
+ * @param cMonth [description]
+ * @param cDay [description]
+ * @return Cn string
+ */
+ toAstro: function (cMonth, cDay) {
+ var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+ var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+ return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+ },
+
+ /**
+ * 传入offset偏移量返回干支
+ * @param offset 相对甲子的偏移量
+ * @return Cn string
+ */
+ toGanZhi: function (offset) {
+ return this.Gan[offset % 10] + this.Zhi[offset % 12]
+ },
+
+ /**
+ * 传入公历(!)y年获得该年第n个节气的公历日期
+ * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+ * @return day Number
+ * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+ */
+ getTerm: function (y, n) {
+ if (y < 1900 || y > 2100) { return -1 }
+ if (n < 1 || n > 24) { return -1 }
+ var _table = this.sTermInfo[y - 1900]
+ var _info = [
+ parseInt('0x' + _table.substr(0, 5)).toString(),
+ parseInt('0x' + _table.substr(5, 5)).toString(),
+ parseInt('0x' + _table.substr(10, 5)).toString(),
+ parseInt('0x' + _table.substr(15, 5)).toString(),
+ parseInt('0x' + _table.substr(20, 5)).toString(),
+ parseInt('0x' + _table.substr(25, 5)).toString()
+ ]
+ var _calday = [
+ _info[0].substr(0, 1),
+ _info[0].substr(1, 2),
+ _info[0].substr(3, 1),
+ _info[0].substr(4, 2),
+
+ _info[1].substr(0, 1),
+ _info[1].substr(1, 2),
+ _info[1].substr(3, 1),
+ _info[1].substr(4, 2),
+
+ _info[2].substr(0, 1),
+ _info[2].substr(1, 2),
+ _info[2].substr(3, 1),
+ _info[2].substr(4, 2),
+
+ _info[3].substr(0, 1),
+ _info[3].substr(1, 2),
+ _info[3].substr(3, 1),
+ _info[3].substr(4, 2),
+
+ _info[4].substr(0, 1),
+ _info[4].substr(1, 2),
+ _info[4].substr(3, 1),
+ _info[4].substr(4, 2),
+
+ _info[5].substr(0, 1),
+ _info[5].substr(1, 2),
+ _info[5].substr(3, 1),
+ _info[5].substr(4, 2)
+ ]
+ return parseInt(_calday[n - 1])
+ },
+
+ /**
+ * 传入农历数字月份返回汉语通俗表示法
+ * @param lunar month
+ * @return Cn string
+ * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+ */
+ toChinaMonth: function (m) { // 月 => \u6708
+ if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+ var s = this.nStr3[m - 1]
+ s += '\u6708'// 加上月字
+ return s
+ },
+
+ /**
+ * 传入农历日期数字返回汉字表示法
+ * @param lunar day
+ * @return Cn string
+ * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+ */
+ toChinaDay: function (d) { // 日 => \u65e5
+ var s
+ switch (d) {
+ case 10:
+ s = '\u521d\u5341'; break
+ case 20:
+ s = '\u4e8c\u5341'; break
+ break
+ case 30:
+ s = '\u4e09\u5341'; break
+ break
+ default:
+ s = this.nStr2[Math.floor(d / 10)]
+ s += this.nStr1[d % 10]
+ }
+ return (s)
+ },
+
+ /**
+ * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+ * @param y year
+ * @return Cn string
+ * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+ */
+ getAnimal: function (y) {
+ return this.Animals[(y - 4) % 12]
+ },
+
+ /**
+ * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+ * @param y solar year
+ * @param m solar month
+ * @param d solar day
+ * @return JSON object
+ * @eg:console.log(calendar.solar2lunar(1987,11,01));
+ */
+ solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+ // 年份限定、上限
+ if (y < 1900 || y > 2100) {
+ return -1// undefined转换为数字变为NaN
+ }
+ // 公历传参最下限
+ if (y == 1900 && m == 1 && d < 31) {
+ return -1
+ }
+ // 未传参 获得当天
+ if (!y) {
+ var objDate = new Date()
+ } else {
+ var objDate = new Date(y, parseInt(m) - 1, d)
+ }
+ var i; var leap = 0; var temp = 0
+ // 修正ymd参数
+ var y = objDate.getFullYear()
+ var m = objDate.getMonth() + 1
+ var d = objDate.getDate()
+ var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+ for (i = 1900; i < 2101 && offset > 0; i++) {
+ temp = this.lYearDays(i)
+ offset -= temp
+ }
+ if (offset < 0) {
+ offset += temp; i--
+ }
+
+ // 是否今天
+ var isTodayObj = new Date()
+ var isToday = false
+ if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+ isToday = true
+ }
+ // 星期几
+ var nWeek = objDate.getDay()
+ var cWeek = this.nStr1[nWeek]
+ // 数字表示周几顺应天朝周一开始的惯例
+ if (nWeek == 0) {
+ nWeek = 7
+ }
+ // 农历年
+ var year = i
+ var leap = this.leapMonth(i) // 闰哪个月
+ var isLeap = false
+
+ // 效验闰月
+ for (i = 1; i < 13 && offset > 0; i++) {
+ // 闰月
+ if (leap > 0 && i == (leap + 1) && isLeap == false) {
+ --i
+ isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+ } else {
+ temp = this.monthDays(year, i)// 计算农历普通月天数
+ }
+ // 解除闰月
+ if (isLeap == true && i == (leap + 1)) { isLeap = false }
+ offset -= temp
+ }
+ // 闰月导致数组下标重叠取反
+ if (offset == 0 && leap > 0 && i == leap + 1) {
+ if (isLeap) {
+ isLeap = false
+ } else {
+ isLeap = true; --i
+ }
+ }
+ if (offset < 0) {
+ offset += temp; --i
+ }
+ // 农历月
+ var month = i
+ // 农历日
+ var day = offset + 1
+ // 天干地支处理
+ var sm = m - 1
+ var gzY = this.toGanZhiYear(year)
+
+ // 当月的两个节气
+ // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+ var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+ var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+ // 依据12节气修正干支月
+ var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+ if (d >= firstNode) {
+ gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+ }
+
+ // 传入的日期的节气与否
+ var isTerm = false
+ var Term = null
+ if (firstNode == d) {
+ isTerm = true
+ Term = this.solarTerm[m * 2 - 2]
+ }
+ if (secondNode == d) {
+ isTerm = true
+ Term = this.solarTerm[m * 2 - 1]
+ }
+ // 日柱 当月一日与 1900/1/1 相差天数
+ var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+ var gzD = this.toGanZhi(dayCyclical + d - 1)
+ // 该日期所属的星座
+ var astro = this.toAstro(m, d)
+
+ return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+ },
+
+ /**
+ * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+ * @param y lunar year
+ * @param m lunar month
+ * @param d lunar day
+ * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+ * @return JSON object
+ * @eg:console.log(calendar.lunar2solar(1987,9,10));
+ */
+ lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+ var isLeapMonth = !!isLeapMonth
+ var leapOffset = 0
+ var leapMonth = this.leapMonth(y)
+ var leapDay = this.leapDays(y)
+ if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+ if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+ var day = this.monthDays(y, m)
+ var _day = day
+ // bugFix 2016-9-25
+ // if month is leap, _day use leapDays method
+ if (isLeapMonth) {
+ _day = this.leapDays(y, m)
+ }
+ if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+ // 计算农历的时间差
+ var offset = 0
+ for (var i = 1900; i < y; i++) {
+ offset += this.lYearDays(i)
+ }
+ var leap = 0; var isAdd = false
+ for (var i = 1; i < m; i++) {
+ leap = this.leapMonth(y)
+ if (!isAdd) { // 处理闰月
+ if (leap <= i && leap > 0) {
+ offset += this.leapDays(y); isAdd = true
+ }
+ }
+ offset += this.monthDays(y, i)
+ }
+ // 转换闰月农历 需补充该年闰月的前一个月的时差
+ if (isLeapMonth) { offset += day }
+ // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+ var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+ var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+ var cY = calObj.getUTCFullYear()
+ var cM = calObj.getUTCMonth() + 1
+ var cD = calObj.getUTCDate()
+
+ return this.solar2lunar(cY, cM, cD)
+ }
+}
+
+export default calendar
diff --git a/uni_modules/uv-calendar/components/uv-calendar/header.vue b/uni_modules/uv-calendar/components/uv-calendar/header.vue
new file mode 100644
index 0000000..d7daa7b
--- /dev/null
+++ b/uni_modules/uv-calendar/components/uv-calendar/header.vue
@@ -0,0 +1,104 @@
+<template>
+ <view class="uv-calendar-header uv-border-bottom">
+ <text
+ class="uv-calendar-header__title"
+ v-if="showTitle"
+ >{{ title }}</text>
+ <text
+ class="uv-calendar-header__subtitle"
+ v-if="showSubtitle"
+ >{{ subtitle }}</text>
+ <view class="uv-calendar-header__weekdays">
+ <text class="uv-calendar-header__weekdays__weekday">一</text>
+ <text class="uv-calendar-header__weekdays__weekday">二</text>
+ <text class="uv-calendar-header__weekdays__weekday">三</text>
+ <text class="uv-calendar-header__weekdays__weekday">四</text>
+ <text class="uv-calendar-header__weekdays__weekday">五</text>
+ <text class="uv-calendar-header__weekdays__weekday">六</text>
+ <text class="uv-calendar-header__weekdays__weekday">日</text>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ export default {
+ name: 'uv-calendar-header',
+ mixins: [mpMixin, mixin],
+ props: {
+ // 标题
+ title: {
+ type: String,
+ default: ''
+ },
+ // 副标题
+ subtitle: {
+ type: [String,null],
+ default: ''
+ },
+ // 是否显示标题
+ showTitle: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示副标题
+ showSubtitle: {
+ type: Boolean,
+ default: true
+ },
+ },
+ data() {
+ return {
+
+ }
+ },
+ methods: {
+ name() {
+
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-border: 1;
+ $show-border-bottom: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-calendar-header {
+ padding-bottom: 4px;
+
+ &__title {
+ font-size: 16px;
+ color: $uv-main-color;
+ text-align: center;
+ height: 42px;
+ line-height: 42px;
+ font-weight: bold;
+ }
+
+ &__subtitle {
+ font-size: 14px;
+ color: $uv-main-color;
+ height: 40px;
+ text-align: center;
+ line-height: 40px;
+ font-weight: bold;
+ }
+
+ &__weekdays {
+ @include flex;
+ justify-content: space-between;
+
+ &__weekday {
+ font-size: 13px;
+ color: $uv-main-color;
+ line-height: 30px;
+ flex: 1;
+ text-align: center;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-calendar/components/uv-calendar/month.vue b/uni_modules/uv-calendar/components/uv-calendar/month.vue
new file mode 100644
index 0000000..1a649bc
--- /dev/null
+++ b/uni_modules/uv-calendar/components/uv-calendar/month.vue
@@ -0,0 +1,616 @@
+<template>
+ <view class="uv-calendar-month-wrapper" ref="uv-calendar-month-wrapper">
+ <view v-for="(item, index) in months" :key="index" :class="[`uv-calendar-month-${index}`]"
+ :ref="`uv-calendar-month-${index}`" :id="`month-${index}`">
+ <text v-if="index !== 0" class="uv-calendar-month__title">{{ item.year }}年{{ item.month }}月</text>
+ <view class="uv-calendar-month__days">
+ <view v-if="showMark" class="uv-calendar-month__days__month-mark-wrapper">
+ <text class="uv-calendar-month__days__month-mark-wrapper__text">{{ item.month }}</text>
+ </view>
+ <view class="uv-calendar-month__days__day" v-for="(item1, index1) in item.date" :key="index1"
+ :style="[dayStyle(index, index1, item1)]" @tap="clickHandler(index, index1, item1)"
+ :class="[item1.selected && 'uv-calendar-month__days__day__select--selected']">
+ <view class="uv-calendar-month__days__day__select" :style="[daySelectStyle(index, index1, item1)]">
+ <text v-if="getTopInfo(index, index1, item1)"
+ class="uv-calendar-month__days__day__select__top-info"
+ :class="[item1.disabled && 'uv-calendar-month__days__day__select__top-info--disabled']"
+ :style="[textStyle(item1)]"
+ >{{ getTopInfo(index, index1, item1) }}</text>
+ <text class="uv-calendar-month__days__day__select__info"
+ :class="[item1.disabled && 'uv-calendar-month__days__day__select__info--disabled']"
+ :style="[textStyle(item1)]">{{ item1.day }}</text>
+ <text v-if="getBottomInfo(index, index1, item1)"
+ class="uv-calendar-month__days__day__select__buttom-info"
+ :class="[item1.disabled && 'uv-calendar-month__days__day__select__buttom-info--disabled']"
+ :style="[textStyle(item1)]">{{ getBottomInfo(index, index1, item1) }}</text>
+ <text v-if="item1.dot" class="uv-calendar-month__days__day__select__dot"></text>
+ </view>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ // #ifdef APP-NVUE
+ // 由于nvue不支持百分比单位,需要查询宽度来计算每个日期的宽度
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ import { colorGradient } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js';
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import dayjs from '@/uni_modules/uv-ui-tools/libs/util/dayjs.js'
+ export default {
+ name: 'uv-calendar-month',
+ emits:['monthSelected','updateMonthTop','change'],
+ mixins: [mpMixin, mixin],
+ props: {
+ // 是否显示月份背景色
+ showMark: {
+ type: Boolean,
+ default: true
+ },
+ // 主题色,对底部按钮和选中日期有效
+ color: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 月份数据
+ months: {
+ type: Array,
+ default: () => []
+ },
+ // 日期选择类型
+ mode: {
+ type: String,
+ default: 'single'
+ },
+ // 日期行高
+ rowHeight: {
+ type: [String, Number],
+ default: 58
+ },
+ // mode=multiple时,最多可选多少个日期
+ maxCount: {
+ type: [String, Number],
+ default: Infinity
+ },
+ // mode=range时,第一个日期底部的提示文字
+ startText: {
+ type: String,
+ default: '开始'
+ },
+ // mode=range时,最后一个日期底部的提示文字
+ endText: {
+ type: String,
+ default: '结束'
+ },
+ // 默认选中的日期,mode为multiple或range是必须为数组格式
+ defaultDate: {
+ type: [Array, String, Date],
+ default: null
+ },
+ // 最小的可选日期
+ minDate: {
+ type: [String, Number],
+ default: 0
+ },
+ // 最大可选日期
+ maxDate: {
+ type: [String, Number],
+ default: 0
+ },
+ // 如果没有设置maxDate,则往后推多少个月
+ maxMonth: {
+ type: [String, Number],
+ default: 2
+ },
+ // 是否为只读状态,只读状态下禁止选择日期
+ readonly: {
+ type: Boolean,
+ default: false
+ },
+ // 日期区间最多可选天数,默认无限制,mode = range时有效
+ maxRange: {
+ type: [Number, String],
+ default: Infinity
+ },
+ // 范围选择超过最多可选天数时的提示文案,mode = range时有效
+ rangePrompt: {
+ type: String,
+ default: ''
+ },
+ // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
+ showRangePrompt: {
+ type: Boolean,
+ default: true
+ },
+ // 是否允许日期范围的起止时间为同一天,mode = range时有效
+ allowSameDay: {
+ type: Boolean,
+ default: false
+ }
+ },
+ data() {
+ return {
+ // 每个日期的宽度
+ width: 0,
+ // 当前选中的日期item
+ item: {},
+ selected: []
+ }
+ },
+ watch: {
+ selectedChange: {
+ immediate: true,
+ handler(n) {
+ this.setDefaultDate()
+ }
+ }
+ },
+ computed: {
+ // 多个条件的变化,会引起选中日期的变化,这里统一管理监听
+ selectedChange() {
+ return [this.minDate, this.maxDate, this.defaultDate]
+ },
+ dayStyle(index1, index2, item) {
+ return (index1, index2, item) => {
+ const style = {}
+ let week = item.week
+ // 不进行四舍五入的形式保留2位小数
+ const dayWidth = Number(parseFloat(this.width / 7).toFixed(3).slice(0, -1))
+ // 得出每个日期的宽度
+ // #ifdef APP-NVUE
+ style.width = this.$uv.addUnit(dayWidth)
+ // #endif
+ style.height = this.$uv.addUnit(this.rowHeight)
+ if (index2 === 0) {
+ // 获取当前为星期几,如果为0,则为星期天,减一为每月第一天时,需要向左偏移的item个数
+ week = (week === 0 ? 7 : week) - 1
+ style.marginLeft = this.$uv.addUnit(week * dayWidth)
+ }
+ if (this.mode === 'range') {
+ // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
+ style.paddingLeft = 0
+ style.paddingRight = 0
+ style.paddingBottom = 0
+ style.paddingTop = 0
+ }
+ return style
+ }
+ },
+ daySelectStyle() {
+ return (index1, index2, item) => {
+ let date = dayjs(item.date).format("YYYY-MM-DD"),
+ style = {}
+ // 判断date是否在selected数组中,因为月份可能会需要补0,所以使用dateSame判断,而不用数组的includes判断
+ if (this.selected.some(item => this.dateSame(item, date))) {
+ style.backgroundColor = this.color
+ }
+ if (this.mode === 'single') {
+ if (date === this.selected[0]) {
+ // 因为需要对nvue的兼容,只能这么写,无法缩写,也无法通过类名控制等等
+ style.borderTopLeftRadius = '3px'
+ style.borderBottomLeftRadius = '3px'
+ style.borderTopRightRadius = '3px'
+ style.borderBottomRightRadius = '3px'
+ }
+ } else if (this.mode === 'range') {
+ if (this.selected.length >= 2) {
+ const len = this.selected.length - 1
+ // 第一个日期设置左上角和左下角的圆角
+ if (this.dateSame(date, this.selected[0])) {
+ style.borderTopLeftRadius = '3px'
+ style.borderBottomLeftRadius = '3px'
+ }
+ // 最后一个日期设置右上角和右下角的圆角
+ if (this.dateSame(date, this.selected[len])) {
+ style.borderTopRightRadius = '3px'
+ style.borderBottomRightRadius = '3px'
+ }
+ // 处于第一和最后一个之间的日期,背景色设置为浅色,通过将对应颜色进行等分,再取其尾部的颜色值
+ if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
+ .selected[len]))) {
+ style.backgroundColor = colorGradient(this.color, '#ffffff', 100)[90]
+ // 增加一个透明度,让范围区间的背景色也能看到底部的mark水印字符
+ style.opacity = 0.7
+ }
+ } else if (this.selected.length === 1) {
+ // 之所以需要这么写,是因为DCloud公司的iOS客户端的开发者能力有限导致的bug
+ // 进行还原操作,否则在nvue的iOS,uni-app有bug,会导致诡异的表现
+ style.borderTopLeftRadius = '3px'
+ style.borderBottomLeftRadius = '3px'
+ }
+ } else {
+ if (this.selected.some(item => this.dateSame(item, date))) {
+ style.borderTopLeftRadius = '3px'
+ style.borderBottomLeftRadius = '3px'
+ style.borderTopRightRadius = '3px'
+ style.borderBottomRightRadius = '3px'
+ }
+ }
+ return style
+ }
+ },
+ // 某个日期是否被选中
+ textStyle() {
+ return (item) => {
+ const date = dayjs(item.date).format("YYYY-MM-DD"),
+ style = {}
+ // 选中的日期,提示文字设置白色
+ if (this.selected.some(item => this.dateSame(item, date))) {
+ style.color = '#ffffff'
+ }
+ if (this.mode === 'range') {
+ const len = this.selected.length - 1
+ // 如果是范围选择模式,第一个和最后一个之间的日期,文字颜色设置为高亮的主题色
+ if (dayjs(date).isAfter(dayjs(this.selected[0])) && dayjs(date).isBefore(dayjs(this
+ .selected[len]))) {
+ style.color = this.color
+ }
+ }
+ return style
+ }
+ },
+ // 获取顶部的提示文字
+ getTopInfo() {
+ return (index1, index2, item) => {
+ return item.topInfo;
+ }
+ },
+ // 获取底部的提示文字
+ getBottomInfo() {
+ return (index1, index2, item) => {
+ const date = dayjs(item.date).format("YYYY-MM-DD")
+ const bottomInfo = item.bottomInfo
+ // 当为日期范围模式时,且选择的日期个数大于0时
+ if (this.mode === 'range' && this.selected.length > 0) {
+ if (this.selected.length === 1) {
+ // 选择了一个日期时,如果当前日期为数组中的第一个日期,则显示底部文字为“开始”
+ if (this.dateSame(date, this.selected[0])) return this.startText
+ else return bottomInfo
+ } else {
+ const len = this.selected.length - 1
+ // 如果数组中的日期大于2个时,第一个和最后一个显示为开始和结束日期
+ if (this.dateSame(date, this.selected[0]) && this.dateSame(date, this.selected[1]) &&
+ len === 1) {
+ // 如果长度为2,且第一个等于第二个日期,则提示语放在同一个item中
+ return `${this.startText}/${this.endText}`
+ } else if (this.dateSame(date, this.selected[0])) {
+ return this.startText
+ } else if (this.dateSame(date, this.selected[len])) {
+ return this.endText
+ } else {
+ return bottomInfo
+ }
+ }
+ } else {
+ return bottomInfo
+ }
+ }
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 初始化默认选中
+ this.$emit('monthSelected', this.selected)
+ this.$nextTick(() => {
+ // 这里需要另一个延时,因为获取宽度后,会进行月份数据渲染,只有渲染完成之后,才有真正的高度
+ // 因为nvue下,$nextTick并不是100%可靠的
+ this.$uv.sleep(10).then(() => {
+ this.getWrapperWidth()
+ this.getMonthRect()
+ })
+ })
+ },
+ // 判断两个日期是否相等
+ dateSame(date1, date2) {
+ return dayjs(date1).isSame(dayjs(date2))
+ },
+ // 获取月份数据区域的宽度,因为nvue不支持百分比,所以无法通过css设置每个日期item的宽度
+ getWrapperWidth() {
+ // #ifdef APP-NVUE
+ dom.getComponentRect(this.$refs['uv-calendar-month-wrapper'], res => {
+ this.width = res.size.width
+ })
+ // #endif
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-calendar-month-wrapper').then(size => {
+ this.width = size.width
+ })
+ // #endif
+ },
+ getMonthRect() {
+ // 获取每个月份数据的尺寸,用于父组件在scroll-view滚动事件中,监听当前滚动到了第几个月份
+ const promiseAllArr = this.months.map((item, index) => this.getMonthRectByPromise(
+ `uv-calendar-month-${index}`))
+ // 一次性返回
+ Promise.all(promiseAllArr).then(
+ sizes => {
+ let height = 1
+ const topArr = []
+ for (let i = 0; i < this.months.length; i++) {
+ // 添加到months数组中,供scroll-view滚动事件中,判断当前滚动到哪个月份
+ topArr[i] = height
+ height += sizes[i].height
+ }
+ // 由于微信下,无法通过this.months[i].top的形式(引用类型)去修改父组件的month的top值,所以使用事件形式对外发出
+ this.$emit('updateMonthTop', topArr)
+ })
+ },
+ // 获取每个月份区域的尺寸
+ getMonthRectByPromise(el) {
+ // #ifndef APP-NVUE
+ // $uvGetRect为uvui自带的节点查询简化方法,详见文档介绍:https://www.uvui.cn/js/getRect.html
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`.${el}`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs[el][0], res => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ // 点击某一个日期
+ clickHandler(index1, index2, item) {
+ if (this.readonly) {
+ return;
+ }
+ this.item = item
+ const date = dayjs(item.date).format("YYYY-MM-DD")
+ if (item.disabled) return
+ // 对上一次选择的日期数组进行深度克隆
+ let selected = this.$uv.deepClone(this.selected)
+ if (this.mode === 'single') {
+ // 单选情况下,让数组中的元素为当前点击的日期
+ selected = [date]
+ } else if (this.mode === 'multiple') {
+ if (selected.some(item => this.dateSame(item, date))) {
+ // 如果点击的日期已在数组中,则进行移除操作,也就是达到反选的效果
+ const itemIndex = selected.findIndex(item => dayjs(item).format("YYYY-MM-DD") === dayjs(date).format("YYYY-MM-DD"))
+ selected.splice(itemIndex, 1)
+ } else {
+ // 如果点击的日期不在数组中,且已有的长度小于总可选长度时,则添加到数组中去
+ if (selected.length < this.maxCount) selected.push(date)
+ }
+ } else {
+ // 选择区间形式
+ if (selected.length === 0 || selected.length >= 2) {
+ // 如果原来就为0或者大于2的长度,则当前点击的日期,就是开始日期
+ selected = [date]
+ } else if (selected.length === 1) {
+ // 如果已经选择了开始日期
+ const existsDate = selected[0]
+ // 如果当前选择的日期小于上一次选择的日期,则当前的日期定为开始日期
+ if (dayjs(date).isBefore(existsDate)) {
+ selected = [date]
+ } else if (dayjs(date).isAfter(existsDate)) {
+ // 当前日期减去最大可选的日期天数,如果大于起始时间,则进行提示
+ if(dayjs(dayjs(date).subtract(this.maxRange, 'day')).isAfter(dayjs(selected[0])) && this.showRangePrompt) {
+ if(this.rangePrompt) {
+ this.$uv.toast(this.rangePrompt)
+ } else {
+ this.$uv.toast(`选择天数不能超过 ${this.maxRange} 天`)
+ }
+ return
+ }
+ // 如果当前日期大于已有日期,将当前的添加到数组尾部
+ selected.push(date)
+ const startDate = selected[0]
+ const endDate = selected[1]
+ const arr = []
+ let i = 0
+ do {
+ // 将开始和结束日期之间的日期添加到数组中
+ arr.push(dayjs(startDate).add(i, 'day').format("YYYY-MM-DD"))
+ i++
+ // 累加的日期小于结束日期时,继续下一次的循环
+ } while (dayjs(startDate).add(i, 'day').isBefore(dayjs(endDate)))
+ // 为了一次性修改数组,避免computed中多次触发,这里才用arr变量一次性赋值的方式,同时将最后一个日期添加近来
+ arr.push(endDate)
+ selected = arr
+ } else {
+ // 选择区间时,只有一个日期的情况下,且不允许选择起止为同一天的话,不允许选择自己
+ if (selected[0] === date && !this.allowSameDay) return
+ selected.push(date)
+ }
+ }
+ }
+ this.setSelected(selected)
+ this.$emit('change',{
+ day: date,
+ selected: selected
+ });
+ },
+ // 设置默认日期
+ setDefaultDate() {
+ if (!this.defaultDate) {
+ // 如果没有设置默认日期,则将当天日期设置为默认选中的日期
+ const selected = [dayjs().format("YYYY-MM-DD")]
+ return this.setSelected(selected, false)
+ }
+ let defaultDate = []
+ const minDate = this.minDate || dayjs().format("YYYY-MM-DD")
+ const maxDate = this.maxDate || dayjs(minDate).add(this.maxMonth - 1, 'month').format("YYYY-MM-DD")
+ if (this.mode === 'single') {
+ // 单选模式,可以是字符串或数组,Date对象等
+ if (!this.$uv.test.array(this.defaultDate)) {
+ defaultDate = [dayjs(this.defaultDate).format("YYYY-MM-DD")]
+ } else {
+ defaultDate = [this.defaultDate[0]]
+ }
+ } else {
+ // 如果为非数组,则不执行
+ if (!this.$uv.test.array(this.defaultDate)) return
+ defaultDate = this.defaultDate
+ }
+ // 过滤用户传递的默认数组,取出只在可允许最大值与最小值之间的元素
+ defaultDate = defaultDate.filter(item => {
+ return dayjs(item).isAfter(dayjs(minDate).subtract(1, 'day')) && dayjs(item).isBefore(dayjs(
+ maxDate).add(1, 'day'))
+ })
+ this.setSelected(defaultDate, false)
+ },
+ setSelected(selected, event = true) {
+ this.selected = selected
+ event && this.$emit('monthSelected', this.selected)
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-calendar-month-wrapper {
+ margin-top: 4px;
+ }
+
+ .uv-calendar-month {
+
+ &__title {
+ font-size: 14px;
+ line-height: 42px;
+ height: 42px;
+ color: $uv-main-color;
+ text-align: center;
+ font-weight: bold;
+ }
+
+ &__days {
+ position: relative;
+ @include flex;
+ flex-wrap: wrap;
+
+ &__month-mark-wrapper {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+
+ &__text {
+ font-size: 155px;
+ color: rgba(231, 232, 234, 0.83);
+ }
+ }
+
+ &__day {
+ @include flex;
+ padding: 2px;
+ /* #ifndef APP-NVUE */
+ // vue下使用css进行宽度计算,因为某些安卓机会无法进行js获取父元素宽度进行计算得出,会有偏移
+ width: calc(100% / 7);
+ box-sizing: border-box;
+ /* #endif */
+
+ &__select {
+ flex: 1;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+
+ &__dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 100px;
+ background-color: $uv-error;
+ position: absolute;
+ top: 12px;
+ right: 7px;
+ }
+
+ &__top-info {
+ color: $uv-content-color;
+ text-align: center;
+ position: absolute;
+ top: 2px;
+ font-size: 10px;
+ text-align: center;
+ left: 0;
+ right: 0;
+ &--selected {
+ color: #ffffff;
+ }
+
+ &--disabled {
+ color: #cacbcd;
+ }
+ }
+
+ &__buttom-info {
+ color: $uv-content-color;
+ text-align: center;
+ position: absolute;
+ bottom: 5px;
+ font-size: 10px;
+ text-align: center;
+ left: 0;
+ right: 0;
+
+ &--selected {
+ color: #ffffff;
+ }
+
+ &--disabled {
+ color: #cacbcd;
+ }
+ }
+
+ &__info {
+ text-align: center;
+ font-size: 16px;
+
+ &--selected {
+ color: #ffffff;
+ }
+
+ &--disabled {
+ color: #cacbcd;
+ }
+ }
+
+ &--selected {
+ background-color: $uv-primary;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ flex: 1;
+ border-radius: 3px;
+ }
+
+ &--range-selected {
+ opacity: 0.3;
+ border-radius: 0;
+ }
+
+ &--range-start-selected {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ &--range-end-selected {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-calendar/components/uv-calendar/props.js b/uni_modules/uv-calendar/components/uv-calendar/props.js
new file mode 100644
index 0000000..c359293
--- /dev/null
+++ b/uni_modules/uv-calendar/components/uv-calendar/props.js
@@ -0,0 +1,145 @@
+export default {
+ props: {
+ // 日历顶部标题
+ title: {
+ type: String,
+ default: '日期选择'
+ },
+ // 是否显示标题
+ showTitle: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示副标题
+ showSubtitle: {
+ type: Boolean,
+ default: true
+ },
+ // 日期类型选择,single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围
+ mode: {
+ type: String,
+ default: 'single'
+ },
+ // mode=range时,第一个日期底部的提示文字
+ startText: {
+ type: String,
+ default: '开始'
+ },
+ // mode=range时,最后一个日期底部的提示文字
+ endText: {
+ type: String,
+ default: '结束'
+ },
+ // 自定义列表
+ customList: {
+ type: Array,
+ default: () => []
+ },
+ // 主题色,对底部按钮和选中日期有效
+ color: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 最小的可选日期
+ minDate: {
+ type: [String, Number],
+ default: 0
+ },
+ // 最大可选日期
+ maxDate: {
+ type: [String, Number],
+ default: 0
+ },
+ // 默认选中的日期,mode为multiple或range是必须为数组格式
+ defaultDate: {
+ type: [Array, String, Date, null],
+ default: null
+ },
+ // mode=multiple时,最多可选多少个日期
+ maxCount: {
+ type: [String, Number],
+ default: Number.MAX_SAFE_INTEGER
+ },
+ // 日期行高
+ rowHeight: {
+ type: [String, Number],
+ default: 56
+ },
+ // 日期格式化函数
+ formatter: {
+ type: [Function, null],
+ default: null
+ },
+ // 是否显示农历
+ showLunar: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示月份背景色
+ showMark: {
+ type: Boolean,
+ default: true
+ },
+ // 确定按钮的文字
+ confirmText: {
+ type: String,
+ default: '确定'
+ },
+ // 确认按钮处于禁用状态时的文字
+ confirmDisabledText: {
+ type: String,
+ default: '确定'
+ },
+ // 是否允许点击遮罩关闭日历
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: false
+ },
+ // 是否允许点击确认按钮关闭日历
+ closeOnClickConfirm: {
+ type: Boolean,
+ default: true
+ },
+ // 是否为只读状态,只读状态下禁止选择日期
+ readonly: {
+ type: Boolean,
+ default: false
+ },
+ // 是否展示确认按钮
+ showConfirm: {
+ type: Boolean,
+ default: true
+ },
+ // 日期区间最多可选天数,默认无限制,mode = range时有效 Infinity
+ maxRange: {
+ type: [Number, String],
+ default: Number.MAX_SAFE_INTEGER
+ },
+ // 范围选择超过最多可选天数时的提示文案,mode = range时有效
+ rangePrompt: {
+ type: String,
+ default: ''
+ },
+ // 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效
+ showRangePrompt: {
+ type: Boolean,
+ default: true
+ },
+ // 是否允许日期范围的起止时间为同一天,mode = range时有效
+ allowSameDay: {
+ type: Boolean,
+ default: false
+ },
+ // 圆角值
+ round: {
+ type: [Boolean, String, Number],
+ default: 0
+ },
+ // 最多展示月份数量
+ monthNum: {
+ type: [Number, String],
+ default: 3
+ },
+ ...uni.$uv?.props?.calendar
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-calendar/components/uv-calendar/uv-calendar.vue b/uni_modules/uv-calendar/components/uv-calendar/uv-calendar.vue
new file mode 100644
index 0000000..626e833
--- /dev/null
+++ b/uni_modules/uv-calendar/components/uv-calendar/uv-calendar.vue
@@ -0,0 +1,390 @@
+<template>
+ <uv-popup
+ ref="calendarPopup"
+ mode="bottom"
+ closeable
+ :round="round"
+ :closeOnClickOverlay="closeOnClickOverlay"
+ @change="popupChange"
+ >
+ <view class="uv-calendar">
+ <uvHeader
+ :title="title"
+ :subtitle="subtitle"
+ :showSubtitle="showSubtitle"
+ :showTitle="showTitle"
+ ></uvHeader>
+ <scroll-view
+ :style="{ height: $uv.addUnit(listHeight) }"
+ scroll-y
+ @scroll="onScroll"
+ :scroll-top="scrollTop"
+ :scrollIntoView="scrollIntoView"
+ >
+ <uvMonth
+ :color="color"
+ :rowHeight="rowHeight"
+ :showMark="showMark"
+ :months="months"
+ :mode="mode"
+ :maxCount="maxCount"
+ :startText="startText"
+ :endText="endText"
+ :defaultDate="defaultDate"
+ :minDate="innerMinDate"
+ :maxDate="innerMaxDate"
+ :maxMonth="monthNum"
+ :readonly="readonly"
+ :maxRange="maxRange"
+ :rangePrompt="rangePrompt"
+ :showRangePrompt="showRangePrompt"
+ :allowSameDay="allowSameDay"
+ ref="month"
+ @monthSelected="monthSelected"
+ @updateMonthTop="updateMonthTop"
+ @change="changeDay"
+ ></uvMonth>
+ </scroll-view>
+ <slot name="footer" v-if="showConfirm">
+ <view class="uv-calendar__confirm">
+ <uv-button
+ shape="circle"
+ :text="buttonDisabled ? confirmDisabledText : confirmText"
+ :color="color"
+ @click="confirm"
+ :disabled="buttonDisabled"
+ ></uv-button>
+ </view>
+ </slot>
+ </view>
+ </uv-popup>
+</template>
+
+<script>
+import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+import uvHeader from './header.vue'
+import uvMonth from './month.vue'
+import props from './props.js'
+import dayjs from '@/uni_modules/uv-ui-tools/libs/util/dayjs.js'
+import Calendar from './calendar.js'
+/**
+ * Calendar 日历
+ * @description 此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中.
+ * @tutorial https://www.uvui.cn/components/calendar.html
+ *
+ * @property {String} title 标题内容 (默认 日期选择 )
+ * @property {Boolean} showTitle 是否显示标题 (默认 true )
+ * @property {Boolean} showSubtitle 是否显示副标题 (默认 true )
+ * @property {String} mode 日期类型选择 single-选择单个日期,multiple-可以选择多个日期,range-选择日期范围 ( 默认 'single' )
+ * @property {String} startText mode=range时,第一个日期底部的提示文字 (默认 '开始' )
+ * @property {String} endText mode=range时,最后一个日期底部的提示文字 (默认 '结束' )
+ * @property {Array} customList 自定义列表
+ * @property {String} color 主题色,对底部按钮和选中日期有效 (默认 ‘#3c9cff' )
+ * @property {String | Number} minDate 最小的可选日期 (默认 0 )
+ * @property {String | Number} maxDate 最大可选日期 (默认 0 )
+ * @property {Array | String| Date} defaultDate 默认选中的日期,mode为multiple或range是必须为数组格式
+ * @property {String | Number} maxCount mode=multiple时,最多可选多少个日期 (默认 Number.MAX_SAFE_INTEGER )
+ * @property {String | Number} rowHeight 日期行高 (默认 56 )
+ * @property {Function} formatter 日期格式化函数
+ * @property {Boolean} showLunar 是否显示农历 (默认 false )
+ * @property {Boolean} showMark 是否显示月份背景色 (默认 true )
+ * @property {String} confirmText 确定按钮的文字 (默认 '确定' )
+ * @property {String} confirmDisabledText 确认按钮处于禁用状态时的文字 (默认 '确定' )
+ * @property {Boolean} show 是否显示日历弹窗 (默认 false )
+ * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭日历 (默认 false )
+ * @property {Boolean} closeOnClickConfirm 是否允许点击确认按钮关闭日历,设置为false不影响confirm事件返回 (默认 true )
+ * @property {Boolean} readonly 是否为只读状态,只读状态下禁止选择日期 (默认 false )
+ * @property {String | Number} maxRange 日期区间最多可选天数,默认无限制,mode = range时有效
+ * @property {String} rangePrompt 范围选择超过最多可选天数时的提示文案,mode = range时有效
+ * @property {Boolean} showRangePrompt 范围选择超过最多可选天数时,是否展示提示文案,mode = range时有效 (默认 true )
+ * @property {Boolean} allowSameDay 是否允许日期范围的起止时间为同一天,mode = range时有效 (默认 false )
+ * @property {Number|String} round 圆角值,默认无圆角 (默认 0 )
+ * @property {Number|String} monthNum 最多展示的月份数量 (默认 3 )
+ *
+ * @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数
+ * @event {Function()} close 日历关闭时触发 可定义页面关闭时的回调事件
+ * @example <uv-calendar ref="calendar" :defaultDate="defaultDateMultiple" mode="multiple" @confirm="confirm">
+ </uv-calendar>
+ * */
+export default {
+ name: 'uv-calendar',
+ emits:['confirm','close','change'],
+ mixins: [mpMixin, mixin, props],
+ components: {
+ uvHeader,
+ uvMonth
+ },
+ data() {
+ return {
+ // 需要显示的月份的数组
+ months: [],
+ // 在月份滚动区域中,当前视图中月份的index索引
+ monthIndex: 0,
+ // 月份滚动区域的高度
+ listHeight: 0,
+ // month组件中选择的日期数组
+ selected: [],
+ scrollIntoView: '',
+ scrollTop:0,
+ // 过滤处理方法
+ innerFormatter: (value) => value
+ }
+ },
+ watch: {
+ selectedChange: {
+ immediate: true,
+ handler(n) {
+ this.setMonth()
+ }
+ }
+ },
+ computed: {
+ // 由于maxDate和minDate可以为字符串(2021-10-10),或者数值(时间戳),但是dayjs如果接受字符串形式的时间戳会有问题,这里进行处理
+ innerMaxDate() {
+ return this.$uv.test.number(this.maxDate)
+ ? Number(this.maxDate)
+ : this.maxDate
+ },
+ innerMinDate() {
+ return this.$uv.test.number(this.minDate)
+ ? Number(this.minDate)
+ : this.minDate
+ },
+ // 多个条件的变化,会引起选中日期的变化,这里统一管理监听
+ selectedChange() {
+ return [this.innerMinDate, this.innerMaxDate, this.defaultDate]
+ },
+ subtitle() {
+ // 初始化时,this.months为空数组,所以需要特别判断处理
+ if (this.months.length) {
+ return `${this.months[this.monthIndex].year}年${
+ this.months[this.monthIndex].month
+ }月`
+ } else {
+ return ''
+ }
+ },
+ buttonDisabled() {
+ // 如果为range类型,且选择的日期个数不足1个时,让底部的按钮出于disabled状态
+ if (this.mode === 'range') {
+ if (this.selected.length <= 1) {
+ return true
+ } else {
+ return false
+ }
+ } else {
+ return false
+ }
+ }
+ },
+ mounted() {
+ this.start = Date.now()
+ this.init()
+ },
+ methods: {
+ // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
+ setFormatter(e) {
+ this.innerFormatter = e
+ },
+ // 点击日期框触发
+ changeDay(e) {
+ this.$emit('change',e);
+ },
+ // month组件内部选择日期后,通过事件通知给父组件
+ monthSelected(e) {
+ this.selected = e
+ if (!this.showConfirm) {
+ // 在不需要确认按钮的情况下,如果为单选,或者范围多选且已选长度大于2,则直接进行返还
+ if (
+ this.mode === 'multiple' ||
+ this.mode === 'single' ||
+ (this.mode === 'range' && this.selected.length >= 2)
+ ) {
+ this.$emit('confirm', this.selected)
+ }
+ }
+ },
+ init() {
+ // 校验maxDate,不能小于minDate
+ if (
+ this.innerMaxDate &&
+ this.innerMinDate &&
+ new Date(this.innerMaxDate).getTime() < new Date(this.innerMinDate).getTime()
+ ) {
+ return this.$uv.error('maxDate不能小于minDate')
+ }
+ // 滚动区域的高度
+ this.listHeight = this.rowHeight * 5 + 30
+ this.setMonth()
+ },
+ open() {
+ this.setMonth()
+ this.$refs.calendarPopup.open();
+ },
+ popupChange(e) {
+ if(!e.show) {
+ this.$emit('close');
+ }
+ },
+ // 点击确定按钮
+ confirm() {
+ if (!this.buttonDisabled) {
+ this.$emit('confirm', this.selected)
+ }
+ if (this.closeOnClickConfirm) {
+ this.$refs.calendarPopup.close();
+ }
+ },
+ // 获得两个日期之间的月份数
+ getMonths(minDate, maxDate) {
+ const minYear = dayjs(minDate).year()
+ const minMonth = dayjs(minDate).month() + 1
+ const maxYear = dayjs(maxDate).year()
+ const maxMonth = dayjs(maxDate).month() + 1
+ return (maxYear - minYear) * 12 + (maxMonth - minMonth) + 1
+ },
+ // 设置月份数据
+ setMonth() {
+ // 最小日期的毫秒数
+ const minDate = this.innerMinDate || dayjs().valueOf()
+ // 如果没有指定最大日期,则往后推3个月
+ const maxDate =
+ this.innerMaxDate ||
+ dayjs(minDate)
+ .add(this.monthNum - 1, 'month')
+ .valueOf()
+ // 最大最小月份之间的共有多少个月份,
+ const months = this.$uv.range(
+ 1,
+ this.monthNum,
+ this.getMonths(minDate, maxDate)
+ )
+ // 先清空数组
+ this.months = []
+ for (let i = 0; i < months; i++) {
+ this.months.push({
+ date: new Array(
+ dayjs(minDate).add(i, 'month').daysInMonth()
+ )
+ .fill(1)
+ .map((item, index) => {
+ // 日期,取值1-31
+ let day = index + 1
+ // 星期,0-6,0为周日
+ const week = dayjs(minDate)
+ .add(i, 'month')
+ .date(day)
+ .day()
+ const date = dayjs(minDate)
+ .add(i, 'month')
+ .date(day)
+ .format('YYYY-MM-DD')
+ let topInfo = ''
+ let bottomInfo = ''
+ if (this.showLunar) {
+ // 将日期转为农历格式
+ const lunar = Calendar.solar2lunar(
+ dayjs(date).year(),
+ dayjs(date).month() + 1,
+ dayjs(date).date()
+ )
+ bottomInfo = lunar.IDayCn
+ }
+ let config = {
+ day,
+ week,
+ // 小于最小允许的日期,或者大于最大的日期,则设置为disabled状态
+ disabled:
+ dayjs(date).isBefore(
+ dayjs(minDate).format('YYYY-MM-DD')
+ ) ||
+ dayjs(date).isAfter(
+ dayjs(maxDate).format('YYYY-MM-DD')
+ ),
+ // 返回一个日期对象,供外部的formatter获取当前日期的年月日等信息,进行加工处理
+ date: new Date(date),
+ topInfo,
+ bottomInfo,
+ dot: false,
+ month:
+ dayjs(minDate).add(i, 'month').month() + 1
+ }
+ const formatter =
+ this.formatter || this.innerFormatter
+ return formatter(config)
+ }),
+ // 当前所属的月份
+ month: dayjs(minDate).add(i, 'month').month() + 1,
+ // 当前年份
+ year: dayjs(minDate).add(i, 'month').year()
+ })
+ }
+
+ },
+ // 滚动到默认设置的月份
+ scrollIntoDefaultMonth(selected) {
+ // 查询默认日期在可选列表的下标
+ const _index = this.months.findIndex(({
+ year,
+ month
+ }) => {
+ month = this.$uv.padZero(month)
+ return `${year}-${month}` === selected
+ })
+ if (_index !== -1) {
+ // #ifndef MP-WEIXIN
+ this.$nextTick(() => {
+ this.scrollIntoView = `month-${_index}`
+ })
+ // #endif
+ // #ifdef MP-WEIXIN
+ this.scrollTop = this.months[_index].top || 0;
+ // #endif
+ }
+ },
+ // scroll-view滚动监听
+ onScroll(event) {
+ // 不允许小于0的滚动值,如果scroll-view到顶了,继续下拉,会出现负数值
+ const scrollTop = Math.max(0, event.detail.scrollTop)
+ // 将当前滚动条数值,除以滚动区域的高度,可以得出当前滚动到了哪一个月份的索引
+ for (let i = 0; i < this.months.length; i++) {
+ if (scrollTop >= (this.months[i].top || this.listHeight)) {
+ this.monthIndex = i
+ }
+ }
+ },
+ // 更新月份的top值
+ updateMonthTop(topArr = []) {
+ // 设置对应月份的top值,用于onScroll方法更新月份
+ topArr.map((item, index) => {
+ this.months[index].top = item
+ })
+
+ // 获取默认日期的下标
+ if (!this.defaultDate) {
+ // 如果没有设置默认日期,则将当天日期设置为默认选中的日期
+ const selected = dayjs().format("YYYY-MM")
+ this.scrollIntoDefaultMonth(selected)
+ return
+ }
+ let selected = dayjs().format("YYYY-MM");
+ // 单选模式,可以是字符串或数组,Date对象等
+ if (!this.$uv.test.array(this.defaultDate)) {
+ selected = dayjs(this.defaultDate).format("YYYY-MM")
+ } else {
+ selected = dayjs(this.defaultDate[0]).format("YYYY-MM");
+ }
+ this.scrollIntoDefaultMonth(selected)
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.uv-calendar {
+ &__confirm {
+ padding: 7px 18px;
+ }
+}
+</style>
diff --git a/uni_modules/uv-calendar/package.json b/uni_modules/uv-calendar/package.json
new file mode 100644
index 0000000..03d8812
--- /dev/null
+++ b/uni_modules/uv-calendar/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-calendar",
+ "displayName": "uv-calendar 日历 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.6",
+ "description": "日历组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中,灵活配置,功能齐全,兼容全端。强烈推荐使用最新版日历组件,在下方跳入。",
+ "keywords": [
+ "uv-calendar",
+ "uvui",
+ "uv-ui",
+ "calendar",
+ "日历"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-button",
+ "uv-popup"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-calendar/readme.md b/uni_modules/uv-calendar/readme.md
new file mode 100644
index 0000000..3c2de54
--- /dev/null
+++ b/uni_modules/uv-calendar/readme.md
@@ -0,0 +1,21 @@
+## Calendar 日历
+
+> **组件名:uv-calendar**
+
+此组件用于单个选择日期,范围选择日期等,日历被包裹在底部弹起的容器中。灵活配置,功能齐全,兼容全端。
+
+## 温馨提示:该组件不再更新,强烈推荐使用最新版日历组件:[https://www.uvui.cn/components/calendars.html](https://www.uvui.cn/components/calendars.html)。基于 `uv-ui` 插件市场首款多功能日历组件,不仅可以查看、选择日期,还可以选择任意范围内的日期、打点操作、自定义主题颜色、自定义文案、农历显示等。追求的就是完美。
+
+# <a href="https://www.uvui.cn/components/calendar.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-calendars/changelog.md b/uni_modules/uv-calendars/changelog.md
new file mode 100644
index 0000000..b3b2e38
--- /dev/null
+++ b/uni_modules/uv-calendars/changelog.md
@@ -0,0 +1,40 @@
+## 1.0.15(2023-11-08)
+1. 增加readonly属性,是否为只读状态,只读状态下禁止选择日期
+## 1.0.14(2023-10-12)
+1. 修复selected没有设置了info或者info设置为空字符串后,文本则无法恢复BUG
+## 1.0.13(2023-09-19)
+1. 修复range模式下,selected设置了info后选中后,导致文本不恢复的问题
+2. 修复multiple模式下,selected自定义信息的颜色没变,依然是白色
+## 1.0.12(2023-09-14)
+1. 优化
+## 1.0.11(2023-09-14)
+1. 增加allowSameDay参数,是否允许日期范围的起止时间为同一天,mode = range时有效
+2. 修复在vue2+小程序渲染时闪烁的问题
+## 1.0.10(2023-09-07)
+1. 修复国际化失效的BUG
+## 1.0.9(2023-09-01)
+1. 修复在pages.json中设置easycom会报错的BUG
+## 1.0.8(2023-08-29)
+1. 修复mainjs中设置setConfig修改属性不生效的问题,出自评论区:https://ext.dcloud.net.cn/plugin?id=12287
+## 1.0.7(2023-08-26)
+1. 去除range参数,由mode="range"替换
+2. 新增mode参数,不传 / multiple / range,分别为单日期, 多个日期,选择日期范围
+3. 与uv-calendar选择日期的功能保持一致
+## 1.0.6(2023-08-25)
+1. 修复点击返回今天按钮时,monthSwitch方法回调参数返回月份不是当天对应月份:https://github.com/climblee/uv-ui/issues/7
+## 1.0.5(2023-08-13)
+1. 修复选择月份弹窗层级的问题
+## 1.0.4(2023-08-06)
+1. 优化
+## 1.0.3(2023-08-06)
+1. 修复高度不对的BUG
+2. 修复文案在小屏幕的BUG
+## 1.0.2(2023-08-05)
+1. 增加startText参数
+2. 增加endText参数
+3. 增加selected中的参数
+4. 优化日历范围选择
+## 1.0.1(2023-08-04)
+1. 修复 自定义主题时 颜色错误的BUG
+## 1.0.0(2023-08-03)
+1. 新增 uv-calendars 新版日历发布
diff --git a/uni_modules/uv-calendars/components/uv-calendars/calendar-body.vue b/uni_modules/uv-calendars/components/uv-calendars/calendar-body.vue
new file mode 100644
index 0000000..b675fbd
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/calendar-body.vue
@@ -0,0 +1,376 @@
+<template>
+ <view class="uv-calendar-body">
+ <view class="uv-calendar__header">
+ <view class="uv-calendar__header-btn-box" @click.stop="pre">
+ <view class="uv-calendar__header-btn uv-calendar--left"></view>
+ </view>
+ <picker mode="date" :value="getDate" fields="month" @change="bindDateChange">
+ <text class="uv-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text>
+ </picker>
+ <view class="uv-calendar__header-btn-box" @click.stop="next">
+ <view class="uv-calendar__header-btn uv-calendar--right"></view>
+ </view>
+ <text class="uv-calendar__backtoday" @click="backToday">{{todayText}}</text>
+ </view>
+ <view class="uv-calendar__box">
+ <view v-if="showMonth" class="uv-calendar__box-bg">
+ <text class="uv-calendar__box-bg-text">{{nowDate.month}}</text>
+ </view>
+ <view class="uv-calendar__weeks uv-calendar__weeks-week">
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{SUNText}}</text>
+ </view>
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{monText}}</text>
+ </view>
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{TUEText}}</text>
+ </view>
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{WEDText}}</text>
+ </view>
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{THUText}}</text>
+ </view>
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{FRIText}}</text>
+ </view>
+ <view class="uv-calendar__weeks-day">
+ <text class="uv-calendar__weeks-day-text">{{SATText}}</text>
+ </view>
+ </view>
+ <view class="uv-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+ <view class="uv-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+ <calendar-item class="uv-calendar-item--hook" :weeks="weeks" :rangeInfoText="rangeInfoText(weeks)" :multiple="multiple" :range="range" :calendar="calendar" :selected="selected" :lunar="lunar" :color="color" @change="choiceDate"></calendar-item>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+
+ import CalendarItem from './calendar-item.vue';
+
+ import { initVueI18n } from '@dcloudio/uni-i18n';
+ import i18nMessages from './i18n/index.js';
+ const { t } = initVueI18n(i18nMessages);
+ export default {
+ mixins: [mpMixin, mixin],
+ components: {
+ CalendarItem
+ },
+ props: {
+ date: {
+ type: [String,Array],
+ default: ''
+ },
+ nowDate: {
+ type: [String, Object],
+ default: ''
+ },
+ weeks: {
+ type: [Array, Object],
+ default () {
+ return []
+ }
+ },
+ calendar: {
+ type: Object,
+ default () {
+ return {}
+ }
+ },
+ selected: {
+ type: Array,
+ default () {
+ return []
+ }
+ },
+ lunar: {
+ type: Boolean,
+ default: false
+ },
+ showMonth: {
+ type: Boolean,
+ default: true
+ },
+ color: {
+ type: String,
+ default: '#3c9cff'
+ },
+ startText: {
+ type: String,
+ default: '开始'
+ },
+ endText: {
+ type: String,
+ default: '结束'
+ },
+ range: {
+ type: Boolean,
+ default: false
+ },
+ multiple: {
+ type: Boolean,
+ default: false
+ },
+ // 是否允许日期范围的起止时间为同一天,mode = range时有效
+ allowSameDay: {
+ type: Boolean,
+ default: false
+ }
+ },
+ computed: {
+ getDate() {
+ return Array.isArray(this.date) ? this.date[0] : this.date;
+ },
+ /**
+ * for i18n
+ */
+ todayText() {
+ return t("uv-calender.today")
+ },
+ monText() {
+ return t("uv-calender.MON")
+ },
+ TUEText() {
+ return t("uv-calender.TUE")
+ },
+ WEDText() {
+ return t("uv-calender.WED")
+ },
+ THUText() {
+ return t("uv-calender.THU")
+ },
+ FRIText() {
+ return t("uv-calender.FRI")
+ },
+ SATText() {
+ return t("uv-calender.SAT")
+ },
+ SUNText() {
+ return t("uv-calender.SUN")
+ },
+ rangeInfoText(weeks) {
+ return weeks=> {
+ if(this.allowSameDay && weeks.beforeRange && weeks.afterRange && weeks.dateEqual) {
+ return this.setInfo(weeks,`${this.startText}/${this.endText}`);
+ }
+ if(weeks.beforeRange) {
+ return this.setInfo(weeks,this.startText);
+ }
+ if(weeks.afterRange) {
+ return this.setInfo(weeks,this.endText);
+ }
+ if(weeks.extraInfo?.info_old == ' ') {
+ weeks.extraInfo.info = null;
+ }else if(weeks.extraInfo?.info_old) {
+ weeks.extraInfo.info = weeks.extraInfo.info_old;
+ }
+ }
+ }
+ },
+ methods: {
+ setInfo(weeks,text) {
+ this.setInfoOld(weeks);
+ if(weeks.extraInfo) {
+ weeks.extraInfo.info = text;
+ }else {
+ weeks.extraInfo = {
+ info: text
+ }
+ }
+ },
+ setInfoOld(weeks) {
+ if(weeks.extraInfo) {
+ weeks.extraInfo.info_old = weeks.extraInfo.info ? weeks.extraInfo.info_old || weeks.extraInfo.info : ' ';
+ }
+ },
+ bindDateChange(e) {
+ this.$emit('bindDateChange', e);
+ },
+ backToday() {
+ this.$emit('backToday');
+ },
+ pre() {
+ this.$emit('pre');
+ },
+ next() {
+ this.$emit('next');
+ },
+ choiceDate(e) {
+ this.$emit('choiceDate', e);
+ }
+ }
+ }
+</script>
+
+<style scoped lang="scss">
+ @mixin flex($direction: row) {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ flex-direction: $direction;
+ }
+ $uv-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
+ $uv-border-color: #EDEDED !default;
+ $uv-text-color: #333;
+ $uv-bg-color-hover: #f1f1f1;
+ $uv-font-size-base: 14px;
+ $uv-text-color-placeholder: #808080;
+ $uv-color-subtitle: #555555;
+ $uv-text-color-grey: #999;
+ .uv-calendar {
+ @include flex(column);
+ }
+ .uv-calendar__mask {
+ position: fixed;
+ bottom: 0;
+ top: 0;
+ left: 0;
+ right: 0;
+ background-color: $uv-bg-color-mask;
+ transition-property: opacity;
+ transition-duration: 0.3s;
+ opacity: 0;
+ /* #ifndef APP-NVUE */
+ z-index: 99;
+ /* #endif */
+ }
+ .uv-calendar--mask-show {
+ opacity: 1
+ }
+ .uv-calendar--fixed {
+ position: fixed;
+ /* #ifdef APP-NVUE */
+ bottom: 0;
+ /* #endif */
+ left: 0;
+ right: 0;
+ transition-property: transform;
+ transition-duration: 0.3s;
+ transform: translateY(460px);
+ /* #ifndef APP-NVUE */
+ bottom: calc(var(--window-bottom));
+ z-index: 99;
+ /* #endif */
+ }
+ .uv-calendar--ani-show {
+ transform: translateY(0);
+ }
+ .uv-calendar__content {
+ background-color: #fff;
+ }
+ .uv-calendar__header {
+ position: relative;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ height: 50px;
+ border-bottom-color: $uv-border-color;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ }
+ .uv-calendar--fixed-top {
+ @include flex;
+ justify-content: space-between;
+ border-top-color: $uv-border-color;
+ border-top-style: solid;
+ border-top-width: 1px;
+ }
+ .uv-calendar--fixed-width {
+ width: 50px;
+ }
+ .uv-calendar__backtoday {
+ position: absolute;
+ right: 0;
+ top: 25rpx;
+ padding: 0 5px;
+ padding-left: 10px;
+ height: 25px;
+ line-height: 25px;
+ font-size: 12px;
+ border-top-left-radius: 25px;
+ border-bottom-left-radius: 25px;
+ color: $uv-text-color;
+ background-color: $uv-bg-color-hover;
+ }
+ .uv-calendar__header-text {
+ text-align: center;
+ width: 100px;
+ font-size: $uv-font-size-base;
+ color: $uv-text-color;
+ }
+ .uv-calendar__header-btn-box {
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ width: 50px;
+ height: 50px;
+ }
+ .uv-calendar__header-btn {
+ width: 10px;
+ height: 10px;
+ border-left-color: $uv-text-color-placeholder;
+ border-left-style: solid;
+ border-left-width: 2px;
+ border-top-color: $uv-color-subtitle;
+ border-top-style: solid;
+ border-top-width: 2px;
+ }
+ .uv-calendar--left {
+ transform: rotate(-45deg);
+ }
+ .uv-calendar--right {
+ transform: rotate(135deg);
+ }
+ .uv-calendar__weeks {
+ position: relative;
+ @include flex;
+ }
+ .uv-calendar__weeks-week {
+ padding: 0 0 2rpx;
+ }
+ .uv-calendar__weeks-item {
+ flex: 1;
+ }
+ .uv-calendar__weeks-day {
+ flex: 1;
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+ height: 45px;
+ border-bottom-color: #F5F5F5;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ }
+ .uv-calendar__weeks-day-text {
+ font-size: 14px;
+ }
+ .uv-calendar__box {
+ position: relative;
+ }
+ .uv-calendar__box-bg {
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+ .uv-calendar__box-bg-text {
+ font-size: 200px;
+ font-weight: bold;
+ color: $uv-text-color-grey;
+ opacity: 0.1;
+ text-align: center;
+ /* #ifndef APP-NVUE */
+ line-height: 1;
+ /* #endif */
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-calendars/components/uv-calendars/calendar-item.vue b/uni_modules/uv-calendars/components/uv-calendars/calendar-item.vue
new file mode 100644
index 0000000..862d068
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/calendar-item.vue
@@ -0,0 +1,248 @@
+<template>
+ <view class="uv-calendar-item__weeks-box" :class="{
+ 'uv-calendar-item--disable':weeks.disable || (weeks.extraInfo&&weeks.extraInfo.disable),
+ 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple,
+ 'uv-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple) ,
+ 'uv-calendar-item--before-checked':weeks.beforeRange,
+ 'uv-calendar-item--range': weeks.range,
+ 'uv-calendar-item--after-checked':weeks.afterRange,
+ 'uv-calendar-item--multiple':weeks.multiple
+ }" :style="[itemBoxStyle]" @click="choiceDate(weeks)">
+ <view class="uv-calendar-item__weeks-box-item">
+ <text v-if="selected&&weeks.extraInfo&&weeks.extraInfo.badge" class="uv-calendar-item__weeks-box-circle"></text>
+ <text class="uv-calendar-item__weeks-top-text" v-if="weeks.extraInfo&&weeks.extraInfo.topinfo" :style="[infoStyle('top')]">{{weeks.extraInfo&&weeks.extraInfo.topinfo}}</text>
+ <text class="uv-calendar-item__weeks-box-text" :class="{
+ 'uv-calendar-item--isDay-text': weeks.isDay,
+ 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple,
+ 'uv-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple,
+ 'uv-calendar-item--before-checked':weeks.beforeRange,
+ 'uv-calendar-item--range': weeks.range,
+ 'uv-calendar-item--after-checked':weeks.afterRange,
+ 'uv-calendar-item--multiple':weeks.multiple,
+ 'uv-calendar-item--disable':weeks.disable || (weeks.extraInfo&&weeks.extraInfo.disable)
+ }" :style="[itemBoxStyle]">{{weeks.date}}</text>
+ <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uv-calendar-item__weeks-lunar-text" :class="{
+ 'uv-calendar-item--isDay-text':weeks.isDay,
+ 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple,
+ 'uv-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple,
+ 'uv-calendar-item--before-checked':weeks.beforeRange,
+ 'uv-calendar-item--range': weeks.range,
+ 'uv-calendar-item--after-checked':weeks.afterRange,
+ 'uv-calendar-item--multiple':weeks.multiple
+ }" :style="[itemBoxStyle]">{{todayText}}</text>
+ <text v-if="lunar&&!weeks.extraInfo" class="uv-calendar-item__weeks-lunar-text" :class="{
+ 'uv-calendar-item--isDay-text':weeks.isDay,
+ 'uv-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay && !multiple,
+ 'uv-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay && !multiple,
+ 'uv-calendar-item--before-checked':weeks.beforeRange,
+ 'uv-calendar-item--range': weeks.range,
+ 'uv-calendar-item--after-checked':weeks.afterRange,
+ 'uv-calendar-item--multiple':weeks.multiple,
+ 'uv-calendar-item--disable':weeks.disable || (weeks.extraInfo&&weeks.extraInfo.disable)
+ }" :style="[itemBoxStyle]">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
+ <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uv-calendar-item__weeks-lunar-text" :class="{
+ 'uv-calendar-item__weeks-lunar-text--equal': weeks.dateEqual
+ }" :style="[infoStyle('bottom')]">{{weeks.extraInfo.info}}</text>
+ </view>
+ </view>
+</template>
+<script>
+ import { colorGradient } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js';
+ import { initVueI18n } from '@dcloudio/uni-i18n'
+ import i18nMessages from './i18n/index.js'
+ const { t } = initVueI18n(i18nMessages)
+ export default {
+ emits: ['change'],
+ props: {
+ weeks: {
+ type: Object,
+ default () {
+ return {}
+ }
+ },
+ calendar: {
+ type: Object,
+ default: () => {
+ return {}
+ }
+ },
+ selected: {
+ type: Array,
+ default: () => {
+ return []
+ }
+ },
+ lunar: {
+ type: Boolean,
+ default: false
+ },
+ color: {
+ type: String,
+ default: '#3c9cff'
+ },
+ range: {
+ type: Boolean,
+ default: false
+ },
+ multiple: {
+ type: Boolean,
+ default: false
+ }
+ },
+ computed: {
+ todayText() {
+ return t("uv-calender.today")
+ },
+ itemBoxStyle() {
+ const style = {};
+ if (this.multiple) { // 多选状态
+ if (this.weeks.multiple) {
+ style.backgroundColor = this.color;
+ style.color = '#fff';
+ } else if (this.weeks.isDay) {
+ style.color = this.color;
+ }
+ } else if (this.range) { // 范围选择
+ if (this.weeks.beforeRange || this.weeks.afterRange) {
+ style.backgroundColor = this.color;
+ } else if (this.weeks.range) {
+ style.backgroundColor = colorGradient(this.color, '#ffffff', 100)[90]
+ style.color = this.color;
+ style.opacity = 0.8;
+ style.borderRadius = 0;
+ }
+ } else {
+ if (this.weeks.isDay) {
+ style.color = this.color;
+ }
+ if (this.calendar.fullDate === this.weeks.fullDate) {
+ style.backgroundColor = this.color;
+ style.color = '#fff';
+ }
+ }
+ return style;
+ },
+ infoStyle(val) {
+ return val => {
+ const style = {};
+ if (!this.weeks.multiple) {
+ if (val == 'top') {
+ style.color = this.weeks.extraInfo.topinfoColor ? this.weeks.extraInfo.topinfoColor : '#606266';
+ } else if (val == 'bottom') {
+ style.color = this.weeks.extraInfo.infoColor ? this.weeks.extraInfo.infoColor : '#f56c6c';
+ }
+ if (this.weeks.range) {
+ style.color = this.color;
+ }
+ if (this.calendar.fullDate === this.weeks.fullDate || this.weeks.beforeRange || this.weeks.afterRange) {
+ style.color = this.multiple ? style.color : '#fff';
+ }
+ } else {
+ style.color = '#fff';
+ }
+ return style;
+ }
+ }
+ },
+ methods: {
+ choiceDate(weeks) {
+ if (this.weeks.extraInfo && this.weeks.extraInfo.disable) return;
+ this.$emit('change', weeks)
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @mixin flex($direction: row) {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ flex-direction: $direction;
+ }
+ $uv-font-size-base: 14px;
+ $uv-text-color: #333;
+ $uv-font-size-sm: 24rpx;
+ $uv-error: #f56c6c !default;
+ $uv-opacity-disabled: 0.3;
+ $uv-text-color-disable: #c0c0c0;
+ $uv-primary: #3c9cff !default;
+ $info-height: 32rpx;
+ .uv-calendar-item__weeks-box {
+ flex: 1;
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+ border-radius: 4px;
+ }
+ .uv-calendar-item__weeks-top-text {
+ height: $info-height;
+ line-height: $info-height;
+ font-size: $uv-font-size-sm;
+ }
+ .uv-calendar-item__weeks-box-text {
+ font-size: $uv-font-size-base;
+ color: $uv-text-color;
+ }
+ .uv-calendar-item__weeks-lunar-text {
+ height: $info-height;
+ line-height: $info-height;
+ font-size: $uv-font-size-sm;
+ color: $uv-text-color;
+ }
+ .uv-calendar-item__weeks-lunar-text--equal {
+ /* #ifdef H5 */
+ white-space: nowrap;
+ transform: scale(.8);
+ /* #endif */
+ /* #ifndef H5 */
+ font-size: 20rpx;
+ /* #endif */
+ }
+ .uv-calendar-item__weeks-box-item {
+ position: relative;
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+ width: 106rpx;
+ height: 56px;
+ }
+ .uv-calendar-item__weeks-box-circle {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ width: 8px;
+ height: 8px;
+ border-radius: 8px;
+ background-color: $uv-error;
+ }
+ .uv-calendar-item--disable {
+ background-color: rgba(249, 249, 249, $uv-opacity-disabled);
+ color: $uv-text-color-disable;
+ }
+ .uv-calendar-item--isDay-text {
+ color: $uv-primary;
+ }
+ .uv-calendar-item--isDay {
+ background-color: $uv-primary;
+ color: #fff;
+ }
+ .uv-calendar-item--checked {
+ background-color: $uv-primary;
+ color: #fff;
+ border-radius: 4px;
+ }
+ // .uv-calendar-item--range {
+ // background-color: $uv-primary;
+ // color: #fff;
+ // }
+ .uv-calendar-item--before-checked {
+ color: #fff;
+ }
+ .uv-calendar-item--after-checked {
+ color: #fff;
+ }
+ .uv-calendar-item--multiple {
+ background-color: $uv-primary;
+ color: #fff;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-calendars/components/uv-calendars/calendar.js b/uni_modules/uv-calendars/components/uv-calendars/calendar.js
new file mode 100644
index 0000000..b8d7d6f
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/calendar.js
@@ -0,0 +1,546 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github https://github.com/jjonline/calendar.js
+* @Author Jea杨(JJonline@JJonline.Cn)
+* @Time 2014-7-21
+* @Time 2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+ /**
+ * 农历1900-2100的润大小信息表
+ * @Array Of Property
+ * @return Hex
+ */
+ lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+ 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+ 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+ 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+ 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+ 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+ 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+ 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+ 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+ 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+ 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+ 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+ 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+ 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+ 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+ /** Add By JJonline@JJonline.Cn**/
+ 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+ 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+ 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+ 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+ 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+ 0x0d520], // 2100
+
+ /**
+ * 公历每个月份的天数普通表
+ * @Array Of Property
+ * @return Number
+ */
+ solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+ /**
+ * 天干地支之天干速查表
+ * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+ * @return Cn string
+ */
+ Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+ /**
+ * 天干地支之地支速查表
+ * @Array Of Property
+ * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+ * @return Cn string
+ */
+ Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+ /**
+ * 天干地支之地支速查表<=>生肖
+ * @Array Of Property
+ * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+ * @return Cn string
+ */
+ Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+ /**
+ * 24节气速查表
+ * @Array Of Property
+ * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+ * @return Cn string
+ */
+ solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+ /**
+ * 1900-2100各年的24节气日期速查表
+ * @Array Of Property
+ * @return 0x string For splice
+ */
+ sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+ '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+ 'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+ '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+ '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+ '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+ '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+ '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+ '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+ '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+ '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+ '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+ '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+ '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+ '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+ '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+ '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+ '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+ '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+ '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+ '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+ '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+ '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+ '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+ '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+ '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+ '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+ '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+ '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+ '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+ '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+ '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+ /**
+ * 数字转中文速查表
+ * @Array Of Property
+ * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+ * @return Cn string
+ */
+ nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+ /**
+ * 日期转农历称呼速查表
+ * @Array Of Property
+ * @trans ['初','十','廿','卅']
+ * @return Cn string
+ */
+ nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+ /**
+ * 月份转农历称呼速查表
+ * @Array Of Property
+ * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+ * @return Cn string
+ */
+ nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+ /**
+ * 返回农历y年一整年的总天数
+ * @param lunar Year
+ * @return Number
+ * @eg:var count = calendar.lYearDays(1987) ;//count=387
+ */
+ lYearDays: function (y) {
+ var i; var sum = 348
+ for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+ return (sum + this.leapDays(y))
+ },
+
+ /**
+ * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+ * @param lunar Year
+ * @return Number (0-12)
+ * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+ */
+ leapMonth: function (y) { // 闰字编码 \u95f0
+ return (this.lunarInfo[y - 1900] & 0xf)
+ },
+
+ /**
+ * 返回农历y年闰月的天数 若该年没有闰月则返回0
+ * @param lunar Year
+ * @return Number (0、29、30)
+ * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+ */
+ leapDays: function (y) {
+ if (this.leapMonth(y)) {
+ return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+ }
+ return (0)
+ },
+
+ /**
+ * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+ * @param lunar Year
+ * @return Number (-1、29、30)
+ * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+ */
+ monthDays: function (y, m) {
+ if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+ return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+ },
+
+ /**
+ * 返回公历(!)y年m月的天数
+ * @param solar Year
+ * @return Number (-1、28、29、30、31)
+ * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+ */
+ solarDays: function (y, m) {
+ if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+ var ms = m - 1
+ if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+ return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+ } else {
+ return (this.solarMonth[ms])
+ }
+ },
+
+ /**
+ * 农历年份转换为干支纪年
+ * @param lYear 农历年的年份数
+ * @return Cn string
+ */
+ toGanZhiYear: function (lYear) {
+ var ganKey = (lYear - 3) % 10
+ var zhiKey = (lYear - 3) % 12
+ if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+ if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+ return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+ },
+
+ /**
+ * 公历月、日判断所属星座
+ * @param cMonth [description]
+ * @param cDay [description]
+ * @return Cn string
+ */
+ toAstro: function (cMonth, cDay) {
+ var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+ var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+ return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+ },
+
+ /**
+ * 传入offset偏移量返回干支
+ * @param offset 相对甲子的偏移量
+ * @return Cn string
+ */
+ toGanZhi: function (offset) {
+ return this.Gan[offset % 10] + this.Zhi[offset % 12]
+ },
+
+ /**
+ * 传入公历(!)y年获得该年第n个节气的公历日期
+ * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+ * @return day Number
+ * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+ */
+ getTerm: function (y, n) {
+ if (y < 1900 || y > 2100) { return -1 }
+ if (n < 1 || n > 24) { return -1 }
+ var _table = this.sTermInfo[y - 1900]
+ var _info = [
+ parseInt('0x' + _table.substr(0, 5)).toString(),
+ parseInt('0x' + _table.substr(5, 5)).toString(),
+ parseInt('0x' + _table.substr(10, 5)).toString(),
+ parseInt('0x' + _table.substr(15, 5)).toString(),
+ parseInt('0x' + _table.substr(20, 5)).toString(),
+ parseInt('0x' + _table.substr(25, 5)).toString()
+ ]
+ var _calday = [
+ _info[0].substr(0, 1),
+ _info[0].substr(1, 2),
+ _info[0].substr(3, 1),
+ _info[0].substr(4, 2),
+
+ _info[1].substr(0, 1),
+ _info[1].substr(1, 2),
+ _info[1].substr(3, 1),
+ _info[1].substr(4, 2),
+
+ _info[2].substr(0, 1),
+ _info[2].substr(1, 2),
+ _info[2].substr(3, 1),
+ _info[2].substr(4, 2),
+
+ _info[3].substr(0, 1),
+ _info[3].substr(1, 2),
+ _info[3].substr(3, 1),
+ _info[3].substr(4, 2),
+
+ _info[4].substr(0, 1),
+ _info[4].substr(1, 2),
+ _info[4].substr(3, 1),
+ _info[4].substr(4, 2),
+
+ _info[5].substr(0, 1),
+ _info[5].substr(1, 2),
+ _info[5].substr(3, 1),
+ _info[5].substr(4, 2)
+ ]
+ return parseInt(_calday[n - 1])
+ },
+
+ /**
+ * 传入农历数字月份返回汉语通俗表示法
+ * @param lunar month
+ * @return Cn string
+ * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+ */
+ toChinaMonth: function (m) { // 月 => \u6708
+ if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+ var s = this.nStr3[m - 1]
+ s += '\u6708'// 加上月字
+ return s
+ },
+
+ /**
+ * 传入农历日期数字返回汉字表示法
+ * @param lunar day
+ * @return Cn string
+ * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+ */
+ toChinaDay: function (d) { // 日 => \u65e5
+ var s
+ switch (d) {
+ case 10:
+ s = '\u521d\u5341'; break
+ case 20:
+ s = '\u4e8c\u5341'; break
+ break
+ case 30:
+ s = '\u4e09\u5341'; break
+ break
+ default :
+ s = this.nStr2[Math.floor(d / 10)]
+ s += this.nStr1[d % 10]
+ }
+ return (s)
+ },
+
+ /**
+ * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+ * @param y year
+ * @return Cn string
+ * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+ */
+ getAnimal: function (y) {
+ return this.Animals[(y - 4) % 12]
+ },
+
+ /**
+ * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+ * @param y solar year
+ * @param m solar month
+ * @param d solar day
+ * @return JSON object
+ * @eg:console.log(calendar.solar2lunar(1987,11,01));
+ */
+ solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+ // 年份限定、上限
+ if (y < 1900 || y > 2100) {
+ return -1// undefined转换为数字变为NaN
+ }
+ // 公历传参最下限
+ if (y == 1900 && m == 1 && d < 31) {
+ return -1
+ }
+ // 未传参 获得当天
+ if (!y) {
+ var objDate = new Date()
+ } else {
+ var objDate = new Date(y, parseInt(m) - 1, d)
+ }
+ var i; var leap = 0; var temp = 0
+ // 修正ymd参数
+ var y = objDate.getFullYear()
+ var m = objDate.getMonth() + 1
+ var d = objDate.getDate()
+ var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+ for (i = 1900; i < 2101 && offset > 0; i++) {
+ temp = this.lYearDays(i)
+ offset -= temp
+ }
+ if (offset < 0) {
+ offset += temp; i--
+ }
+
+ // 是否今天
+ var isTodayObj = new Date()
+ var isToday = false
+ if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+ isToday = true
+ }
+ // 星期几
+ var nWeek = objDate.getDay()
+ var cWeek = this.nStr1[nWeek]
+ // 数字表示周几顺应天朝周一开始的惯例
+ if (nWeek == 0) {
+ nWeek = 7
+ }
+ // 农历年
+ var year = i
+ var leap = this.leapMonth(i) // 闰哪个月
+ var isLeap = false
+
+ // 效验闰月
+ for (i = 1; i < 13 && offset > 0; i++) {
+ // 闰月
+ if (leap > 0 && i == (leap + 1) && isLeap == false) {
+ --i
+ isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+ } else {
+ temp = this.monthDays(year, i)// 计算农历普通月天数
+ }
+ // 解除闰月
+ if (isLeap == true && i == (leap + 1)) { isLeap = false }
+ offset -= temp
+ }
+ // 闰月导致数组下标重叠取反
+ if (offset == 0 && leap > 0 && i == leap + 1) {
+ if (isLeap) {
+ isLeap = false
+ } else {
+ isLeap = true; --i
+ }
+ }
+ if (offset < 0) {
+ offset += temp; --i
+ }
+ // 农历月
+ var month = i
+ // 农历日
+ var day = offset + 1
+ // 天干地支处理
+ var sm = m - 1
+ var gzY = this.toGanZhiYear(year)
+
+ // 当月的两个节气
+ // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+ var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+ var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+ // 依据12节气修正干支月
+ var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+ if (d >= firstNode) {
+ gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+ }
+
+ // 传入的日期的节气与否
+ var isTerm = false
+ var Term = null
+ if (firstNode == d) {
+ isTerm = true
+ Term = this.solarTerm[m * 2 - 2]
+ }
+ if (secondNode == d) {
+ isTerm = true
+ Term = this.solarTerm[m * 2 - 1]
+ }
+ // 日柱 当月一日与 1900/1/1 相差天数
+ var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+ var gzD = this.toGanZhi(dayCyclical + d - 1)
+ // 该日期所属的星座
+ var astro = this.toAstro(m, d)
+
+ return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+ },
+
+ /**
+ * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+ * @param y lunar year
+ * @param m lunar month
+ * @param d lunar day
+ * @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+ * @return JSON object
+ * @eg:console.log(calendar.lunar2solar(1987,9,10));
+ */
+ lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+ var isLeapMonth = !!isLeapMonth
+ var leapOffset = 0
+ var leapMonth = this.leapMonth(y)
+ var leapDay = this.leapDays(y)
+ if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+ if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+ var day = this.monthDays(y, m)
+ var _day = day
+ // bugFix 2016-9-25
+ // if month is leap, _day use leapDays method
+ if (isLeapMonth) {
+ _day = this.leapDays(y, m)
+ }
+ if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+ // 计算农历的时间差
+ var offset = 0
+ for (var i = 1900; i < y; i++) {
+ offset += this.lYearDays(i)
+ }
+ var leap = 0; var isAdd = false
+ for (var i = 1; i < m; i++) {
+ leap = this.leapMonth(y)
+ if (!isAdd) { // 处理闰月
+ if (leap <= i && leap > 0) {
+ offset += this.leapDays(y); isAdd = true
+ }
+ }
+ offset += this.monthDays(y, i)
+ }
+ // 转换闰月农历 需补充该年闰月的前一个月的时差
+ if (isLeapMonth) { offset += day }
+ // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+ var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+ var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+ var cY = calObj.getUTCFullYear()
+ var cM = calObj.getUTCMonth() + 1
+ var cD = calObj.getUTCDate()
+
+ return this.solar2lunar(cY, cM, cD)
+ }
+}
+
+export default calendar
diff --git a/uni_modules/uv-calendars/components/uv-calendars/i18n/en.json b/uni_modules/uv-calendars/components/uv-calendars/i18n/en.json
new file mode 100644
index 0000000..c83e8b2
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/i18n/en.json
@@ -0,0 +1,12 @@
+{
+ "uv-calender.ok": "ok",
+ "uv-calender.cancel": "cancel",
+ "uv-calender.today": "today",
+ "uv-calender.MON": "MON",
+ "uv-calender.TUE": "TUE",
+ "uv-calender.WED": "WED",
+ "uv-calender.THU": "THU",
+ "uv-calender.FRI": "FRI",
+ "uv-calender.SAT": "SAT",
+ "uv-calender.SUN": "SUN"
+}
diff --git a/uni_modules/uv-calendars/components/uv-calendars/i18n/index.js b/uni_modules/uv-calendars/components/uv-calendars/i18n/index.js
new file mode 100644
index 0000000..de7509c
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/i18n/index.js
@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+ en,
+ 'zh-Hans': zhHans,
+ 'zh-Hant': zhHant
+}
diff --git a/uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hans.json b/uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hans.json
new file mode 100644
index 0000000..03c5486
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hans.json
@@ -0,0 +1,12 @@
+{
+ "uv-calender.ok": "确定",
+ "uv-calender.cancel": "取消",
+ "uv-calender.today": "今日",
+ "uv-calender.SUN": "日",
+ "uv-calender.MON": "一",
+ "uv-calender.TUE": "二",
+ "uv-calender.WED": "三",
+ "uv-calender.THU": "四",
+ "uv-calender.FRI": "五",
+ "uv-calender.SAT": "六"
+}
diff --git a/uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hant.json b/uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hant.json
new file mode 100644
index 0000000..95cf29e
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hant.json
@@ -0,0 +1,12 @@
+{
+ "uv-calender.ok": "確定",
+ "uv-calender.cancel": "取消",
+ "uv-calender.today": "今日",
+ "uv-calender.SUN": "日",
+ "uv-calender.MON": "一",
+ "uv-calender.TUE": "二",
+ "uv-calender.WED": "三",
+ "uv-calender.THU": "四",
+ "uv-calender.FRI": "五",
+ "uv-calender.SAT": "六"
+}
diff --git a/uni_modules/uv-calendars/components/uv-calendars/util.js b/uni_modules/uv-calendars/components/uv-calendars/util.js
new file mode 100644
index 0000000..ae1c6d6
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/util.js
@@ -0,0 +1,435 @@
+import CALENDAR from './calendar.js'
+class Calendar {
+ constructor({
+ date,
+ selected,
+ startDate,
+ endDate,
+ range,
+ multiple,
+ allowSameDay
+ } = {}) {
+ // 当前日期
+ this.date = this.getDate(new Date()) // 当前初入日期
+ // 打点信息
+ this.selected = selected || [];
+ // 范围开始
+ this.startDate = startDate
+ // 范围结束
+ this.endDate = endDate
+ this.range = range
+ this.multiple = multiple
+ this.allowSameDay = allowSameDay
+ // 多选状态
+ this.cleanRangeStatus()
+ // 范围状态
+ this.cleanMultipleStatus()
+ // 每周日期
+ this.weeks = {}
+ // this._getWeek(this.date.fullDate)
+ }
+ /**
+ * 设置日期
+ * @param {Object} date
+ */
+ setDate(date, status) {
+ if (this.range && status == 'init') {
+ this.cleanRangeStatus();
+ if (Array.isArray(date)) {
+ this.rangeStatus.before = date[0];
+ this.rangeStatus.after = date.length > 1 ? date[date.length - 1] : '';
+ if (this.rangeStatus.after && this.dateCompare(this.rangeStatus.before, this.rangeStatus.after)) {
+ this.rangeStatus.data = this.geDateAll(this.rangeStatus.before, this.rangeStatus.after)
+ }
+ this.selectDate = this.getDate(date[0])
+ this._getWeek(this.selectDate.fullDate)
+ } else {
+ this.selectDate = this.getDate(date)
+ this.rangeStatus.before = this.selectDate.fullDate;
+ this._getWeek(this.selectDate.fullDate)
+ }
+ } else if (this.multiple && status == 'init') {
+ this.cleanMultipleStatus();
+ if (Array.isArray(date)) {
+ this.multipleStatus.data = date;
+ this.selectDate = this.getDate(date[0])
+ this._getWeek(this.selectDate.fullDate)
+ } else {
+ this.selectDate = this.getDate(date)
+ this.multipleStatus.data = [this.selectDate.fullDate];
+ this._getWeek(this.selectDate.fullDate)
+ }
+ } else {
+ if (Array.isArray(date)) {
+ this.selectDate = this.getDate(date[0])
+ this._getWeek(this.selectDate.fullDate)
+ } else {
+ this.selectDate = this.getDate(date)
+ this._getWeek(this.selectDate.fullDate)
+ }
+ }
+ }
+ /**
+ * 清理多选状态
+ */
+ cleanRangeStatus() {
+ this.rangeStatus = {
+ before: '',
+ after: '',
+ data: []
+ }
+ }
+ /**
+ * 清理多选状态
+ */
+ cleanMultipleStatus() {
+ this.multipleStatus = {
+ data: []
+ }
+ }
+ /**
+ * 重置开始日期
+ */
+ resetSatrtDate(startDate) {
+ // 范围开始
+ this.startDate = startDate
+ }
+ /**
+ * 重置结束日期
+ */
+ resetEndDate(endDate) {
+ // 范围结束
+ this.endDate = endDate
+ }
+ /**
+ * 获取任意时间
+ */
+ getDate(date, AddDayCount = 0, str = 'day') {
+ if (!date) {
+ date = new Date()
+ }
+ if (typeof date !== 'object') {
+ date = date.replace(/-/g, '/')
+ }
+ const dd = new Date(date)
+ switch (str) {
+ case 'day':
+ dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+ break
+ case 'month':
+ if (dd.getDate() === 31 && AddDayCount > 0) {
+ dd.setDate(dd.getDate() + AddDayCount)
+ } else {
+ const preMonth = dd.getMonth()
+ dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
+ const nextMonth = dd.getMonth()
+ // 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
+ if (AddDayCount < 0 && preMonth !== 0 && nextMonth - preMonth > AddDayCount) {
+ dd.setMonth(nextMonth + (nextMonth - preMonth + AddDayCount))
+ }
+ // 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
+ if (AddDayCount > 0 && nextMonth - preMonth > AddDayCount) {
+ dd.setMonth(nextMonth - (nextMonth - preMonth - AddDayCount))
+ }
+ }
+ break
+ case 'year':
+ dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+ break
+ }
+ const y = dd.getFullYear()
+ const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+ const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+ return {
+ fullDate: y + '-' + m + '-' + d,
+ year: y,
+ month: m,
+ date: d,
+ day: dd.getDay()
+ }
+ }
+ /**
+ * 获取上月剩余天数
+ */
+ _getLastMonthDays(firstDay, full) {
+ let dateArr = []
+ for (let i = firstDay; i > 0; i--) {
+ const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+ dateArr.push({
+ date: beforeDate,
+ month: full.month - 1,
+ lunar: this.getlunar(full.year, full.month - 1, beforeDate),
+ disable: true
+ })
+ }
+ return dateArr
+ }
+ /**
+ * 获取本月天数
+ */
+ _currentMonthDys(dateData, full) {
+ let dateArr = []
+ let fullDate = this.date.fullDate
+ for (let i = 1; i <= dateData; i++) {
+ let nowDate = full.year + '-' + (full.month < 10 ? full.month : full.month) + '-' + (i < 10 ? '0' + i : i)
+ // 是否今天
+ let isDay = fullDate === nowDate
+ // 获取打点信息
+ let info = this.selected && this.selected.find((item) => {
+ if (this.dateEqual(nowDate, item.date)) {
+ return item
+ }
+ })
+ // 日期禁用
+ let disableBefore = true
+ let disableAfter = true
+ if (this.startDate) {
+ // let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+ // disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+ disableBefore = this.dateCompare(this.startDate, nowDate)
+ }
+ if (this.endDate) {
+ // let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+ // disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+ disableAfter = this.dateCompare(nowDate, this.endDate)
+ }
+ let ranges = this.rangeStatus.data
+ let checked = false
+ let rangesStatus = -1
+ if (this.range) {
+ if (ranges) {
+ rangesStatus = ranges.findIndex((item) => {
+ return this.dateEqual(item, nowDate)
+ })
+ }
+ if (rangesStatus !== -1) {
+ checked = true
+ }
+ }
+ let multiples = this.multipleStatus.data
+ let checked_multiple = false
+ let multiplesStatus = -1
+ if (this.multiple) {
+ if (multiples) {
+ multiplesStatus = multiples.findIndex((item) => {
+ return this.dateEqual(item, nowDate)
+ })
+ }
+ if (multiplesStatus !== -1) {
+ checked_multiple = true
+ }
+ }
+ let data = {
+ fullDate: nowDate,
+ year: full.year,
+ date: i,
+ range: this.range ? checked : false,
+ multiple: this.multiple ? checked_multiple : false,
+ beforeRange: this.dateEqual(this.rangeStatus.before, nowDate),
+ afterRange: this.dateEqual(this.rangeStatus.after, nowDate),
+ dateEqual: this.range && checked && this.dateEqual(this.rangeStatus.before, this.rangeStatus.after),
+ month: full.month,
+ lunar: this.getlunar(full.year, full.month, i),
+ disable: !(disableBefore && disableAfter),
+ isDay
+ }
+ if (info) {
+ data.extraInfo = info
+ }
+ dateArr.push(data)
+ }
+ return dateArr
+ }
+ /**
+ * 获取下月天数
+ */
+ _getNextMonthDays(surplus, full) {
+ let dateArr = []
+ for (let i = 1; i < surplus + 1; i++) {
+ dateArr.push({
+ date: i,
+ month: Number(full.month) + 1,
+ lunar: this.getlunar(full.year, Number(full.month) + 1, i),
+ disable: true
+ })
+ }
+ return dateArr
+ }
+ /**
+ * 获取当前日期详情
+ * @param {Object} date
+ */
+ getInfo(date) {
+ if (!date) {
+ date = new Date()
+ } else if (Array.isArray(date)) {
+ date = date[0]
+ }
+ const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+ return dateInfo
+ }
+ /**
+ * 比较时间大小
+ */
+ dateCompare(startDate, endDate) {
+ // 计算截止时间
+ startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+ // 计算详细项的截止时间
+ endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+ if (startDate <= endDate) {
+ return true
+ } else {
+ return false
+ }
+ }
+ /**
+ * 比较时间是否相等
+ */
+ dateEqual(before, after) {
+ // 计算截止时间
+ before = new Date(before.replace('-', '/').replace('-', '/'))
+ // 计算详细项的截止时间
+ after = new Date(after.replace('-', '/').replace('-', '/'))
+ if (before.getTime() - after.getTime() === 0) {
+ return true
+ } else {
+ return false
+ }
+ }
+ /**
+ * 比较after时间是否大于before时间
+ */
+ dateAfterLgBefore(before, after) {
+ // 计算截止时间
+ before = new Date(before.replace('-', '/').replace('-', '/'))
+ // 计算详细项的截止时间
+ after = new Date(after.replace('-', '/').replace('-', '/'))
+ if (after.getTime() - before.getTime() > 0) {
+ return true
+ } else {
+ return false
+ }
+ }
+ /**
+ * 获取日期范围内所有日期
+ * @param {Object} begin
+ * @param {Object} end
+ */
+ geDateAll(begin, end) {
+ var arr = []
+ var ab = begin.split('-')
+ var ae = end.split('-')
+ var db = new Date()
+ db.setFullYear(ab[0], ab[1] - 1, ab[2])
+ var de = new Date()
+ de.setFullYear(ae[0], ae[1] - 1, ae[2])
+ var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+ var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+ for (var k = unixDb; k <= unixDe;) {
+ k = k + 24 * 60 * 60 * 1000
+ arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+ }
+ return arr
+ }
+ /**
+ * 计算阴历日期显示
+ */
+ getlunar(year, month, date) {
+ return CALENDAR.solar2lunar(year, month, date)
+ }
+ /**
+ * 设置打点
+ */
+ setSelectInfo(data, value) {
+ this.selected = value
+ this._getWeek(data)
+ }
+ /**
+ * 获取多选状态
+ */
+ setMultiple(fullDate) {
+ if (!this.multiple) return
+ let multiples = this.multipleStatus.data;
+ const findIndex = multiples.findIndex(item => this.dateEqual(fullDate, item));
+ if (findIndex < 0) {
+ this.multipleStatus.data = this.multipleStatus.data.concat([fullDate]);
+ } else {
+ this.multipleStatus.data.splice(findIndex, 1);
+ }
+ this._getWeek(fullDate)
+ }
+ /**
+ * 获取范围状态
+ */
+ setRange(fullDate) {
+ let {
+ before,
+ after
+ } = this.rangeStatus
+ if (!this.range) return
+ if (before && after) {
+ this.cleanRangeStatus();
+ this.rangeStatus.before = fullDate
+ } else {
+ if (!before) {
+ this.rangeStatus.before = fullDate
+ } else {
+ if (this.allowSameDay && this.dateEqual(before, fullDate)) {
+ this.rangeStatus.after = fullDate
+ } else if (!this.dateAfterLgBefore(this.rangeStatus.before, fullDate)) {
+ this.cleanRangeStatus();
+ this.rangeStatus.before = fullDate
+ this._getWeek(fullDate)
+ return;
+ }
+ this.rangeStatus.after = fullDate
+ if (this.dateCompare(this.rangeStatus.before, this.rangeStatus.after)) {
+ this.rangeStatus.data = this.geDateAll(this.rangeStatus.before, this.rangeStatus.after);
+ } else {
+ this.rangeStatus.data = this.geDateAll(this.rangeStatus.after, this.rangeStatus.before);
+ }
+ }
+ }
+ this._getWeek(fullDate)
+ }
+ /**
+ * 获取每周数据
+ * @param {Object} dateData
+ */
+ _getWeek(dateData) {
+ const {
+ year,
+ month
+ } = this.getDate(dateData)
+ let firstDay = new Date(year, month - 1, 1).getDay()
+ let currentDay = new Date(year, month, 0).getDate()
+ let dates = {
+ lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+ currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+ nextMonthDays: [], // 下个月开始几天
+ weeks: []
+ }
+ let canlender = []
+ const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+ dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+ canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+ let weeks = {}
+ // 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
+ for (let i = 0; i < canlender.length; i++) {
+ if (i % 7 === 0) {
+ weeks[parseInt(i / 7)] = new Array(7)
+ }
+ weeks[parseInt(i / 7)][i % 7] = canlender[i]
+ }
+ this.canlender = canlender
+ this.weeks = weeks
+ }
+ //静态方法
+ // static init(date) {
+ // if (!this.instance) {
+ // this.instance = new Calendar(date);
+ // }
+ // return this.instance;
+ // }
+}
+export default Calendar
\ No newline at end of file
diff --git a/uni_modules/uv-calendars/components/uv-calendars/uv-calendars.vue b/uni_modules/uv-calendars/components/uv-calendars/uv-calendars.vue
new file mode 100644
index 0000000..8d5b377
--- /dev/null
+++ b/uni_modules/uv-calendars/components/uv-calendars/uv-calendars.vue
@@ -0,0 +1,452 @@
+<template>
+ <view class="uv-calendar">
+ <view class="uv-calendar__content" v-if="insert">
+ <calendar-body
+ :date="date"
+ :nowDate="nowDate"
+ :weeks="weeks"
+ :calendar="calendar"
+ :selected="selected"
+ :lunar="lunar"
+ :showMonth="showMonth"
+ :color="color"
+ :startText="startText"
+ :endText="endText"
+ :range="range"
+ :multiple="multiple"
+ :allowSameDay="allowSameDay"
+ @bindDateChange="bindDateChange"
+ @pre="pre"
+ @next="next"
+ @backToday="backToday"
+ @choiceDate="choiceDate"
+ ></calendar-body>
+ </view>
+ <uv-popup ref="popup" mode="bottom" v-else :round="round" z-index="998" :close-on-click-overlay="closeOnClickOverlay" @maskClick="maskClick">
+ <view style="min-height: 100px;">
+ <uv-toolbar
+ :show="true"
+ :cancelColor="cancelColor"
+ :confirmColor="getConfirmColor"
+ :cancelText="cancelText"
+ :confirmText="confirmText"
+ :title="title"
+ @cancel="close"
+ @confirm="confirm"></uv-toolbar>
+ <view class="line"></view>
+ <calendar-body
+ :nowDate="nowDate"
+ :weeks="weeks"
+ :calendar="calendar"
+ :selected="selected"
+ :lunar="lunar"
+ :showMonth="showMonth"
+ :color="color"
+ :startText="startText"
+ :endText="endText"
+ :range="range"
+ :multiple="multiple"
+ :allowSameDay="allowSameDay"
+ @bindDateChange="bindDateChange"
+ @pre="pre"
+ @next="next"
+ @backToday="backToday"
+ @choiceDate="choiceDate"
+ ></calendar-body>
+ </view>
+ </uv-popup>
+ </view>
+</template>
+<script>
+ /**
+ * Calendar 日历
+ * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
+ * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-calendar
+ * @property {String} date 自定义当前时间,默认为今天
+ * @property {Boolean} lunar 显示农历
+ * @property {String} startDate 日期选择范围-开始日期
+ * @property {String} endDate 日期选择范围-结束日期
+ * @property {String} mode = [不传 | multiple | range ] 多个日期 | 选择日期范围 默认单日期
+ * @property {Boolean} insert = [true|false] 插入模式,默认为false
+ * @value true 弹窗模式
+ * @value false 插入模式
+ * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
+ * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
+ * @property {String} cancelColor 取消按钮颜色
+ * @property {String} confirmColor 确认按钮颜色,默认#3c9cff
+ * @property {String} title 头部工具条中间的标题文字
+ * @property {String} color 主题色,默认#3c9cff
+ * @property {Number} round :insert="false"时的圆角
+ * @property {Boolean} closeOnClickOverlay 点击遮罩是否关闭
+ * @property {String} startText range为true时,第一个日期底部的提示文字
+ * @property {String} endText range为true时,最后一个日期底部的提示文字
+ * @property {String} readonly 是否为只读状态,只读状态下禁止选择日期,默认false
+ *
+ * @event {Function} change 日期改变,`insert :ture` 时生效
+ * @event {Function} confirm 确认选择`insert :false` 时生效
+ * @event {Function} monthSwitch 切换月份时触发
+ *
+ * @example <uv-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+ */
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+ import Calendar from './util.js';
+ import calendarBody from './calendar-body.vue';
+ import { initVueI18n } from '@dcloudio/uni-i18n';
+ import i18nMessages from './i18n/index.js';
+ const { t } = initVueI18n(i18nMessages);
+ export default {
+ components: {
+ calendarBody
+ },
+ mixins: [mpMixin, mixin],
+ emits: ['close', 'confirm', 'change', 'monthSwitch'],
+ props: {
+ // 取消按钮颜色
+ cancelColor: {
+ type: String,
+ default: ''
+ },
+ // 确认按钮颜色,range模式下未选全显示灰色
+ confirmColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 标题
+ title: {
+ type: String,
+ default: ''
+ },
+ // 主题色
+ color: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 默认显示日期
+ date: {
+ type: [String,Array],
+ default: ''
+ },
+ // 打点等设置
+ selected: {
+ type: Array,
+ default () {
+ return []
+ }
+ },
+ // 是否显示农历
+ lunar: {
+ type: Boolean,
+ default: false
+ },
+ // 可选择的起始日期
+ startDate: {
+ type: String,
+ default: ''
+ },
+ // 可选择的结束日期
+ endDate: {
+ type: String,
+ default: ''
+ },
+ // multiple - 选择多日期 range - 选择日期范围
+ mode: {
+ type: String,
+ default: ''
+ },
+ // 是否插入模式
+ insert: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示月份为背景
+ showMonth: {
+ type: Boolean,
+ default: true
+ },
+ // 弹窗模式是否清空上次选择内容
+ clearDate: {
+ type: Boolean,
+ default: true
+ },
+ // 弹窗圆角
+ round: {
+ type: [Number,String],
+ default: 8
+ },
+ // 点击遮罩是否关闭弹窗
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: true
+ },
+ // range为true时,第一个日期底部的提示文字
+ startText: {
+ type: String,
+ default: '开始'
+ },
+ // range为true时,最后一个日期底部的提示文字
+ endText: {
+ type: String,
+ default: '结束'
+ },
+ // 是否允许日期范围的起止时间为同一天,mode = range时有效
+ allowSameDay: {
+ type: Boolean,
+ default: false
+ },
+ // 是否禁用
+ readonly: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.calendars
+ },
+ data(){
+ return {
+ weeks: [],
+ calendar: {},
+ nowDate: '',
+ allowConfirm: false,
+ multiple: false,
+ range: false
+ }
+ },
+ computed:{
+ /**
+ * for i18n
+ */
+ confirmText() {
+ return t("uv-calender.ok")
+ },
+ cancelText() {
+ return t("uv-calender.cancel")
+ },
+ getConfirmColor() {
+ if(this.range || this.multiple || this.readonly) {
+ return this.allowConfirm? this.confirmColor: '#999'
+ }else {
+ return this.confirmColor;
+ }
+ }
+ },
+ watch: {
+ date(newVal) {
+ this.init(newVal)
+ },
+ startDate(val) {
+ this.cale.resetSatrtDate(val)
+ this.cale.setDate(this.nowDate.fullDate)
+ this.weeks = this.cale.weeks
+ },
+ endDate(val) {
+ this.cale.resetEndDate(val)
+ this.cale.setDate(this.nowDate.fullDate)
+ this.weeks = this.cale.weeks
+ },
+ selected(newVal) {
+ this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+ this.weeks = this.cale.weeks
+ }
+ },
+ created() {
+ this.setMode();
+ this.cale = new Calendar({
+ selected: this.selected,
+ startDate: this.startDate,
+ endDate: this.endDate,
+ range: this.range,
+ multiple: this.multiple,
+ allowSameDay: this.allowSameDay
+ })
+ this.init(this.date)
+ },
+ methods: {
+ setMode() {
+ switch (this.mode){
+ case 'range':
+ this.range = true;
+ break;
+ case 'multiple':
+ this.multiple = true;
+ default:
+ break;
+ }
+ },
+ async open() {
+ if (this.clearDate && !this.insert) {
+ this.cale.cleanRangeStatus()
+ this.init(this.date)
+ }
+ if(!this.insert){
+ this.$refs.popup.open();
+ }
+ },
+ close() {
+ this.$refs.popup.close();
+ this.$emit('close');
+ },
+ confirm() {
+ if(this.readonly) {
+ return;
+ } else if(this.range && !this.cale.rangeStatus.after) {
+ return;
+ } else if(this.multiple && this.cale.multipleStatus.data.length == 0){
+ return;
+ }
+ this.setEmit('confirm');
+ this.close()
+ },
+ maskClick() {
+ if(this.closeOnClickOverlay) {
+ this.$emit('close');
+ }
+ },
+ bindDateChange(e) {
+ const value = e.detail.value + '-1'
+ this.setDate(value)
+
+ const { year, month } = this.cale.getDate(value)
+ this.$emit('monthSwitch', {
+ year,
+ month
+ })
+ },
+ /**
+ * 初始化日期显示
+ * @param {Object} date
+ */
+ init(date) {
+ if(this.range) {
+ // 重置范围选择状态
+ this.cale.cleanRangeStatus();
+ }else if(this.multiple){
+ // 重置多选状态
+ this.cale.cleanMultipleStatus();
+ }
+ this.cale.setDate(date,'init')
+ this.weeks = this.cale.weeks
+ this.nowDate = this.calendar = this.cale.getInfo(date)
+ this.changeConfirmStatus();
+ },
+ /**
+ * 变化触发
+ */
+ change() {
+ this.changeConfirmStatus();
+ if (!this.insert) return
+ this.setEmit('change')
+ },
+ changeConfirmStatus() {
+ if(this.readonly) {
+ this.allowConfirm = false;
+ } else if (this.range) {
+ this.allowConfirm = this.cale.rangeStatus.after ? true : false;
+ } else if(this.multiple) {
+ this.allowConfirm = this.cale.multipleStatus.data.length > 0 ? true : false;
+ }
+ },
+ /**
+ * 选择月份触发
+ */
+ monthSwitch() {
+ let {
+ year,
+ month
+ } = this.nowDate
+ this.$emit('monthSwitch', {
+ year,
+ month: Number(month)
+ })
+ },
+ /**
+ * 派发事件
+ * @param {Object} name
+ */
+ setEmit(name) {
+ let {
+ year,
+ month,
+ date,
+ fullDate,
+ lunar,
+ extraInfo
+ } = this.calendar
+ this.$emit(name, {
+ range: this.cale.rangeStatus,
+ multiple: this.cale.multipleStatus,
+ year,
+ month,
+ date,
+ fulldate: fullDate,
+ lunar,
+ extraInfo: extraInfo || {}
+ })
+ },
+ /**
+ * 选择天触发
+ * @param {Object} weeks
+ */
+ choiceDate(weeks) {
+ if (weeks.disable || this.readonly) return
+ this.calendar = weeks
+ // 设置范围选择
+ this.cale.setRange(this.calendar.fullDate)
+ // 设置多选
+ this.cale.setMultiple(this.calendar.fullDate);
+ this.weeks = this.cale.weeks
+ this.change()
+ },
+ /**
+ * 回到今天
+ */
+ backToday() {
+ const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
+ const date = this.cale.getDate(new Date())
+ const todayYearMonth = `${date.year}-${date.month}`
+ this.init(date.fullDate)
+ if (nowYearMonth !== todayYearMonth) {
+ this.monthSwitch()
+ }
+ this.change()
+ },
+ /**
+ * 上个月
+ */
+ pre() {
+ const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+ this.setDate(preDate)
+ this.monthSwitch()
+ },
+ /**
+ * 下个月
+ */
+ next() {
+ const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+ this.setDate(nextDate)
+ this.monthSwitch()
+ },
+ /**
+ * 设置日期
+ * @param {Object} date
+ */
+ setDate(date) {
+ this.cale.setDate(date)
+ this.weeks = this.cale.weeks
+ this.nowDate = this.cale.getInfo(date)
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ $uv-border-color: #EDEDED !default;
+ .uv-calendar__content {
+ background-color: #fff;
+ }
+ .line {
+ width: 750rpx;
+ height: 1px;
+ border-bottom-color: $uv-border-color;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-calendars/package.json b/uni_modules/uv-calendars/package.json
new file mode 100644
index 0000000..7153510
--- /dev/null
+++ b/uni_modules/uv-calendars/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-calendars",
+ "displayName": "uv-calendars 最新日历 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.15",
+ "description": "新版本uv-calendars,不仅拥有老版本的所有功能,还增加了更加适用的插入页面等强大功能,且更加简洁。查看日期、选择单个或多个或任意范围日期,打点操作,自定义文案,自定义主题等强大功能。",
+ "keywords": [
+ "uv-ui",
+ "uvui",
+ "日历",
+ "打卡",
+ "日历选择"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration":{
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-popup",
+ "uv-toolbar"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-calendars/readme.md b/uni_modules/uv-calendars/readme.md
new file mode 100644
index 0000000..d986eb9
--- /dev/null
+++ b/uni_modules/uv-calendars/readme.md
@@ -0,0 +1,23 @@
+## Calendars 全新日历
+
+> **组件名:uv-calendars**
+
+为了解决老版本`uv-calendar`性能问题,特别是对日期选择范围有很大限制,体验不友好等缺点。于是有了新版日历组件。
+
+新版本`uv-calendars`,不仅拥有老版本的所有功能,还增加了更加适用的插入页面等强大功能,且更加简洁。查看日期、选择单个或多个或任意范围日期,打点操作,自定义文案,自定义主题等强大功能。
+
+常用场景:酒店日期预订、火车机票选择购买日期、上下班打卡等。
+
+# <a href="https://www.uvui.cn/components/calendars.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-code-input/changelog.md b/uni_modules/uv-code-input/changelog.md
new file mode 100644
index 0000000..1fefc03
--- /dev/null
+++ b/uni_modules/uv-code-input/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.5(2023-08-05)
+在vue2模式下,v-model设置为0时不生效的BUG
+## 1.0.4(2023-07-13)
+1. 修复value/v-model更改不生效的BUG
+## 1.0.3(2023-06-28)
+修复:使用:disabledKeyboard="true"属性,事件全部失效的BUG
+## 1.0.2(2023-06-23)
+优化下边框
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-code-input 验证码输入
diff --git a/uni_modules/uv-code-input/components/uv-code-input/props.js b/uni_modules/uv-code-input/components/uv-code-input/props.js
new file mode 100644
index 0000000..4a5e460
--- /dev/null
+++ b/uni_modules/uv-code-input/components/uv-code-input/props.js
@@ -0,0 +1,83 @@
+export default {
+ props: {
+ value: {
+ type: [String, Number],
+ default: ''
+ },
+ modelValue: {
+ type: [String, Number],
+ default: ''
+ },
+ // 键盘弹起时,是否自动上推页面
+ adjustPosition: {
+ type: Boolean,
+ default: true
+ },
+ // 最大输入长度
+ maxlength: {
+ type: [String, Number],
+ default: 6
+ },
+ // 是否用圆点填充
+ dot: {
+ type: Boolean,
+ default: false
+ },
+ // 显示模式,box-盒子模式,line-底部横线模式
+ mode: {
+ type: String,
+ default: 'box'
+ },
+ // 是否细边框
+ hairline: {
+ type: Boolean,
+ default: false
+ },
+ // 字符间的距离
+ space: {
+ type: [String, Number],
+ default: 10
+ },
+ // 是否自动获取焦点
+ focus: {
+ type: Boolean,
+ default: false
+ },
+ // 字体是否加粗
+ bold: {
+ type: Boolean,
+ default: false
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: '#606266'
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 18
+ },
+ // 输入框的大小,宽等于高
+ size: {
+ type: [String, Number],
+ default: 35
+ },
+ // 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true
+ disabledKeyboard: {
+ type: Boolean,
+ default: false
+ },
+ // 边框和线条颜色
+ borderColor: {
+ type: String,
+ default: '#c9cacc'
+ },
+ // 是否禁止输入"."符号
+ disabledDot: {
+ type: Boolean,
+ default: true
+ },
+ ...uni.$uv?.props?.codeInput
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-code-input/components/uv-code-input/uv-code-input.vue b/uni_modules/uv-code-input/components/uv-code-input/uv-code-input.vue
new file mode 100644
index 0000000..14ad8a2
--- /dev/null
+++ b/uni_modules/uv-code-input/components/uv-code-input/uv-code-input.vue
@@ -0,0 +1,272 @@
+<template>
+ <view class="uv-code-input">
+ <view
+ class="uv-code-input__item"
+ :style="[itemStyle(index)]"
+ v-for="(item, index) in codeLength"
+ :key="index"
+ >
+ <view
+ class="uv-code-input__item__dot"
+ v-if="dot && codeArray.length > index"
+ ></view>
+ <text
+ v-else
+ :style="{
+ fontSize: $uv.addUnit(fontSize),
+ fontWeight: bold ? 'bold' : 'normal',
+ color: color
+ }"
+ >{{codeArray[index]}}</text>
+ <view
+ class="uv-code-input__item__line"
+ v-if="mode === 'line'"
+ :style="[lineStyle]"
+ ></view>
+ <!-- #ifndef APP-PLUS -->
+ <view v-if="isFocus && codeArray.length === index" :style="{backgroundColor: color}" class="uv-code-input__item__cursor"></view>
+ <!-- #endif -->
+ </view>
+ <input
+ :disabled="disabledKeyboard"
+ type="number"
+ :focus="focus"
+ :value="inputValue"
+ :maxlength="maxlength"
+ :adjustPosition="adjustPosition"
+ class="uv-code-input__input"
+ @input="inputHandler"
+ :style="{
+ height: $uv.addUnit(size)
+ }"
+ @focus="isFocus = true"
+ @blur="isFocus = false"
+ />
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * CodeInput 验证码输入
+ * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uvui的键盘组件使用
+ * @tutorial https://www.uvui.cn/components/codeInput.html
+ * @property {String | Number} value / v-model 预置值
+ * @property {String | Number} maxlength 最大输入长度 (默认 6 )
+ * @property {Boolean} dot 是否用圆点填充 (默认 false )
+ * @property {String} mode 显示模式,box-盒子模式,line-底部横线模式 (默认 'box' )
+ * @property {Boolean} hairline 是否细边框 (默认 false )
+ * @property {String | Number} space 字符间的距离 (默认 10 )
+ * @property {Boolean} focus 是否自动获取焦点 (默认 false )
+ * @property {Boolean} bold 字体和输入横线是否加粗 (默认 false )
+ * @property {String} color 字体颜色 (默认 '#606266' )
+ * @property {String | Number} fontSize 字体大小,单位px (默认 18 )
+ * @property {String | Number} size 输入框的大小,宽等于高 (默认 35 )
+ * @property {Boolean} disabledKeyboard 是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true (默认 false )
+ * @property {String} borderColor 边框和线条颜色 (默认 '#c9cacc' )
+ * @property {Boolean} disabledDot 是否禁止输入"."符号 (默认 true )
+ *
+ * @event {Function} change 输入内容发生改变时触发,具体见上方说明 value:当前输入的值
+ * @event {Function} finish 输入字符个数达maxlength值时触发,见上方说明 value:当前输入的值
+ * @example <uv-code-input v-model="value4" :focus="true"></uv-code-input>
+ */
+ export default {
+ name: 'uv-code-input',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ inputValue: '',
+ isFocus: this.focus
+ }
+ },
+ created() {
+ const value = String(this.value) || String(this.modelValue);
+ this.inputValue = String(value).substring(0, this.maxlength);
+ },
+ watch: {
+ value(newVal) {
+ // 转为字符串,超出部分截掉
+ this.inputValue = String(newVal).substring(0, this.maxlength);
+ if (this.disabledKeyboard) {
+ this.customInput();
+ }
+ },
+ modelValue(newVal) {
+ // 转为字符串,超出部分截掉
+ this.inputValue = String(newVal).substring(0, this.maxlength);
+ if (this.disabledKeyboard) {
+ this.customInput();
+ }
+ }
+ },
+ computed: {
+ // 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for
+ codeLength() {
+ return new Array(Number(this.maxlength))
+ },
+ // 循环item的样式
+ itemStyle() {
+ return index => {
+ const addUnit = this.$uv.addUnit
+ const style = {
+ width: addUnit(this.size),
+ height: addUnit(this.size)
+ }
+ // 盒子模式下,需要额外进行处理
+ if (this.mode === 'box') {
+ // 设置盒子的边框,如果是细边框,则设置为0.5px宽度
+ style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}`
+ // 如果盒子间距为0的话
+ if (this.$uv.getPx(this.space) === 0) {
+ // 给第一和最后一个盒子设置圆角
+ if (index === 0) {
+ style.borderTopLeftRadius = '3px'
+ style.borderBottomLeftRadius = '3px'
+ }
+ if (index === this.codeLength.length - 1) {
+ style.borderTopRightRadius = '3px'
+ style.borderBottomRightRadius = '3px'
+ }
+ // 最后一个盒子的右边框需要保留
+ if (index !== this.codeLength.length - 1) {
+ style.borderRight = 'none'
+ }
+ }
+ }
+ if (index !== this.codeLength.length - 1) {
+ // 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框
+ style.marginRight = addUnit(this.space)
+ } else {
+ // 最后一个盒子的有边框需要保留
+ style.marginRight = 0
+ }
+ return style
+ }
+ },
+ // 将输入的值,转为数组,给item历遍时,根据当前的索引显示数组的元素
+ codeArray() {
+ return String(this.inputValue).split('')
+ },
+ // 下划线模式下,横线的样式
+ lineStyle() {
+ const style = {}
+ style.height = this.hairline ? '1px' : '4px'
+ style.width = this.$uv.addUnit(this.size)
+ // 线条模式下,背景色即为边框颜色
+ style.backgroundColor = this.borderColor
+ return style
+ }
+ },
+ methods: {
+ // 监听输入框的值发生变化
+ inputHandler(e) {
+ const value = e.detail.value
+ this.inputValue = value
+ // 是否允许输入“.”符号
+ if (this.disabledDot) {
+ this.$nextTick(() => {
+ this.inputValue = value.replace('.', '')
+ })
+ }
+ // 未达到maxlength之前,发送change事件,达到后发送finish事件
+ this.$emit('change', value)
+ // 修改通过v-model双向绑定的值
+ this.$emit('input', value)
+ this.$emit('update:modelValue', value)
+ // 达到用户指定输入长度时,发出完成事件
+ if (String(value).length >= Number(this.maxlength)) {
+ this.$emit('finish', value)
+ }
+ },
+ // 自定义键盘输入值监听
+ customInput() {
+ const value = this.inputValue;
+ // 是否允许输入“.”符号
+ if (this.disabledDot) {
+ this.$nextTick(() => {
+ this.inputValue = value.replace('.', '')
+ })
+ }
+ // 未达到maxlength之前,发送change事件,达到后发送finish事件
+ this.$emit('change', value)
+ // 达到用户指定输入长度时,发出完成事件
+ if (String(value).length >= Number(this.maxlength)) {
+ this.$emit('finish', value)
+ }
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-code-input-cursor-width: 1px;
+ $uv-code-input-cursor-height: 40%;
+ $uv-code-input-cursor-animation-duration: 1s;
+ $uv-code-input-cursor-animation-name: uv-cursor-flicker;
+ .uv-code-input {
+ @include flex;
+ position: relative;
+ overflow: hidden;
+ &__item {
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ &__text {
+ font-size: 15px;
+ color: $uv-content-color;
+ }
+ &__dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 100px;
+ background-color: $uv-content-color;
+ }
+ &__line {
+ position: absolute;
+ bottom: 0;
+ height: 4px;
+ border-radius: 100px;
+ width: 40px;
+ background-color: $uv-content-color;
+ }
+ /* #ifndef APP-PLUS */
+ &__cursor {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: $uv-code-input-cursor-width;
+ height: $uv-code-input-cursor-height;
+ animation: $uv-code-input-cursor-animation-duration uv-cursor-flicker infinite;
+ }
+ /* #endif */
+ }
+ &__input {
+ // 之所以需要input输入框,是因为有它才能唤起键盘
+ // 这里将它设置为两倍的屏幕宽度,再将左边的一半移出屏幕,为了不让用户看到输入的内容
+ position: absolute;
+ left: -750rpx;
+ width: 1500rpx;
+ top: 0;
+ background-color: transparent;
+ text-align: left;
+ }
+ }
+ /* #ifndef APP-PLUS */
+ @keyframes uv-cursor-flicker {
+ 0% {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+ }
+ /* #endif */
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-code-input/package.json b/uni_modules/uv-code-input/package.json
new file mode 100644
index 0000000..ab8762c
--- /dev/null
+++ b/uni_modules/uv-code-input/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-code-input",
+ "displayName": "uv-code-input 验证码输入 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "验证码输入组件一般用于验证用户短信验证码的场景,输入框或横线多种模式可选。",
+ "keywords": [
+ "uv-code-input",
+ "uvui",
+ "uv-ui",
+ "code",
+ "验证码输入"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-code-input/readme.md b/uni_modules/uv-code-input/readme.md
new file mode 100644
index 0000000..aa5b0a1
--- /dev/null
+++ b/uni_modules/uv-code-input/readme.md
@@ -0,0 +1,19 @@
+## CodeInput 验证码输入框
+
+> **组件名:uv-code-input**
+
+该组件一般用于验证用户短信验证码的场景,输入框或横线多种模式可选。
+
+# <a href="https://www.uvui.cn/components/codeInput.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-code/changelog.md b/uni_modules/uv-code/changelog.md
new file mode 100644
index 0000000..7054a25
--- /dev/null
+++ b/uni_modules/uv-code/changelog.md
@@ -0,0 +1,9 @@
+## 1.0.3(2023-10-13)
+1. 优化
+## 1.0.2(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-code 验证码倒计时
diff --git a/uni_modules/uv-code/components/uv-code/props.js b/uni_modules/uv-code/components/uv-code/props.js
new file mode 100644
index 0000000..afc9d3c
--- /dev/null
+++ b/uni_modules/uv-code/components/uv-code/props.js
@@ -0,0 +1,35 @@
+export default {
+ props: {
+ // 倒计时总秒数
+ seconds: {
+ type: [String, Number],
+ default: 60
+ },
+ // 尚未开始时提示
+ startText: {
+ type: String,
+ default: '获取验证码'
+ },
+ // 正在倒计时中的提示
+ changeText: {
+ type: String,
+ default: 'X秒重新获取'
+ },
+ // 倒计时结束时的提示
+ endText: {
+ type: String,
+ default: '重新获取'
+ },
+ // 是否在H5刷新或各端返回再进入时继续倒计时
+ keepRunning: {
+ type: Boolean,
+ default: false
+ },
+ // 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了
+ uniqueKey: {
+ type: String,
+ default: ''
+ },
+ ...uni.$uv?.props?.code
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-code/components/uv-code/uv-code.vue b/uni_modules/uv-code/components/uv-code/uv-code.vue
new file mode 100644
index 0000000..de02c83
--- /dev/null
+++ b/uni_modules/uv-code/components/uv-code/uv-code.vue
@@ -0,0 +1,136 @@
+<template>
+ <view class="uv-code">
+ <!-- 此组件功能由js完成,无需写html逻辑 -->
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Code 验证码倒计时
+ * @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件不提供界面显示,只提供倒计时文本,由用户将文本嵌入到具体的场景
+ * @tutorial https://www.uvui.cn/components/code.html
+ * @property {String | Number} seconds 倒计时所需的秒数(默认 60 )
+ * @property {String} startText 开始前的提示语,见官网说明(默认 '获取验证码' )
+ * @property {String} changeText 倒计时期间的提示语,必须带有字母"x",见官网说明(默认 'X秒重新获取' )
+ * @property {String} endText 倒计结束的提示语,见官网说明(默认 '重新获取' )
+ * @property {Boolean} keepRunning 是否在H5刷新或各端返回再进入时继续倒计时( 默认false )
+ * @property {String} uniqueKey 为了区分多个页面,或者一个页面多个倒计时组件本地存储的继续倒计时变了
+ *
+ * @event {Function} change 倒计时期间,每秒触发一次
+ * @event {Function} start 开始倒计时触发
+ * @event {Function} end 结束倒计时触发
+ * @example <uv-code ref="uCode" @change="codeChange" seconds="20"></uv-code>
+ */
+ export default {
+ name: "uv-code",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ secNum: this.seconds,
+ timer: null,
+ canGetCode: true, // 是否可以执行验证码操作
+ }
+ },
+ mounted() {
+ this.checkKeepRunning()
+ },
+ watch: {
+ seconds: {
+ immediate: true,
+ handler(n) {
+ this.secNum = n
+ }
+ }
+ },
+ methods: {
+ checkKeepRunning() {
+ // 获取上一次退出页面(H5还包括刷新)时的时间戳,如果没有上次的保存,此值可能为空
+ let lastTimestamp = Number(uni.getStorageSync(this.uniqueKey + '_$uCountDownTimestamp'))
+ if (!lastTimestamp) return this.changeEvent(this.startText)
+ // 当前秒的时间戳
+ let nowTimestamp = Math.floor((+new Date()) / 1000)
+ // 判断当前的时间戳,是否小于上一次的本该按设定结束,却提前结束的时间戳
+ if (this.keepRunning && lastTimestamp && lastTimestamp > nowTimestamp) {
+ // 剩余尚未执行完的倒计秒数
+ this.secNum = lastTimestamp - nowTimestamp
+ // 清除本地保存的变量
+ uni.removeStorageSync(this.uniqueKey + '_$uCountDownTimestamp')
+ // 开始倒计时
+ this.start()
+ } else {
+ // 如果不存在需要继续上一次的倒计时,执行正常的逻辑
+ this.changeEvent(this.startText)
+ }
+ },
+ // 开始倒计时
+ start() {
+ // 防止快速点击获取验证码的按钮而导致内部产生多个定时器导致混乱
+ if (this.timer) {
+ clearInterval(this.timer)
+ this.timer = null
+ }
+ this.$emit('start')
+ this.canGetCode = false
+ // 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示
+ this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
+ this.timer = setInterval(() => {
+ if (--this.secNum) {
+ // 用当前倒计时的秒数替换提示字符串中的"x"字母
+ this.changeEvent(this.changeText.replace(/x|X/, this.secNum))
+ } else {
+ clearInterval(this.timer)
+ this.timer = null
+ this.changeEvent(this.endText)
+ this.secNum = this.seconds
+ this.$emit('end')
+ this.canGetCode = true
+ }
+ }, 1000)
+ this.setTimeToStorage()
+ },
+ // 重置,可以让用户再次获取验证码
+ reset() {
+ this.canGetCode = true
+ clearInterval(this.timer)
+ this.secNum = this.seconds
+ this.changeEvent(this.endText)
+ },
+ changeEvent(text) {
+ this.$emit('change', text)
+ },
+ // 保存时间戳,为了防止倒计时尚未结束,H5刷新或者各端的右上角返回上一页再进来
+ setTimeToStorage() {
+ if (!this.keepRunning || !this.timer) return
+ // 记录当前的时间戳,为了下次进入页面,如果还在倒计时内的话,继续倒计时
+ // 倒计时尚未结束,结果大于0;倒计时已经开始,就会小于初始值,如果等于初始值,说明没有开始倒计时,无需处理
+ if (this.secNum > 0 && this.secNum <= this.seconds) {
+ // 获取当前时间戳(+ new Date()为特殊写法),除以1000变成秒,再去除小数部分
+ let nowTimestamp = Math.floor((+new Date()) / 1000)
+ // 将本该结束时候的时间戳保存起来 => 当前时间戳 + 剩余的秒数
+ uni.setStorage({
+ key: this.uniqueKey + '_$uCountDownTimestamp',
+ data: nowTimestamp + Number(this.secNum)
+ })
+ }
+ }
+ },
+ // #ifdef VUE2
+ // 组件销毁的时候,清除定时器,否则定时器会继续存在,系统不会自动清除
+ beforeDestroy() {
+ this.setTimeToStorage()
+ clearTimeout(this.timer)
+ this.timer = null
+ },
+ // #endif
+ // #ifdef VUE3
+ // 组件销毁,兼容vue3
+ unmounted() {
+ this.setTimeToStorage()
+ clearTimeout(this.timer)
+ this.timer = null
+ }
+ // #endif
+ }
+</script>
\ No newline at end of file
diff --git a/uni_modules/uv-code/package.json b/uni_modules/uv-code/package.json
new file mode 100644
index 0000000..d4392c0
--- /dev/null
+++ b/uni_modules/uv-code/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-code",
+ "displayName": "uv-code 验证码倒计时 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件不提供界面显示,只提供倒计时文本,由用户将文本嵌入到具体的场景。",
+ "keywords": [
+ "uv-code",
+ "uvui",
+ "uv-ui",
+ "code",
+ "验证码倒计时"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-code/readme.md b/uni_modules/uv-code/readme.md
new file mode 100644
index 0000000..cf2eb51
--- /dev/null
+++ b/uni_modules/uv-code/readme.md
@@ -0,0 +1,11 @@
+## Code 验证码输入框
+
+> **组件名:uv-code**
+
+考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件不提供界面显示,只提供倒计时文本,由用户将文本嵌入到具体的场景。
+
+### <a href="https://www.uvui.cn/components/code.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-collapse/changelog.md b/uni_modules/uv-collapse/changelog.md
new file mode 100644
index 0000000..0219560
--- /dev/null
+++ b/uni_modules/uv-collapse/changelog.md
@@ -0,0 +1,5 @@
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-collapse 折叠面板
diff --git a/uni_modules/uv-collapse/components/uv-collapse-item/props.js b/uni_modules/uv-collapse/components/uv-collapse-item/props.js
new file mode 100644
index 0000000..18983c1
--- /dev/null
+++ b/uni_modules/uv-collapse/components/uv-collapse-item/props.js
@@ -0,0 +1,60 @@
+export default {
+ props: {
+ // 标题
+ title: {
+ type: String,
+ default: ''
+ },
+ // 标题右侧内容
+ value: {
+ type: String,
+ default: ''
+ },
+ // 标题下方的描述信息
+ label: {
+ type: String,
+ default: ''
+ },
+ // 是否禁用折叠面板
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否展示右侧箭头并开启点击反馈
+ isLink: {
+ type: Boolean,
+ default: true
+ },
+ // 是否开启点击反馈
+ clickable: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示内边框
+ border: {
+ type: Boolean,
+ default: true
+ },
+ // 标题的对齐方式
+ align: {
+ type: String,
+ default: 'left'
+ },
+ // 唯一标识符
+ name: {
+ type: [String, Number],
+ default: ''
+ },
+ // 标题左侧图片,可为绝对路径的图片或内置图标
+ icon: {
+ type: String,
+ default: ''
+ },
+ // 面板展开收起的过渡时间,单位ms
+ duration: {
+ type: Number,
+ default: 300
+ },
+ ...uni.$uv?.props?.collapseItem
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-collapse/components/uv-collapse-item/uv-collapse-item.vue b/uni_modules/uv-collapse/components/uv-collapse-item/uv-collapse-item.vue
new file mode 100644
index 0000000..840f28d
--- /dev/null
+++ b/uni_modules/uv-collapse/components/uv-collapse-item/uv-collapse-item.vue
@@ -0,0 +1,229 @@
+<template>
+ <view class="uv-collapse-item">
+ <uv-cell
+ :title="title"
+ :value="value"
+ :label="label"
+ :icon="icon"
+ :isLink="isLink"
+ :clickable="clickable"
+ :border="parentData.border && showBorder"
+ @click="clickHandler"
+ :arrowDirection="expanded ? 'up' : 'down'"
+ :disabled="disabled"
+ >
+ <!-- #ifndef MP-WEIXIN -->
+ <!-- 微信小程序不支持,因为微信中不支持 <slot name="title" slot="title" />的写法 -->
+ <template slot="title">
+ <slot name="title"></slot>
+ </template>
+ <template slot="icon">
+ <slot name="icon"></slot>
+ </template>
+ <template slot="value">
+ <slot name="value"></slot>
+ </template>
+ <template slot="right-icon">
+ <slot name="right-icon"></slot>
+ </template>
+ <!-- #endif -->
+ </uv-cell>
+ <view
+ class="uv-collapse-item__content"
+ :animation="animationData"
+ ref="animation"
+ >
+ <view
+ class="uv-collapse-item__content__text content-class"
+ :id="elId"
+ :ref="elId"
+ ><slot /></view>
+ </view>
+ <uv-line v-if="parentData.border"></uv-line>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const animation = uni.requireNativePlugin('animation')
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * collapseItem 折叠面板Item
+ * @description 通过折叠面板收纳内容区域(搭配uv-collapse使用)
+ * @tutorial https://www.uvui.cn/components/collapse.html
+ * @property {String} title 标题
+ * @property {String} value 标题右侧内容
+ * @property {String} label 标题下方的描述信息
+ * @property {Boolean} disbled 是否禁用折叠面板 ( 默认 false )
+ * @property {Boolean} isLink 是否展示右侧箭头并开启点击反馈 ( 默认 true )
+ * @property {Boolean} clickable 是否开启点击反馈 ( 默认 true )
+ * @property {Boolean} border 是否显示内边框 ( 默认 true )
+ * @property {String} align 标题的对齐方式 ( 默认 'left' )
+ * @property {String | Number} name 唯一标识符
+ * @property {String} icon 标题左侧图片,可为绝对路径的图片或内置图标
+ * @event {Function} change 某个item被打开或者收起时触发
+ * @example <uv-collapse-item :title="item.head" v-for="(item, index) in itemList" :key="index">{{item.body}}</uv-collapse-item>
+ */
+ export default {
+ name: "uv-collapse-item",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ elId: '',
+ // uni.createAnimation的导出数据
+ animationData: {},
+ // 是否展开状态
+ expanded: false,
+ // 根据expanded确定是否显示border,为了控制展开时,cell的下划线更好的显示效果,进行一定时间的延时
+ showBorder: false,
+ // 是否动画中,如果是则不允许继续触发点击
+ animating: false,
+ // 父组件uv-collapse的参数
+ parentData: {
+ accordion: false,
+ border: false
+ }
+ };
+ },
+ watch: {
+ expanded(n) {
+ clearTimeout(this.timer)
+ this.timer = null
+ // 这里根据expanded的值来进行一定的延时,是为了cell的下划线更好的显示效果
+ this.timer = setTimeout(() => {
+ this.showBorder = n
+ }, n ? 10 : 290)
+ }
+ },
+ created() {
+ this.elId = this.$uv.guid();
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ // 异步获取内容,或者动态修改了内容时,需要重新初始化
+ init() {
+ // 初始化数据
+ this.updateParentData()
+ if (!this.parent) {
+ return this.$uv.error('uv-collapse-item必须要搭配uv-collapse组件使用')
+ }
+ const {
+ value,
+ accordion,
+ children = []
+ } = this.parent
+
+ if (accordion) {
+ if (this.$uv.test.array(value)) {
+ return this.$uv.error('手风琴模式下,uv-collapse组件的value参数不能为数组')
+ }
+ this.expanded = this.name == value
+ } else {
+ if (!this.$uv.test.array(value) && value !== null) {
+ return this.$uv.error('非手风琴模式下,uv-collapse组件的value参数必须为数组')
+ }
+ this.expanded = (value || []).some(item => item == this.name)
+ }
+ // 设置组件的展开或收起状态
+ this.$nextTick(function() {
+ this.setContentAnimate()
+ })
+ },
+ updateParentData() {
+ // 此方法在mixin中
+ this.getParentData('uv-collapse')
+ },
+ async setContentAnimate() {
+ // 每次面板打开或者收起时,都查询元素尺寸
+ // 好处是,父组件从服务端获取内容后,变更折叠面板后可以获得最新的高度
+ const rect = await this.queryRect()
+ const height = this.expanded ? rect.height : 0
+ this.animating = true
+ // #ifdef APP-NVUE
+ const ref = this.$refs['animation'].ref
+ animation.transition(ref, {
+ styles: {
+ height: height + 'px'
+ },
+ duration: this.duration,
+ // 必须设置为true,否则会到面板收起或展开时,页面其他元素不会随之调整它们的布局
+ needLayout: true,
+ timingFunction: 'ease-in-out',
+ }, () => {
+ this.animating = false
+ })
+ // #endif
+
+ // #ifndef APP-NVUE
+ const animation = uni.createAnimation({
+ timingFunction: 'ease-in-out',
+ });
+ animation
+ .height(height)
+ .step({
+ duration: this.duration,
+ })
+ .step()
+ // 导出动画数据给面板的animationData值
+ this.animationData = animation.export()
+ // 标识动画结束
+ this.$uv.sleep(this.duration).then(() => {
+ this.animating = false
+ })
+ // #endif
+ },
+ // 点击collapsehead头部
+ clickHandler() {
+ if (this.disabled && this.animating) return
+ // 设置本组件为相反的状态
+ this.parent && this.parent.onChange(this)
+ },
+ // 查询内容高度
+ queryRect() {
+ // #ifndef APP-NVUE
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`#${this.elId}`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs[this.elId], res => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ }
+ },
+ };
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-collapse-item {
+
+ &__content {
+ overflow: hidden;
+ height: 0;
+
+ &__text {
+ padding: 12px 15px;
+ color: $uv-content-color;
+ font-size: 14px;
+ line-height: 18px;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-collapse/components/uv-collapse/props.js b/uni_modules/uv-collapse/components/uv-collapse/props.js
new file mode 100644
index 0000000..70fa04c
--- /dev/null
+++ b/uni_modules/uv-collapse/components/uv-collapse/props.js
@@ -0,0 +1,20 @@
+export default {
+ props: {
+ // 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number
+ value: {
+ type: [String, Number, Array, null],
+ default: null
+ },
+ // 是否手风琴模式
+ accordion: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示外边框
+ border: {
+ type: Boolean,
+ default: true
+ },
+ ...uni.$uv?.props?.collapse
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-collapse/components/uv-collapse/uv-collapse.vue b/uni_modules/uv-collapse/components/uv-collapse/uv-collapse.vue
new file mode 100644
index 0000000..5e5c96d
--- /dev/null
+++ b/uni_modules/uv-collapse/components/uv-collapse/uv-collapse.vue
@@ -0,0 +1,86 @@
+<template>
+ <view class="uv-collapse">
+ <uv-line v-if="border"></uv-line>
+ <slot />
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * collapse 折叠面板
+ * @description 通过折叠面板收纳内容区域
+ * @tutorial https://www.uvui.cn/components/collapse.html
+ * @property {String | Number | Array} value 当前展开面板的name,非手风琴模式:[<string | number>],手风琴模式:string | number
+ * @property {Boolean} accordion 是否手风琴模式( 默认 false )
+ * @property {Boolean} border 是否显示外边框 ( 默认 true )
+ * @event {Function} change 当前激活面板展开时触发(如果是手风琴模式,参数activeNames类型为String,否则为Array)
+ * @example <uv-collapse></uv-collapse>
+ */
+ export default {
+ name: "uv-collapse",
+ mixins: [mpMixin, mixin, props],
+ watch: {
+ needInit() {
+ this.init()
+ },
+ // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+ parentData() {
+ if (this.children.length) {
+ this.children.map(child => {
+ // 判断子组件(uv-checkbox)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+ typeof(child.updateParentData) === 'function' && child.updateParentData()
+ })
+ }
+ }
+ },
+ created() {
+ this.children = []
+ },
+ computed: {
+ needInit() {
+ // 通过computed,同时监听accordion和value值的变化
+ // 再通过watch去执行init()方法,进行再一次的初始化
+ return [this.accordion, this.value]
+ }
+ },
+ methods: {
+ // 重新初始化一次内部的所有子元素
+ init() {
+ this.children.map(child => {
+ child.init()
+ })
+ },
+ /**
+ * collapse-item被点击时触发,由collapse统一处理各子组件的状态
+ * @param {Object} target 被操作的面板的实例
+ */
+ onChange(target) {
+ let changeArr = []
+ this.children.map((child, index) => {
+ // 如果是手风琴模式,将其他的折叠面板收起来
+ if (this.accordion) {
+ child.expanded = child === target ? !target.expanded : false
+ child.setContentAnimate()
+ } else {
+ if(child === target) {
+ child.expanded = !child.expanded
+ child.setContentAnimate()
+ }
+ }
+ // 拼接change事件中,数组元素的状态
+ changeArr.push({
+ // 如果没有定义name属性,则默认返回组件的index索引
+ name: child.name || index,
+ status: child.expanded ? 'open' : 'close'
+ })
+ })
+
+ this.$emit('change', changeArr)
+ this.$emit(target.expanded ? 'open' : 'close', target.name)
+ }
+ }
+ }
+</script>
diff --git a/uni_modules/uv-collapse/package.json b/uni_modules/uv-collapse/package.json
new file mode 100644
index 0000000..cf9565f
--- /dev/null
+++ b/uni_modules/uv-collapse/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-collapse",
+ "displayName": "uv-collapse 折叠面板 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.1",
+ "description": "折叠面板组件,通过折叠面板收纳内容区域,点击可展开收起,多功能参数可配置。",
+ "keywords": [
+ "uv-collapse",
+ "uvui",
+ "uv-ui",
+ "collapse",
+ "折叠面板"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-line",
+ "uv-cell"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-collapse/readme.md b/uni_modules/uv-collapse/readme.md
new file mode 100644
index 0000000..cb340ba
--- /dev/null
+++ b/uni_modules/uv-collapse/readme.md
@@ -0,0 +1,11 @@
+## Collapse 折叠面板
+
+> **组件名:uv-collapse**
+
+通过折叠面板收纳内容区域,点击可展开收起,多功能参数可配置。
+
+### <a href="https://www.uvui.cn/components/collapse.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-count-down/changelog.md b/uni_modules/uv-count-down/changelog.md
new file mode 100644
index 0000000..44392c2
--- /dev/null
+++ b/uni_modules/uv-count-down/changelog.md
@@ -0,0 +1,9 @@
+## 1.0.3(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.2(2023-06-20)
+1. 增加外部样式customStyle参数
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-count-down 倒计时
diff --git a/uni_modules/uv-count-down/components/uv-count-down/props.js b/uni_modules/uv-count-down/components/uv-count-down/props.js
new file mode 100644
index 0000000..c5b5461
--- /dev/null
+++ b/uni_modules/uv-count-down/components/uv-count-down/props.js
@@ -0,0 +1,25 @@
+export default {
+ props: {
+ // 倒计时时长,单位ms
+ time: {
+ type: [String, Number],
+ default: 0
+ },
+ // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒
+ format: {
+ type: String,
+ default: 'HH:mm:ss'
+ },
+ // 是否自动开始倒计时
+ autoStart: {
+ type: Boolean,
+ default: true
+ },
+ // 是否展示毫秒倒计时
+ millisecond: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.countDown
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-count-down/components/uv-count-down/utils.js b/uni_modules/uv-count-down/components/uv-count-down/utils.js
new file mode 100644
index 0000000..8c75005
--- /dev/null
+++ b/uni_modules/uv-count-down/components/uv-count-down/utils.js
@@ -0,0 +1,62 @@
+// 补0,如1 -> 01
+function padZero(num, targetLength = 2) {
+ let str = `${num}`
+ while (str.length < targetLength) {
+ str = `0${str}`
+ }
+ return str
+}
+const SECOND = 1000
+const MINUTE = 60 * SECOND
+const HOUR = 60 * MINUTE
+const DAY = 24 * HOUR
+export function parseTimeData(time) {
+ const days = Math.floor(time / DAY)
+ const hours = Math.floor((time % DAY) / HOUR)
+ const minutes = Math.floor((time % HOUR) / MINUTE)
+ const seconds = Math.floor((time % MINUTE) / SECOND)
+ const milliseconds = Math.floor(time % SECOND)
+ return {
+ days,
+ hours,
+ minutes,
+ seconds,
+ milliseconds
+ }
+}
+export function parseFormat(format, timeData) {
+ let {
+ days,
+ hours,
+ minutes,
+ seconds,
+ milliseconds
+ } = timeData
+ // 如果格式化字符串中不存在DD(天),则将天的时间转为小时中去
+ if (format.indexOf('DD') === -1) {
+ hours += days * 24
+ } else {
+ // 对天补0
+ format = format.replace('DD', padZero(days))
+ }
+ // 其他同理于DD的格式化处理方式
+ if (format.indexOf('HH') === -1) {
+ minutes += hours * 60
+ } else {
+ format = format.replace('HH', padZero(hours))
+ }
+ if (format.indexOf('mm') === -1) {
+ seconds += minutes * 60
+ } else {
+ format = format.replace('mm', padZero(minutes))
+ }
+ if (format.indexOf('ss') === -1) {
+ milliseconds += seconds * 1000
+ } else {
+ format = format.replace('ss', padZero(seconds))
+ }
+ return format.replace('SSS', padZero(milliseconds, 3))
+}
+export function isSameSecond(time1, time2) {
+ return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
+}
diff --git a/uni_modules/uv-count-down/components/uv-count-down/uv-count-down.vue b/uni_modules/uv-count-down/components/uv-count-down/uv-count-down.vue
new file mode 100644
index 0000000..a758d31
--- /dev/null
+++ b/uni_modules/uv-count-down/components/uv-count-down/uv-count-down.vue
@@ -0,0 +1,169 @@
+<template>
+ <view
+ class="uv-count-down"
+ :style="[$uv.addStyle(customStyle)]">
+ <slot>
+ <text class="uv-count-down__text">{{ formattedTime }}</text>
+ </slot>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ import {
+ isSameSecond,
+ parseFormat,
+ parseTimeData
+ } from './utils';
+ /**
+ * uv-count-down 倒计时
+ * @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
+ * @tutorial https://www.uvui.cn/components/countDown.html
+ * @property {String | Number} time 倒计时时长,单位ms (默认 0 )
+ * @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' )
+ * @property {Boolean} autoStart 是否自动开始倒计时 (默认 true )
+ * @property {Boolean} millisecond 是否展示毫秒倒计时 (默认 false )
+ * @event {Function} finish 倒计时结束时触发
+ * @event {Function} change 倒计时变化时触发
+ * @event {Function} start 开始倒计时
+ * @event {Function} pause 暂停倒计时
+ * @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时
+ * @example <uv-count-down :time="time"></uv-count-down>
+ */
+ export default {
+ name: 'uv-count-down',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ timer: null,
+ // 各单位(天,时,分等)剩余时间
+ timeData: parseTimeData(0),
+ // 格式化后的时间,如"03:23:21"
+ formattedTime: '0',
+ // 倒计时是否正在进行中
+ runing: false,
+ endTime: 0, // 结束的毫秒时间戳
+ remainTime: 0, // 剩余的毫秒时间
+ }
+ },
+ watch: {
+ time(n) {
+ this.reset()
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.reset()
+ },
+ // 开始倒计时
+ start() {
+ if (this.runing) return
+ // 标识为进行中
+ this.runing = true
+ // 结束时间戳 = 此刻时间戳 + 剩余的时间
+ this.endTime = Date.now() + this.remainTime
+ this.toTick()
+ },
+ // 根据是否展示毫秒,执行不同操作函数
+ toTick() {
+ if (this.millisecond) {
+ this.microTick()
+ } else {
+ this.macroTick()
+ }
+ },
+ macroTick() {
+ this.clearTimeout()
+ // 每隔一定时间,更新一遍定时器的值
+ // 同时此定时器的作用也能带来毫秒级的更新
+ this.timer = setTimeout(() => {
+ // 获取剩余时间
+ const remain = this.getRemainTime()
+ // 重设剩余时间
+ if (!isSameSecond(remain, this.remainTime) || remain === 0) {
+ this.setRemainTime(remain)
+ }
+ // 如果剩余时间不为0,则继续检查更新倒计时
+ if (this.remainTime !== 0) {
+ this.macroTick()
+ }
+ }, 30)
+ },
+ microTick() {
+ this.clearTimeout()
+ this.timer = setTimeout(() => {
+ this.setRemainTime(this.getRemainTime())
+ if (this.remainTime !== 0) {
+ this.microTick()
+ }
+ }, 50)
+ },
+ // 获取剩余的时间
+ getRemainTime() {
+ // 取最大值,防止出现小于0的剩余时间值
+ return Math.max(this.endTime - Date.now(), 0)
+ },
+ // 设置剩余的时间
+ setRemainTime(remain) {
+ this.remainTime = remain
+ // 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象
+ const timeData = parseTimeData(remain)
+ this.$emit('change', timeData)
+ // 得出格式化后的时间
+ this.formattedTime = parseFormat(this.format, timeData)
+ // 如果时间已到,停止倒计时
+ if (remain <= 0) {
+ this.pause()
+ this.$emit('finish')
+ }
+ },
+ // 重置倒计时
+ reset() {
+ this.pause()
+ this.remainTime = this.time
+ this.setRemainTime(this.remainTime)
+ if (this.autoStart) {
+ this.start()
+ }
+ },
+ // 暂停倒计时
+ pause() {
+ this.runing = false;
+ this.clearTimeout()
+ },
+ // 清空定时器
+ clearTimeout() {
+ clearTimeout(this.timer)
+ this.timer = null
+ }
+ },
+ // #ifdef VUE2
+ beforeDestroy() {
+ this.clearTimeout()
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ this.clearTimeout()
+ }
+ // #endif
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-count-down-text-color: $uv-content-color !default;
+ $uv-count-down-text-font-size: 15px !default;
+ $uv-count-down-text-line-height: 22px !default;
+ .uv-count-down {
+ &__text {
+ color: $uv-count-down-text-color;
+ font-size: $uv-count-down-text-font-size;
+ line-height: $uv-count-down-text-line-height;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-count-down/package.json b/uni_modules/uv-count-down/package.json
new file mode 100644
index 0000000..da501f1
--- /dev/null
+++ b/uni_modules/uv-count-down/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-count-down",
+ "displayName": "uv-count-down 倒计时 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "该倒计时组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。",
+ "keywords": [
+ "uv-count-down",
+ "uvui",
+ "uv-ui",
+ "countDown",
+ "倒计时"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-count-down/readme.md b/uni_modules/uv-count-down/readme.md
new file mode 100644
index 0000000..0f326a8
--- /dev/null
+++ b/uni_modules/uv-count-down/readme.md
@@ -0,0 +1,11 @@
+## CountDown 倒计时
+
+> **组件名:uv-count-down**
+
+该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
+
+### <a href="https://www.uvui.cn/components/countDown.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-count-to/changelog.md b/uni_modules/uv-count-to/changelog.md
new file mode 100644
index 0000000..cf323f9
--- /dev/null
+++ b/uni_modules/uv-count-to/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.4(2023-06-20)
+1. 优化
+## 1.0.3(2023-06-20)
+1. 修复继续滚动的函数
+2. 修复其他
+## 1.0.2(2023-06-20)
+1. 适配px和rpx的单位
+2. 适配customStyle参数
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-count-to 数字滚动
diff --git a/uni_modules/uv-count-to/components/uv-count-to/props.js b/uni_modules/uv-count-to/components/uv-count-to/props.js
new file mode 100644
index 0000000..498371a
--- /dev/null
+++ b/uni_modules/uv-count-to/components/uv-count-to/props.js
@@ -0,0 +1,60 @@
+export default {
+ props: {
+ // 开始的数值,默认从0增长到某一个数
+ startVal: {
+ type: [String, Number],
+ default: 0
+ },
+ // 要滚动的目标数值,必须
+ endVal: {
+ type: [String, Number],
+ default: 0
+ },
+ // 滚动到目标数值的动画持续时间,单位为毫秒(ms)
+ duration: {
+ type: [String, Number],
+ default: 2000
+ },
+ // 设置数值后是否自动开始滚动
+ autoplay: {
+ type: Boolean,
+ default: true
+ },
+ // 要显示的小数位数
+ decimals: {
+ type: [String, Number],
+ default: 0
+ },
+ // 是否在即将到达目标数值的时候,使用缓慢滚动的效果
+ useEasing: {
+ type: Boolean,
+ default: true
+ },
+ // 十进制分割
+ decimal: {
+ type: [String, Number],
+ default: '.'
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: '#606266'
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 22
+ },
+ // 是否加粗字体
+ bold: {
+ type: Boolean,
+ default: false
+ },
+ // 千位分隔符,类似金额的分割(¥23,321.05中的",")
+ separator: {
+ type: String,
+ default: ''
+ },
+ ...uni.$uv?.props?.countTo
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-count-to/components/uv-count-to/uv-count-to.vue b/uni_modules/uv-count-to/components/uv-count-to/uv-count-to.vue
new file mode 100644
index 0000000..5f34167
--- /dev/null
+++ b/uni_modules/uv-count-to/components/uv-count-to/uv-count-to.vue
@@ -0,0 +1,187 @@
+<template>
+ <text
+ class="uv-count-num"
+ :style="[{
+ fontSize: $uv.addUnit(fontSize),
+ fontWeight: bold ? 'bold' : 'normal',
+ color: color
+ },$uv.addStyle(customStyle)]"
+ >{{ displayValue }}</text>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+/**
+ * countTo 数字滚动
+ * @description 该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。
+ * @tutorial https://www.uvui.cn/components/countTo.html
+ * @property {String | Number} startVal 开始的数值,默认从0增长到某一个数(默认 0 )
+ * @property {String | Number} endVal 要滚动的目标数值,必须 (默认 0 )
+ * @property {String | Number} duration 滚动到目标数值的动画持续时间,单位为毫秒(ms) (默认 2000 )
+ * @property {Boolean} autoplay 设置数值后是否自动开始滚动 (默认 true )
+ * @property {String | Number} decimals 要显示的小数位数,见官网说明(默认 0 )
+ * @property {Boolean} useEasing 滚动结束时,是否缓动结尾,见官网说明(默认 true )
+ * @property {String} decimal 十进制分割 ( 默认 "." )
+ * @property {String} color 字体颜色( 默认 '#606266' )
+ * @property {String | Number} fontSize 字体大小,单位px( 默认 22 )
+ * @property {Boolean} bold 字体是否加粗(默认 false )
+ * @property {String} separator 千位分隔符,见官网说明
+ * @event {Function} end 数值滚动到目标值时触发
+ * @example <uv-count-to ref="uCountTo" :end-val="endVal" :autoplay="autoplay"></uv-count-to>
+ */
+export default {
+ name: 'uv-count-to',
+ data() {
+ return {
+ localStartVal: this.startVal,
+ displayValue: this.formatNumber(this.startVal),
+ printVal: null,
+ paused: false, // 是否暂停
+ localDuration: Number(this.duration),
+ startTime: null, // 开始的时间
+ timestamp: null, // 时间戳
+ remaining: null, // 停留的时间
+ rAF: null,
+ lastTime: 0 // 上一次的时间
+ };
+ },
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ countDown() {
+ return this.startVal > this.endVal;
+ }
+ },
+ watch: {
+ startVal() {
+ this.autoplay && this.start();
+ },
+ endVal() {
+ this.autoplay && this.start();
+ }
+ },
+ mounted() {
+ this.autoplay && this.start();
+ },
+ methods: {
+ easingFn(t, b, c, d) {
+ return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b;
+ },
+ requestAnimationFrame(callback) {
+ const currTime = new Date().getTime();
+ // 为了使setTimteout的尽可能的接近每秒60帧的效果
+ const timeToCall = Math.max(0, 16 - (currTime - this.lastTime));
+ const id = setTimeout(() => {
+ callback(currTime + timeToCall);
+ }, timeToCall);
+ this.lastTime = currTime + timeToCall;
+ return id;
+ },
+ cancelAnimationFrame(id) {
+ clearTimeout(id);
+ },
+ // 开始滚动数字
+ start() {
+ this.localStartVal = this.startVal;
+ this.startTime = null;
+ this.localDuration = this.duration;
+ this.paused = false;
+ this.rAF = this.requestAnimationFrame(this.count);
+ },
+ // 暂定状态,从暂停状态开始滚动;或者滚动状态下,暂停
+ restart() {
+ if (this.paused) {
+ this.resume();
+ this.paused = false;
+ } else {
+ this.stop();
+ this.paused = true;
+ }
+ },
+ // 暂停
+ stop() {
+ this.cancelAnimationFrame(this.rAF);
+ this.paused = true;
+ },
+ // 重新开始(暂停的情况下)
+ resume() {
+ if (!this.remaining) return;
+ this.startTime = 0;
+ this.localDuration = this.remaining;
+ this.localStartVal = this.printVal;
+ this.requestAnimationFrame(this.count);
+ },
+ // 重置
+ reset() {
+ this.startTime = null;
+ this.cancelAnimationFrame(this.rAF);
+ this.displayValue = this.formatNumber(this.startVal);
+ },
+ count(timestamp) {
+ if (!this.startTime) this.startTime = timestamp;
+ this.timestamp = timestamp;
+ const progress = timestamp - this.startTime;
+ this.remaining = this.localDuration - progress;
+ if (this.useEasing) {
+ if (this.countDown) {
+ this.printVal = this.localStartVal - this.easingFn(progress, 0, this.localStartVal - this.endVal, this.localDuration);
+ } else {
+ this.printVal = this.easingFn(progress, this.localStartVal, this.endVal - this.localStartVal, this.localDuration);
+ }
+ } else {
+ if (this.countDown) {
+ this.printVal = this.localStartVal - (this.localStartVal - this.endVal) * (progress / this.localDuration);
+ } else {
+ this.printVal = this.localStartVal + (this.endVal - this.localStartVal) * (progress / this.localDuration);
+ }
+ }
+ if (this.countDown) {
+ this.printVal = this.printVal < this.endVal ? this.endVal : this.printVal;
+ } else {
+ this.printVal = this.printVal > this.endVal ? this.endVal : this.printVal;
+ }
+ this.displayValue = this.formatNumber(this.printVal) || 0;
+ if (progress < this.localDuration) {
+ this.rAF = this.requestAnimationFrame(this.count);
+ } else {
+ this.$emit('end');
+ }
+ },
+ // 判断是否数字
+ isNumber(val) {
+ return !isNaN(parseFloat(val));
+ },
+ formatNumber(num) {
+ // 将num转为Number类型,因为其值可能为字符串数值,调用toFixed会报错
+ num = Number(num);
+ num = num.toFixed(Number(this.decimals));
+ num += '';
+ const x = num.split('.');
+ let x1 = x[0];
+ const x2 = x.length > 1 ? `${this.decimal}${x[1]}` : '';
+ const rgx = /(\d+)(\d{3})/;
+ if (this.separator && !this.isNumber(this.separator)) {
+ while (rgx.test(x1)) {
+ x1 = x1.replace(rgx, '$1' + this.separator + '$2');
+ }
+ }
+ return x1 + x2;
+ },
+ destroyed() {
+ this.cancelAnimationFrame(this.rAF);
+ }
+ }
+};
+</script>
+
+<style lang="scss" scoped>
+@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+.uv-count-num {
+ /* #ifndef APP-NVUE */
+ display: inline-flex;
+ /* #endif */
+ text-align: center;
+}
+</style>
diff --git a/uni_modules/uv-count-to/package.json b/uni_modules/uv-count-to/package.json
new file mode 100644
index 0000000..1ed07dc
--- /dev/null
+++ b/uni_modules/uv-count-to/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-count-to",
+ "displayName": "uv-count-to 数字滚动 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.4",
+ "description": "该数字滚动组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值,一种数字上升的视觉冲击效果。",
+ "keywords": [
+ "countTo",
+ "uvui",
+ "uv-ui",
+ "数字滚动",
+ "数字变化"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-count-to/readme.md b/uni_modules/uv-count-to/readme.md
new file mode 100644
index 0000000..5d5f996
--- /dev/null
+++ b/uni_modules/uv-count-to/readme.md
@@ -0,0 +1,11 @@
+## CountTo 数字滚动
+
+> **组件名:uv-count-to**
+
+该组件一般用于需要滚动数字到某一个值的场景,目标要求是一个递增的值。
+
+### <a href="https://www.uvui.cn/components/countTo.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js b/uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js
index b9fc578..6031d84 100644
--- a/uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js
+++ b/uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js
@@ -98,7 +98,7 @@
// 确认按钮的颜色
confirmColor: {
type: String,
- default: '#EE2626'
+ default: '#3c9cff'
},
// 每列中可见选项的数量
visibleItemCount: {
diff --git a/uni_modules/uv-datetime-picker/components/uv-datetime-picker/uv-datetime-picker.vue b/uni_modules/uv-datetime-picker/components/uv-datetime-picker/uv-datetime-picker.vue
index c0431b0..d45c822 100644
--- a/uni_modules/uv-datetime-picker/components/uv-datetime-picker/uv-datetime-picker.vue
+++ b/uni_modules/uv-datetime-picker/components/uv-datetime-picker/uv-datetime-picker.vue
@@ -55,7 +55,7 @@
* @property {String} cancelText 取消按钮的文字 ( 默认 '取消' )
* @property {String} confirmText 确认按钮的文字 ( 默认 '确认' )
* @property {String} cancelColor 取消按钮的颜色 ( 默认 '#909193' )
- * @property {String} confirmColor 确认按钮的颜色 ( 默认 '#EE2626' )
+ * @property {String} confirmColor 确认按钮的颜色 ( 默认 '#3c9cff' )
* @property {String | Number} visibleItemCount 每列中可见选项的数量 ( 默认 5 )
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器 ( 默认 true )
* @property {String | Number} round 圆角 ( 默认 0 )
diff --git a/uni_modules/uv-divider/changelog.md b/uni_modules/uv-divider/changelog.md
new file mode 100644
index 0000000..9ff8c52
--- /dev/null
+++ b/uni_modules/uv-divider/changelog.md
@@ -0,0 +1,11 @@
+## 1.0.4(2023-12-06)
+1. 优化
+## 1.0.3(2023-12-06)
+1. 阻止事件冒泡问题
+## 1.0.2(2023-06-01)
+1. 修复点击触发两次事件的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-divider 分割线
diff --git a/uni_modules/uv-divider/components/uv-divider/props.js b/uni_modules/uv-divider/components/uv-divider/props.js
new file mode 100644
index 0000000..7b97c2c
--- /dev/null
+++ b/uni_modules/uv-divider/components/uv-divider/props.js
@@ -0,0 +1,45 @@
+export default {
+ props: {
+ // 是否虚线
+ dashed: {
+ type: Boolean,
+ default: false
+ },
+ // 是否细线
+ hairline: {
+ type: Boolean,
+ default: true
+ },
+ // 是否以点替代文字,优先于text字段起作用
+ dot: {
+ type: Boolean,
+ default: false
+ },
+ // 内容文本的位置,left-左边,center-中间,right-右边
+ textPosition: {
+ type: String,
+ default: 'center'
+ },
+ // 文本内容
+ text: {
+ type: [String, Number],
+ default: ''
+ },
+ // 文本大小
+ textSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 文本颜色
+ textColor: {
+ type: String,
+ default: '#909399'
+ },
+ // 线条颜色
+ lineColor: {
+ type: String,
+ default: '#dcdfe6'
+ },
+ ...uni.$uv?.props?.divider
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-divider/components/uv-divider/uv-divider.vue b/uni_modules/uv-divider/components/uv-divider/uv-divider.vue
new file mode 100644
index 0000000..ba0dfa7
--- /dev/null
+++ b/uni_modules/uv-divider/components/uv-divider/uv-divider.vue
@@ -0,0 +1,113 @@
+<template>
+ <view
+ class="uv-divider"
+ :style="[$uv.addStyle(customStyle)]"
+ @tap="click"
+ >
+ <uv-line
+ :color="lineColor"
+ :customStyle="leftLineStyle"
+ :hairline="hairline"
+ :dashed="dashed"></uv-line>
+ <text
+ v-if="dot"
+ class="uv-divider__dot"
+ >●</text>
+ <text
+ v-else-if="text"
+ class="uv-divider__text"
+ :style="[textStyle]"
+ >{{text}}</text>
+ <uv-line
+ :color="lineColor"
+ :customStyle="rightLineStyle"
+ :hairline="hairline"
+ :dashed="dashed"
+ ></uv-line>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * divider 分割线
+ * @description 区隔内容的分割线,一般用于页面底部"没有更多"的提示。
+ * @tutorial https://www.uvui.cn/components/divider.html
+ * @property {Boolean} dashed 是否虚线 (默认 false )
+ * @property {Boolean} hairline 是否细线 (默认 true )
+ * @property {Boolean} dot 是否以点替代文字,优先于text字段起作用 (默认 false )
+ * @property {String} textPosition 内容文本的位置,left-左边,center-中间,right-右边 (默认 'center' )
+ * @property {String | Number} text 文本内容
+ * @property {String | Number} textSize 文本大小 (默认 14)
+ * @property {String} textColor 文本颜色 (默认 '#909399' )
+ * @property {String} lineColor 线条颜色 (默认 '#dcdfe6' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} click divider组件被点击时触发
+ * @example <uv-divider :color="color">锦瑟无端五十弦</uv-divider>
+ */
+ export default {
+ name: 'uv-divider',
+ mixins: [mpMixin, mixin, props],
+ emits: ['click'],
+ computed: {
+ textStyle() {
+ const style = {}
+ style.fontSize = this.$uv.addUnit(this.textSize)
+ style.color = this.textColor
+ return style
+ },
+ // 左边线条的的样式
+ leftLineStyle() {
+ const style = {}
+ // 如果是在左边,设置左边的宽度为固定值
+ if (this.textPosition === 'left') {
+ style.width = '80rpx'
+ } else {
+ style.flex = 1
+ }
+ return style
+ },
+ // 右边线条的的样式
+ rightLineStyle() {
+ const style = {}
+ // 如果是在右边,设置右边的宽度为固定值
+ if (this.textPosition === 'right') {
+ style.width = '80rpx'
+ } else {
+ style.flex = 1
+ }
+ return style
+ }
+ },
+ methods: {
+ // divider组件被点击时触发
+ click() {
+ this.$emit('click');
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ $uv-divider-margin: 15px 0 !default;
+ $uv-divider-text-margin: 0 15px !default;
+ $uv-divider-dot-font-size: 12px !default;
+ $uv-divider-dot-margin: 0 12px !default;
+ $uv-divider-dot-color: #c0c4cc !default;
+ .uv-divider {
+ @include flex;
+ flex-direction: row;
+ align-items: center;
+ margin: $uv-divider-margin;
+ &__text {
+ margin: $uv-divider-text-margin;
+ }
+ &__dot {
+ font-size: $uv-divider-dot-font-size;
+ margin: $uv-divider-dot-margin;
+ color: $uv-divider-dot-color;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-divider/package.json b/uni_modules/uv-divider/package.json
new file mode 100644
index 0000000..a54b458
--- /dev/null
+++ b/uni_modules/uv-divider/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-divider",
+ "displayName": "uv-divider 分割线 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.4",
+ "description": "区隔内容的分割线,一般用于页面底部没有更多的提示。",
+ "keywords": [
+ "divider",
+ "uvui",
+ "uv-ui",
+ "分割线",
+ "没有更多"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-line"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-divider/readme.md b/uni_modules/uv-divider/readme.md
new file mode 100644
index 0000000..2384cba
--- /dev/null
+++ b/uni_modules/uv-divider/readme.md
@@ -0,0 +1,11 @@
+## Divider 分割线
+
+> **组件名:uv-divider**
+
+区隔内容的分割线,一般用于页面底部"没有更多"的提示。
+
+### <a href="https://www.uvui.cn/components/divider.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-drop-down/changelog.md b/uni_modules/uv-drop-down/changelog.md
new file mode 100644
index 0000000..637c605
--- /dev/null
+++ b/uni_modules/uv-drop-down/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.5(2024-01-02)
+1. 修复parentData不变的BUG
+## 1.0.4(2023-09-28)
+1. 增加uv-sticky依赖
+## 1.0.3(2023-08-29)
+1. 修复自定义内容,点击自定义内容时会自动关闭弹窗的问题
+## 1.0.2(2023-08-22)
+1. 优化
+## 1.0.1(2023-08-22)
+1. 增加@change回调,返回弹窗关闭状态
+2. 增加init方法,方便位置改变进行调整
+## 1.0.0(2023-07-30)
+新增uv-drop-down 下拉筛选组件
diff --git a/uni_modules/uv-drop-down/components/uv-drop-down-item/uv-drop-down-item.vue b/uni_modules/uv-drop-down/components/uv-drop-down-item/uv-drop-down-item.vue
new file mode 100644
index 0000000..9438a57
--- /dev/null
+++ b/uni_modules/uv-drop-down/components/uv-drop-down-item/uv-drop-down-item.vue
@@ -0,0 +1,169 @@
+<template>
+ <view class="uv-drop-down-item" @click="clickHandler">
+ <uv-text :text="label" :size="getTextStyle.size" :color="getTextStyle.color" lines="1" :custom-style="{marginRight: '10rpx',maxWidth:'200rpx'}"></uv-text>
+ <uv-icon :name="getDownIcon.name" :size="getDownIcon.size" :color="getDownIcon.color" v-if="[1,'1'].indexOf(type)==-1"></uv-icon>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+ /**
+ * DropDown 下拉框
+ * @description 下拉筛选
+ * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down
+ * @property {String | Number} name 字段标识
+ * @property {String | Number} type 类型 1 没有筛选项,直接进行选中和不选中 2 有多个选项
+ * @property {String | Number} label 筛选项的文本
+ * @property {Boolean} isDrop 该项是否打开
+ */
+ export default {
+ name: 'uv-drop-down-item',
+ mixins: [mpMixin, mixin],
+ emits: ['click'],
+ props: {
+ name: {
+ type: [String, Number],
+ default: ''
+ },
+ // 类型 1 没有筛选项,直接进行选中和不选中 2 有多个选项
+ type: {
+ type: [String, Number],
+ default: '2'
+ },
+ // 筛选的文本
+ label: {
+ type: [String],
+ default: ''
+ },
+ // 筛选值
+ value: {
+ type: [String, Number, null],
+ default: ''
+ },
+ // 是否下拉菜单打开
+ isDrop: {
+ type: Boolean,
+ default: false
+ }
+ },
+ data() {
+ return {
+ parentData: {
+ defaultValue: [0, '0', 'all'],
+ textSize: '30rpx',
+ textColor: '#333',
+ textActiveSize: '30rpx',
+ textActiveColor: '#3c9cff',
+ extraIcon: {},
+ extraActiveIcon: {},
+ sign: '',
+ clickHandler: Function
+ },
+ active: false,
+ isDroped: false,
+ elId: ''
+ }
+ },
+ watch: {
+ isDrop: {
+ handler(newVal) {
+ this.isDroped = newVal;
+ },
+ immediate: true
+ },
+ value: {
+ handler(newVal) {
+ this.$nextTick(()=>{
+ this.active = this.parentData.defaultValue.indexOf(newVal) == -1;
+ })
+ },
+ immediate: true
+ }
+ },
+ computed: {
+ getDownIcon() {
+ const style = {
+ size: '30rpx',
+ color: '#333',
+ ...this.parentData.extraIcon
+ }
+ if (this.active || this.isDroped) {
+ style.color = this.parentData.extraActiveIcon?.color ? this.parentData.extraActiveIcon?.color : '#3c9cff';
+ style.size = this.parentData.extraActiveIcon?.size ? this.parentData.extraActiveIcon?.size : '30rpx';
+ }
+ if (this.isDroped) {
+ style.name = this.parentData.extraActiveIcon?.name;
+ }
+ return style;
+ },
+ getTextStyle() {
+ const style = {
+ size: this.parentData.textSize,
+ color: this.parentData.textColor
+ };
+ if (this.active || this.isDroped) {
+ style.size = this.parentData.textActiveSize;
+ style.color = this.parentData.textActiveColor;
+ }
+ return style;
+ }
+ },
+ created() {
+ this.init();
+ },
+ methods: {
+ init() {
+ this.elId = this.$uv.guid();
+ this.getParentData('uv-drop-down');
+ if (!this.parent) {
+ this.$uv.error('uv-drop-down必须搭配uv-drop-down-item组件使用');
+ }
+ uni.$on('HANDLE_DROPDOWN_ONE', id => {
+ if (this.isDroped && this.elId != id) {
+ this.isDroped = false;
+ }
+ })
+ uni.$on(`${this.parentData.sign}_CLOSEPOPUP`, async () => {
+ if (this.isDroped) {
+ this.isDroped = false;
+ }
+ })
+ },
+ async clickHandler() {
+ let data = {};
+ uni.$emit('HANDLE_DROPDOWN_ONE', this.elId);
+ switch (+this.type) {
+ case 1:
+ this.active = !this.active;
+ data = {
+ name: this.name,
+ active: this.active,
+ type: this.type
+ };
+ break;
+ case 2:
+ this.isDroped = !this.isDroped;
+ data = {
+ name: this.name,
+ active: this.isDroped,
+ type: this.type
+ };
+ break;
+ }
+ this.parentData.clickHandler(data);
+ this.$emit('click', data);
+ uni.$emit(`${this.parentData.sign}_CLICKMENU`, {
+ show: +this.type > 1 && this.isDroped
+ });
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-drop-down-item {
+ @include flex;
+ align-items: center;
+ padding: 20rpx;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-drop-down/components/uv-drop-down-popup/uv-drop-down-popup.vue b/uni_modules/uv-drop-down/components/uv-drop-down-popup/uv-drop-down-popup.vue
new file mode 100644
index 0000000..6a1efc8
--- /dev/null
+++ b/uni_modules/uv-drop-down/components/uv-drop-down-popup/uv-drop-down-popup.vue
@@ -0,0 +1,242 @@
+<template>
+ <view class="uv-drop-down-popup">
+ <uv-transition :show="show" mode="fade" :duration="300" :custom-style="overlayStyle" @click="clickOverlay">
+ <view class="uv-dp__container" ref="uvDPContainer" :style="{height: `${height}px`}" @click.stop="blockClick">
+ <view class="uv-dp__container__list" ref="uvDPList">
+ <slot>
+ <view class="uv-dp__container__list--item" v-for="(item,index) in list" :key="index" @click="clickHandler(item,index)" :style="[itemCustomStyle(index)]">
+ <uv-text :text="item[keyName]" :size="getTextSize(index)" :color="getTextColor(index)"></uv-text>
+ </view>
+ </slot>
+ </view>
+ </view>
+ </uv-transition>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+ // #ifdef APP-NVUE
+ const animation = uni.requireNativePlugin('animation');
+ const dom = uni.requireNativePlugin('dom');
+ // #endif
+ /**
+ * DropDownPopup 下拉框
+ * @description 下拉筛选框
+ * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down
+ * @property {String | Number} name 字段标识
+ * @property {String | Number} zIndex 弹出层的层级
+ * @property {String | Number} opacity 遮罩层的透明度
+ * @property {Boolean} clickOverlayOnClose 是否允许点击遮罩层关闭弹窗
+ * @property {Object} currentDropItem 当前下拉筛选菜单对象
+ * @property {String} keyName 指定从当前下拉筛选菜单对象元素中读取哪个属性作为文本展示
+ */
+ export default {
+ name: 'uv-drop-down-popup',
+ mixins: [mpMixin, mixin],
+ props: {
+ sign: {
+ type: [String, Number],
+ default: 'UVDROPDOWN'
+ },
+ zIndex: {
+ type: [Number, String],
+ default: 999
+ },
+ opacity: {
+ type: [Number, String],
+ default: 0.5
+ },
+ clickOverlayOnClose: {
+ type: Boolean,
+ default: true
+ },
+ // 当前下拉选项对象
+ currentDropItem: {
+ type: Object,
+ default () {
+ return {
+ activeIndex: 0,
+ child: []
+ }
+ }
+ },
+ keyName: {
+ type: String,
+ default: 'label'
+ }
+ },
+ data() {
+ return {
+ show: false,
+ rect: {},
+ height: 0
+ }
+ },
+ computed: {
+ overlayStyle() {
+ let { height = 0, top = 0 } = this.rect;
+ // #ifdef H5
+ top += this.$uv.sys().windowTop;
+ // #endif
+ const style = {
+ position: 'fixed',
+ top: `${top+height}px`,
+ left: 0,
+ right: 0,
+ zIndex: this.zIndex,
+ bottom: 0,
+ 'background-color': `rgba(0, 0, 0, ${this.opacity})`
+ }
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ },
+ list() {
+ try {
+ return Array.isArray(this.currentDropItem.child) ? this.currentDropItem.child : [];
+ } catch (e) {
+ return [];
+ }
+ },
+ getTextColor(index) {
+ return index => {
+ const active = this.currentDropItem.activeIndex == index;
+ const color = this.currentDropItem.color;
+ const activeColor = this.currentDropItem.activeColor;
+ if (active) {
+ return activeColor ? activeColor : '#3c9cff';
+ }
+ return color ? color : '#333';
+ }
+ },
+ getTextSize(index) {
+ return index => {
+ const active = this.currentDropItem.activeIndex == index;
+ const size = this.currentDropItem.size;
+ const activeSize = this.currentDropItem.activeSize;
+ if (active) {
+ return activeSize ? activeSize : '30rpx';
+ }
+ return size ? size : '30rpx';
+ }
+ },
+ itemCustomStyle() {
+ return index => {
+ const active = this.currentDropItem.activeIndex == index;
+ const style = {};
+ if (active && this.currentDropItem.itemActiveCustomStyle) {
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemActiveCustomStyle));
+ }
+ if (this.currentDropItem.itemCustomStyle) {
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.currentDropItem.itemCustomStyle))
+ }
+ return style;
+ }
+ }
+ },
+ created() {
+ this.init();
+ },
+ methods: {
+ blockClick() {},
+ clickHandler(item, index) {
+ this.currentDropItem.activeIndex = index;
+ this.$emit('clickItem', item);
+ this.close();
+ },
+ init() {
+ uni.$off(`${this.sign}_GETRECT`);
+ uni.$on(`${this.sign}_GETRECT`, rect => {
+ this.rect = rect;
+ })
+ uni.$off(`${this.sign}_CLICKMENU`);
+ uni.$on(`${this.sign}_CLICKMENU`, async res => {
+ if (res.show) {
+ this.open();
+ } else {
+ this.close();
+ }
+ })
+ },
+ open() {
+ this.show = true;
+ this.$nextTick(async () => {
+ // #ifndef H5 || MP-WEIXIN
+ await this.$uv.sleep(60);
+ // #endif
+ const res = await this.queryRect();
+ // #ifndef APP-NVUE
+ this.height = res.height;
+ // #endif
+ // #ifdef APP-NVUE
+ this.animation(res.height);
+ // #endif
+ this.$emit('popupChange', { show: true });
+ })
+ },
+ close() {
+ if(!this.show) return;
+ this.height = 0;
+ // #ifndef APP-NVUE
+ this.height = 0;
+ // #endif
+ // #ifdef APP-NVUE
+ this.animation(0);
+ // #endif
+ this.show = false;
+ uni.$emit(`${this.sign}_CLOSEPOPUP`);
+ this.$emit('popupChange', { show: false });
+ },
+ clickOverlay() {
+ if (this.clickOverlayOnClose) {
+ this.close();
+ }
+ },
+ // 查询内容高度
+ queryRect() {
+ // #ifndef APP-NVUE
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`.uv-dp__container__list`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs.uvDPList, res => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ // nvue下设置高度
+ animation(height, duration = 200) {
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uvDPContainer'];
+ animation.transition(ref, {
+ styles: {
+ height: `${height}px`
+ },
+ duration
+ })
+ // #endif
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ .uv-dp__container {
+ /* #ifndef APP-NVUE */
+ overflow: hidden;
+ transition: all .15s;
+ /* #endif */
+ background-color: #fff;
+ }
+ .uv-dp__container__list {
+ &--item {
+ padding: 20rpx 60rpx;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-drop-down/components/uv-drop-down/uv-drop-down.vue b/uni_modules/uv-drop-down/components/uv-drop-down/uv-drop-down.vue
new file mode 100644
index 0000000..9978b40
--- /dev/null
+++ b/uni_modules/uv-drop-down/components/uv-drop-down/uv-drop-down.vue
@@ -0,0 +1,135 @@
+<template>
+ <uv-sticky :disabled="!isSticky">
+ <view ref="dropDownRef" class="uv-drop-down" :style="[$uv.addStyle(customStyle)]">
+ <slot></slot>
+ </view>
+ </uv-sticky>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom');
+ // #endif
+ /**
+ * DropDown 下拉框
+ * @description 下拉筛选
+ * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-drop-down
+ * @property {String | Number} sign 组件唯一标识,需要手动传
+ * @property {Boolean} is-sticky = [true|false] 是否吸顶
+ * @property {Array} default-value 默认值,表示参数value属于这里面的值,就说明是未选中即是默认展示的值。eg:上面示例中的{label: '全部',value: 'all'} 即是默认值。后续处理逻辑也可以根据是否是其中值进行过滤。
+ * @property {String} textSize 每项字体大小
+ * @property {String} textColor 每项文本颜色
+ * @property {String} textActiveSize 每项选中状态字体大小
+ * @property {String} textActiveColor 每项选中状态文本颜色
+ * @property {Object} extraIcon 每项右侧图标
+ * @property {Object} extraActiveIcon 每项选中后右侧图标
+ */
+ export default {
+ name: 'uv-drop-down',
+ mixins: [mpMixin, mixin],
+ emits: ['click'],
+ props: {
+ isSticky: {
+ type: Boolean,
+ default: true
+ },
+ sign: {
+ type: [String, Number],
+ default: 'UVDROPDOWN'
+ },
+ defaultValue: {
+ type: Array,
+ default: () => [0, '0', 'all']
+ },
+ textSize: {
+ type: String,
+ default: '30rpx'
+ },
+ textColor: {
+ type: String,
+ default: '#333'
+ },
+ textActiveSize: {
+ type: String,
+ default: '30rpx'
+ },
+ textActiveColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ extraIcon: {
+ type: Object,
+ default () {
+ return {
+ name: 'arrow-down',
+ size: '30rpx',
+ color: '#333'
+ }
+ }
+ },
+ extraActiveIcon: {
+ type: Object,
+ default () {
+ return {
+ name: 'arrow-up',
+ size: '30rpx',
+ color: '#3c9cff'
+ }
+ }
+ }
+ },
+ computed: {
+ parentData() {
+ return [this.defaultValue, this.textSize, this.textColor, this.textActiveColor, this.textActiveSize, this.extraIcon, this.extraActiveIcon, this.sign, this.clickHandler];
+ }
+ },
+ mounted() {
+ this.init();
+ },
+ methods: {
+ init() {
+ uni.$emit(`${this.sign}_CLICKMENU`, {
+ show: false
+ });
+ this.$nextTick(async () => {
+ const rect = await this.queryRect();
+ uni.$emit(`${this.sign}_GETRECT`, rect);
+ })
+ },
+ // 查询内容高度
+ queryRect() {
+ // #ifndef APP-NVUE
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`.uv-drop-down`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs.dropDownRef, res => {
+ res.size.top = res.size.top <= 0 ? 0 : res.size.top;
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ clickHandler(data) {
+ this.$emit('click', data);
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-drop-down {
+ @include flex;
+ justify-content: space-between;
+ background-color: #fff;
+ border-bottom: 1px solid #dadbde;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-drop-down/package.json b/uni_modules/uv-drop-down/package.json
new file mode 100644
index 0000000..f4ad683
--- /dev/null
+++ b/uni_modules/uv-drop-down/package.json
@@ -0,0 +1,91 @@
+{
+ "id": "uv-drop-down",
+ "displayName": "uv-drop-down 下拉筛选 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "该组件主要提供筛选下拉筛选框,内置基础筛选功能,可以根据自己的需求自定义筛选项",
+ "keywords": [
+ "uv-drop-down",
+ "uvui",
+ "uv-ui",
+ "下拉筛选",
+ "筛选"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-text",
+ "uv-transition",
+ "uv-sticky"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-drop-down/readme.md b/uni_modules/uv-drop-down/readme.md
new file mode 100644
index 0000000..31cbd24
--- /dev/null
+++ b/uni_modules/uv-drop-down/readme.md
@@ -0,0 +1,23 @@
+## DropDown 下拉筛选
+
+> **组件名:uv-drop-down**
+
+该组件主要提供筛选下拉筛选框,内置基础筛选功能,可以根据自己的需求自定义筛选项。
+
+为了兼容app-nvue,需要内置三个组件进行配合使用,uv-drop-down属于菜单项(其实还包括子组件uv-drop-down-item),uv-drop-down-popup属于筛选框。
+
+只需要做简单的约定式配置,即可使用该功能,兼容性良好,已经在多端进行了多次测试。
+
+# <a href="https://www.uvui.cn/components/dropDown.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-empty/changelog.md b/uni_modules/uv-empty/changelog.md
new file mode 100644
index 0000000..a1cc2ad
--- /dev/null
+++ b/uni_modules/uv-empty/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.5(2023-12-20)
+1. 优化
+## 1.0.4(2023-08-04)
+1. icon支持base64图片
+## 1.0.3(2023-07-17)
+1. 修复 uv-empty 恢复设置mode属性的内置图标
+## 1.0.2(2023-07-03)
+去除插槽判断,避免某些平台不显示的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-empty 内容为空
diff --git a/uni_modules/uv-empty/components/uv-empty/props.js b/uni_modules/uv-empty/components/uv-empty/props.js
new file mode 100644
index 0000000..26c282d
--- /dev/null
+++ b/uni_modules/uv-empty/components/uv-empty/props.js
@@ -0,0 +1,60 @@
+export default {
+ props: {
+ // 内置图标名称,或图片路径,建议绝对路径
+ icon: {
+ type: String,
+ default: ''
+ },
+ // 提示文字
+ text: {
+ type: String,
+ default: ''
+ },
+ // 文字颜色
+ textColor: {
+ type: String,
+ default: '#c0c4cc'
+ },
+ // 文字大小
+ textSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 图标的颜色
+ iconColor: {
+ type: String,
+ default: '#c0c4cc'
+ },
+ // 图标的大小
+ iconSize: {
+ type: [String, Number],
+ default: 90
+ },
+ // 选择预置的图标类型
+ mode: {
+ type: String,
+ default: 'data'
+ },
+ // 图标宽度,单位px
+ width: {
+ type: [String, Number],
+ default: 160
+ },
+ // 图标高度,单位px
+ height: {
+ type: [String, Number],
+ default: 160
+ },
+ // 是否显示组件
+ show: {
+ type: Boolean,
+ default: true
+ },
+ // 组件距离上一个元素之间的距离,默认px单位
+ marginTop: {
+ type: [String, Number],
+ default: 0
+ },
+ ...uni.$uv?.props?.empty
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-empty/components/uv-empty/uv-empty.vue b/uni_modules/uv-empty/components/uv-empty/uv-empty.vue
new file mode 100644
index 0000000..22a9264
--- /dev/null
+++ b/uni_modules/uv-empty/components/uv-empty/uv-empty.vue
@@ -0,0 +1,126 @@
+<template>
+ <view
+ class="uv-empty"
+ :style="[emptyStyle]"
+ v-if="show"
+ >
+ <uv-icon
+ v-if="!isImg"
+ :name="mode === 'message' ? 'chat' : `empty-${mode}`"
+ :size="iconSize"
+ :color="iconColor"
+ margin-top="14"
+ ></uv-icon>
+ <image
+ v-else
+ :style="{
+ width: $uv.addUnit(width),
+ height: $uv.addUnit(height)
+ }"
+ :src="icon"
+ mode="widthFix"
+ ></image>
+ <text
+ class="uv-empty__text"
+ :style="[textStyle]"
+ >{{text ? text : icons[mode]}}</text>
+ <view class="uv-empty__wrap">
+ <slot />
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * empty 内容为空
+ * @description 该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。
+ * @tutorial https://www.uvui.cn/components/empty.html
+ * @property {String} icon 内置图标名称,或图片路径,建议绝对路径
+ * @property {String} text 提示文字
+ * @property {String} textColor 文字颜色 (默认 '#c0c4cc' )
+ * @property {String | Number} textSize 文字大小 (默认 14 )
+ * @property {String} iconColor 图标的颜色 (默认 '#c0c4cc' )
+ * @property {String | Number} iconSize 图标的大小 (默认 90 )
+ * @property {String} mode 选择预置的图标类型 (默认 'data' )
+ * @property {String | Number} width 图标宽度,单位px (默认 160 )
+ * @property {String | Number} height 图标高度,单位px (默认 160 )
+ * @property {Boolean} show 是否显示组件 (默认 true )
+ * @property {String | Number} marginTop 组件距离上一个元素之间的距离,默认px单位 (默认 0 )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} click 点击组件时触发
+ * @event {Function} close 点击关闭按钮时触发
+ * @example <uv-empty text="所谓伊人,在水一方" mode="list"></uv-empty>
+ */
+ export default {
+ name: "uv-empty",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ icons: {
+ car: '购物车为空',
+ page: '页面不存在',
+ search: '没有搜索结果',
+ address: '没有收货地址',
+ 'wifi-off': '没有WiFi',
+ order: '订单为空',
+ coupon: '没有优惠券',
+ favor: '暂无收藏',
+ permission: '无权限',
+ history: '无历史记录',
+ news: '无新闻列表',
+ message: '消息列表为空',
+ list: '列表为空',
+ data: '数据为空',
+ comment: '暂无评论',
+ }
+ }
+ },
+ computed: {
+ // 组件样式
+ emptyStyle() {
+ const style = {}
+ style.marginTop = this.$uv.addUnit(this.marginTop)
+ // 合并customStyle样式,此参数通过mixin中的props传递
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ },
+ // 文本样式
+ textStyle() {
+ const style = {}
+ style.color = this.textColor
+ style.fontSize = this.$uv.addUnit(this.textSize)
+ return style
+ },
+ // 判断icon是否图片路径
+ isImg() {
+ const isBase64 = this.icon.indexOf('data:') > -1 && this.icon.indexOf('base64') > -1;
+ return this.icon.indexOf('/') !== -1 || isBase64;
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ $uv-empty-text-margin-top: 20rpx !default;
+ $uv-empty-slot-margin-top: 20rpx !default;
+ .uv-empty {
+ @include flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ &__text {
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: $uv-empty-text-margin-top;
+ }
+ }
+ .uv-slot-wrap {
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: $uv-empty-slot-margin-top;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-empty/package.json b/uni_modules/uv-empty/package.json
new file mode 100644
index 0000000..e10f451
--- /dev/null
+++ b/uni_modules/uv-empty/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-empty",
+ "displayName": "uv-empty 内容为空 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个 没有内容 的场景, 我们精心挑选了十几个场景的图标,方便您使用。",
+ "keywords": [
+ "empty",
+ "uvui",
+ "uv-ui",
+ "空数据",
+ "暂无数据"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-empty/readme.md b/uni_modules/uv-empty/readme.md
new file mode 100644
index 0000000..ecef14d
--- /dev/null
+++ b/uni_modules/uv-empty/readme.md
@@ -0,0 +1,19 @@
+## Empty 内容为空
+
+> **组件名:uv-empty**
+
+该组件用于需要加载内容,但是加载的第一页数据就为空,提示一个"没有内容"的场景, 我们精心挑选了十几个场景的图标,方便您使用。
+
+# <a href="https://www.uvui.cn/components/empty.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-gap/changelog.md b/uni_modules/uv-gap/changelog.md
index 06b12db..7d603b4 100644
--- a/uni_modules/uv-gap/changelog.md
+++ b/uni_modules/uv-gap/changelog.md
@@ -1,4 +1,4 @@
-## 1.0.1(20-05-16)
+## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
diff --git a/uni_modules/uv-grid/changelog.md b/uni_modules/uv-grid/changelog.md
new file mode 100644
index 0000000..8c61261
--- /dev/null
+++ b/uni_modules/uv-grid/changelog.md
@@ -0,0 +1,21 @@
+## 1.0.9(2023-12-06)
+1. 优化
+## 1.0.8(2023-12-06)
+1. 阻止事件冒泡问题
+## 1.0.7(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.6(2023-08-14)
+1. 修复初始的时候闪动的BUG
+## 1.0.5(2023-06-22)
+1. 优化修改
+## 1.0.4(2023-06-21)
+1. 修复BUG
+## 1.0.3(2023-06-01)
+1. 修复点击触发两次事件的BUG
+## 1.0.2(2023-05-23)
+1. 优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-grid 宫格布局
diff --git a/uni_modules/uv-grid/components/uv-grid-item/props.js b/uni_modules/uv-grid/components/uv-grid-item/props.js
new file mode 100644
index 0000000..6b86298
--- /dev/null
+++ b/uni_modules/uv-grid/components/uv-grid-item/props.js
@@ -0,0 +1,15 @@
+export default {
+ props: {
+ // 宫格的name
+ name: {
+ type: [String, Number, null],
+ default: null
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: 'transparent'
+ },
+ ...uni.$uv?.props?.gridItem
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-grid/components/uv-grid-item/uv-grid-item.vue b/uni_modules/uv-grid/components/uv-grid-item/uv-grid-item.vue
new file mode 100644
index 0000000..9412852
--- /dev/null
+++ b/uni_modules/uv-grid/components/uv-grid-item/uv-grid-item.vue
@@ -0,0 +1,226 @@
+<template>
+ <!-- #ifndef APP-NVUE -->
+ <view
+ class="uv-grid-item"
+ hover-class="uv-grid-item--hover-class"
+ :hover-stay-time="200"
+ @tap="clickHandler"
+ :class="classes"
+ :style="[itemStyle]"
+ >
+ <slot />
+ </view>
+ <!-- #endif -->
+ <!-- #ifdef APP-NVUE -->
+ <view
+ class="uv-grid-item"
+ :hover-stay-time="200"
+ @tap="clickHandler"
+ :class="classes"
+ :style="[itemStyle]"
+ >
+ <slot />
+ </view>
+ <!-- #endif -->
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * gridItem 提示
+ * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。搭配uv-grid使用
+ * @tutorial https://www.uvui.cn/components/grid.html
+ * @property {String | Number} name 宫格的name ( 默认 null )
+ * @property {String} bgColor 宫格的背景颜色 (默认 'transparent' )
+ * @property {Object} customStyle 自定义样式,对象形式
+ * @event {Function} click 点击宫格触发
+ * @example <uv-grid-item></uv-grid-item>
+ */
+ export default {
+ name: "uv-grid-item",
+ mixins: [mpMixin, mixin, props],
+ emits: ['$uvGridItem','click'],
+ data() {
+ return {
+ parentData: {
+ col: 3, // 父组件划分的宫格数
+ border: true, // 是否显示边框,根据父组件决定
+ },
+ // #ifdef APP-NVUE
+ width: 0, // nvue下才这么计算,vue下放到computed中,否则会因为延时造成闪烁
+ // #endif
+ classes: [], // 类名集合,用于判断是否显示右边和下边框
+ };
+ },
+ created() {
+ // 父组件的实例
+ this.updateParentData()
+ },
+ mounted() {
+ this.init()
+ },
+ computed: {
+ // #ifndef APP-NVUE
+ // vue下放到computed中,否则会因为延时造成闪烁
+ width() {
+ return 100 / Number(this.parentData.col) + '%'
+ },
+ // #endif
+ itemStyle() {
+ const style = {
+ background: this.bgColor,
+ width: this.width
+ }
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ }
+ },
+ methods: {
+ init() {
+ // 用于在父组件uv-grid的children中被添加入子组件时,
+ // 重新计算item的边框
+ uni.$on('$uvGridItem', () => {
+ this.gridItemClasses()
+ })
+ // #ifdef APP-NVUE
+ // 获取元素该有的长度,nvue下要延时才准确
+ this.$nextTick(function(){
+ this.getItemWidth()
+ })
+ // #endif
+ // 发出事件,通知所有的grid-item都重新计算自己的边框
+ uni.$emit('$uvGridItem')
+ this.gridItemClasses()
+ },
+ // 获取父组件的参数
+ updateParentData() {
+ // 此方法写在mixin中
+ this.getParentData('uv-grid');
+ },
+ clickHandler() {
+ let name = this.name
+ // 如果没有设置name属性,历遍父组件的children数组,判断当前的元素是否和本实例this相等,找出当前组件的索引
+ const children = this.parent?.children
+ if(children && this.name === null) {
+ name = children.findIndex(child => child === this)
+ }
+ // 调用父组件方法,发出事件
+ this.parent && this.parent.childClick(name)
+ this.$emit('click', name)
+ },
+ async getItemWidth() {
+ // 如果是nvue,不能使用百分比,只能使用固定宽度
+ let width = 0
+ if(this.parent) {
+ // 获取父组件宽度后,除以栅格数,得出每个item的宽度
+ const parentWidth = await this.getParentWidth()
+ width = parentWidth / Number(this.parentData.col) + 'px'
+ }
+ this.width = width
+ },
+ // 获取父元素的尺寸
+ getParentWidth() {
+ // #ifdef APP-NVUE
+ // 返回一个promise,让调用者可以用await同步获取
+ const dom = uni.requireNativePlugin('dom')
+ return new Promise(resolve => {
+ // 调用父组件的ref
+ dom.getComponentRect(this.parent.$refs['uv-grid'], res => {
+ resolve(res.size.width)
+ })
+ })
+ // #endif
+ },
+ gridItemClasses() {
+ if(this.parentData.border) {
+ let classes = []
+ this.parent.children.map((child, index) =>{
+ if(this === child) {
+ const len = this.parent.children.length
+ // 贴近右边屏幕边沿的child,并且最后一个(比如只有横向2个的时候),无需右边框
+ if((index + 1) % this.parentData.col !== 0 && index + 1 !== len) {
+ classes.push('uv-border-right')
+ }
+ // 总的宫格数量对列数取余的值
+ // 如果取余后,值为0,则意味着要将最后一排的宫格,都不需要下边框
+ const lessNum = len % this.parentData.col === 0 ? this.parentData.col : len % this.parentData.col
+ // 最下面的一排child,无需下边框
+ if(index < len - lessNum) {
+ classes.push('uv-border-bottom')
+ }
+ }
+ })
+ // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+ // #ifdef MP-ALIPAY || MP-TOUTIAO
+ classes = classes.join(' ')
+ // #endif
+ this.classes = classes
+ }
+ }
+ },
+ // #ifdef VUE2
+ beforeDestroy() {
+ // 移除事件监听,释放性能
+ uni.$off('$uvGridItem')
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ // 移除事件监听,释放性能
+ uni.$off('$uvGridItem')
+ }
+ // #endif
+ };
+</script>
+
+<style lang="scss" scoped>
+ $show-border: 1;
+ $show-border-right: 1;
+ $show-border-bottom: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-grid-item-hover-class-opcatiy:.5 !default;
+ $uv-grid-item-margin-top:1rpx !default;
+ $uv-grid-item-border-right-width:0.5px !default;
+ $uv-grid-item-border-bottom-width:0.5px !default;
+ $uv-grid-item-border-right-color:$uv-border-color !default;
+ $uv-grid-item-border-bottom-color:$uv-border-color !default;
+ .uv-grid-item {
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ flex-direction: column;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ display: flex;
+ /* #endif */
+
+ /* #ifdef MP */
+ position: relative;
+ float: left;
+ /* #endif */
+
+ /* #ifdef MP-WEIXIN */
+ margin-top:$uv-grid-item-margin-top;
+ /* #endif */
+
+ &--hover-class {
+ opacity:$uv-grid-item-hover-class-opcatiy;
+ }
+ }
+
+ /* #ifdef APP-NVUE */
+ // 由于nvue不支持组件内引入app.vue中再引入的样式,所以需要写在这里
+ .uv-border-right {
+ border-right-width:$uv-grid-item-border-right-width;
+ border-color: $uv-grid-item-border-right-color;
+ }
+
+ .uv-border-bottom {
+ border-bottom-width:$uv-grid-item-border-bottom-width;
+ border-color:$uv-grid-item-border-bottom-color;
+ }
+ /* #endif */
+</style>
diff --git a/uni_modules/uv-grid/components/uv-grid/props.js b/uni_modules/uv-grid/components/uv-grid/props.js
new file mode 100644
index 0000000..d5f8474
--- /dev/null
+++ b/uni_modules/uv-grid/components/uv-grid/props.js
@@ -0,0 +1,20 @@
+export default {
+ props: {
+ // 分成几列
+ col: {
+ type: [String, Number],
+ default: 3
+ },
+ // 是否显示边框
+ border: {
+ type: Boolean,
+ default: false
+ },
+ // 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右
+ align: {
+ type: String,
+ default: 'left'
+ },
+ ...uni.$uv?.props?.grid
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-grid/components/uv-grid/uv-grid.vue b/uni_modules/uv-grid/components/uv-grid/uv-grid.vue
new file mode 100644
index 0000000..0314b18
--- /dev/null
+++ b/uni_modules/uv-grid/components/uv-grid/uv-grid.vue
@@ -0,0 +1,100 @@
+<template>
+ <view
+ class="uv-grid"
+ ref='uv-grid'
+ :style="[gridStyle]"
+ >
+ <slot />
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * grid 宫格布局
+ * @description 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。
+ * @tutorial https://www.uvui.cn/components/grid.html
+ * @property {String | Number} col 宫格的列数(默认 3 )
+ * @property {Boolean} border 是否显示宫格的边框(默认 false )
+ * @property {String} align 宫格对齐方式,表现为数量少的时候,靠左,居中,还是靠右 (默认 'left' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ * @event {Function} click 点击宫格触发
+ * @example <uv-grid :col="3" @click="click"></uv-grid>
+ */
+ export default {
+ name: 'uv-grid',
+ mixins: [mpMixin, mixin, props],
+ emits: ['click'],
+ data() {
+ return {
+ index: 0,
+ width: 0
+ }
+ },
+ watch: {
+ // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+ parentData() {
+ if (this.children.length) {
+ this.children.map(child => {
+ // 判断子组件(uv-radio)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+ typeof(child.updateParentData) == 'function' && child.updateParentData();
+ })
+ }
+ },
+ },
+ created() {
+ // 如果将children定义在data中,在微信小程序会造成循环引用而报错
+ this.children = []
+ },
+ computed: {
+ // 计算父组件的值是否发生变化
+ parentData() {
+ return [this.hoverClass, this.col, this.size, this.border];
+ },
+ // 宫格对齐方式
+ gridStyle() {
+ let style = {};
+ switch (this.align) {
+ case 'left':
+ style.justifyContent = 'flex-start';
+ break;
+ case 'center':
+ style.justifyContent = 'center';
+ break;
+ case 'right':
+ style.justifyContent = 'flex-end';
+ break;
+ default:
+ style.justifyContent = 'flex-start';
+ };
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+ }
+ },
+ methods: {
+ // 此方法由uv-grid-item触发,用于在uv-grid发出事件
+ childClick(name) {
+ this.$emit('click', name)
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ $uv-grid-width:100% !default;
+ .uv-grid {
+ /* #ifdef MP */
+ width: $uv-grid-width;
+ position: relative;
+ box-sizing: border-box;
+ overflow: hidden;
+ display: block;
+ /* #endif */
+ justify-content: center;
+ @include flex;
+ flex-wrap: wrap;
+ align-items: center;
+ }
+</style>
diff --git a/uni_modules/uv-grid/package.json b/uni_modules/uv-grid/package.json
new file mode 100644
index 0000000..0c93379
--- /dev/null
+++ b/uni_modules/uv-grid/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-grid",
+ "displayName": "uv-grid 宫格布局 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.9",
+ "description": "uv-grid 宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。",
+ "keywords": [
+ "uv-grid",
+ "uvui",
+ "uv-ui",
+ "宫格布局",
+ "grid"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-grid/readme.md b/uni_modules/uv-grid/readme.md
new file mode 100644
index 0000000..cd13cef
--- /dev/null
+++ b/uni_modules/uv-grid/readme.md
@@ -0,0 +1,17 @@
+## Grid 宫格布局
+
+> **组件名:uv-grid**
+
+宫格组件一般用于同时展示多个同类项目的场景,可以给宫格的项目设置徽标组件(badge),或者图标等,也可以扩展为左右滑动的轮播形式。
+
+# <a href="https://www.uvui.cn/components/grid.html" target="_blank">查看文档</a>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-image/changelog.md b/uni_modules/uv-image/changelog.md
new file mode 100644
index 0000000..e5a633d
--- /dev/null
+++ b/uni_modules/uv-image/changelog.md
@@ -0,0 +1,36 @@
+## 1.0.16(2023-12-21)
+1. 修复设置show-menu-by-longpress不生效的BUG
+## 1.0.15(2023-12-06)
+1. 优化
+## 1.0.14(2023-12-06)
+1. 阻止事件冒泡问题
+## 1.0.13(2023-11-15)
+1. 修复webp之前未使用的BUG
+## 1.0.12(2023-10-11)
+1. 修复懒加载报错:https://gitee.com/climblee/uv-ui/issues/I869JS
+## 1.0.11(2023-08-31)
+1. 修复设置widthFix时出现显示不全的BUG
+2. 修复抖音等平台在width和height属性改变时出现不显示的BUG
+## 1.0.10(2023-08-29)
+1. 修复异步修改宽高不生效的问题,问题来源:https://gitee.com/climblee/uv-ui/issues/I7WUQ3
+## 1.0.9(2023-08-21)
+1. 修复设置宽高为百分比不生效的BUG
+## 1.0.8(2023-07-24)
+1. 优化 nvue模式下增加cellChild参数,是否在list中cell节点下,nvue中cell下建议设置成true
+## 1.0.7(2023-07-02)
+修复VUE3模式下可能不显示的BUG
+## 1.0.6(2023-07-02)
+优化修改
+## 1.0.5(2023-06-28)
+修复duration属性不生效的BUG
+## 1.0.4(2023-05-27)
+1. 修复可能报错的问题
+## 1.0.3(2023-05-24)
+1. 去掉template中存在的this.导致头条小程序编译警告
+## 1.0.2(2023-05-23)
+1. 优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-image 图片
diff --git a/uni_modules/uv-image/components/uv-image/props.js b/uni_modules/uv-image/components/uv-image/props.js
new file mode 100644
index 0000000..6ef04a5
--- /dev/null
+++ b/uni_modules/uv-image/components/uv-image/props.js
@@ -0,0 +1,95 @@
+export default {
+ props: {
+ // 图片地址
+ src: {
+ type: String,
+ default: ''
+ },
+ // 裁剪模式
+ mode: {
+ type: String,
+ default: 'aspectFill'
+ },
+ // 宽度,单位任意
+ width: {
+ type: [String, Number],
+ default: '300'
+ },
+ // 高度,单位任意
+ height: {
+ type: [String, Number],
+ default: '225'
+ },
+ // 图片形状,circle-圆形,square-方形
+ shape: {
+ type: String,
+ default: 'square'
+ },
+ // 圆角,单位任意
+ radius: {
+ type: [String, Number],
+ default: 0
+ },
+ // 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序
+ lazyLoad: {
+ type: Boolean,
+ default: true
+ },
+ // 是否开启observer懒加载,nvue不生效
+ observeLazyLoad: {
+ type: Boolean,
+ default: false
+ },
+ // 开启长按图片显示识别微信小程序码菜单
+ showMenuByLongpress: {
+ type: Boolean,
+ default: true
+ },
+ // 加载中的图标,或者小图片
+ loadingIcon: {
+ type: String,
+ default: 'photo'
+ },
+ // 加载失败的图标,或者小图片
+ errorIcon: {
+ type: String,
+ default: 'error-circle'
+ },
+ // 是否显示加载中的图标或者自定义的slot
+ showLoading: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示加载错误的图标或者自定义的slot
+ showError: {
+ type: Boolean,
+ default: true
+ },
+ // 是否需要淡入效果
+ fade: {
+ type: Boolean,
+ default: true
+ },
+ // 只支持网络资源,只对微信小程序有效
+ webp: {
+ type: Boolean,
+ default: false
+ },
+ // 过渡时间,单位ms
+ duration: {
+ type: [String, Number],
+ default: 500
+ },
+ // 背景颜色,用于深色页面加载图片时,为了和背景色融合
+ bgColor: {
+ type: String,
+ default: '#f3f4f6'
+ },
+ // nvue模式下 是否直接显示,在uv-list等cell下面使用就需要设置
+ cellChild: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.image
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-image/components/uv-image/uv-image.vue b/uni_modules/uv-image/components/uv-image/uv-image.vue
new file mode 100644
index 0000000..2ab4a78
--- /dev/null
+++ b/uni_modules/uv-image/components/uv-image/uv-image.vue
@@ -0,0 +1,287 @@
+<template>
+ <uv-transition
+ v-if="show"
+ :show="show"
+ mode="fade"
+ :duration="fade ? duration : 0"
+ :cell-child="cellChild"
+ :custom-style="wrapStyle"
+ >
+ <view
+ class="uv-image"
+ :class="[`uv-image--${elIndex}`]"
+ @tap="onClick"
+ :style="[wrapStyle, backgroundStyle]"
+ >
+ <image
+ v-if="!isError && observeShow"
+ :src="src"
+ :mode="mode"
+ @error="onErrorHandler"
+ @load="onLoadHandler"
+ :show-menu-by-longpress="showMenuByLongpress"
+ :lazy-load="lazyLoad"
+ class="uv-image__image"
+ :style="[imageStyle]"
+ :webp="webp"
+ ></image>
+ <view
+ v-if="showLoading && loading"
+ class="uv-image__loading"
+ :style="{
+ borderRadius: shape == 'circle' ? '50%' : $uv.addUnit(radius),
+ backgroundColor: bgColor,
+ width: $uv.addUnit(width),
+ height: $uv.addUnit(height)
+ }"
+ >
+ <slot name="loading">
+ <uv-icon
+ :name="loadingIcon"
+ :width="width"
+ :height="height"
+ ></uv-icon>
+ </slot>
+ </view>
+ <view
+ v-if="showError && isError && !loading"
+ class="uv-image__error"
+ :style="{
+ borderRadius: shape == 'circle' ? '50%' : $uv.addUnit(radius),
+ width: $uv.addUnit(width),
+ height: $uv.addUnit(height)
+ }"
+ >
+ <slot name="error">
+ <uv-icon
+ :name="errorIcon"
+ :width="width"
+ :height="height"
+ ></uv-icon>
+ </slot>
+ </view>
+ </view>
+ </uv-transition>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Image 图片
+ * @description 此组件为uni-app的image组件的加强版,在继承了原有功能外,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
+ * @tutorial https://www.uvui.cn/components/image.html
+ * @property {String} src 图片地址
+ * @property {String} mode 裁剪模式,见官网说明 (默认 'aspectFill' )
+ * @property {String | Number} width 宽度,单位任意,如果为数值,则为px单位 (默认 '300' )
+ * @property {String | Number} height 高度,单位任意,如果为数值,则为px单位 (默认 '225' )
+ * @property {String} shape 图片形状,circle-圆形,square-方形 (默认 'square' )
+ * @property {String | Number} radius 圆角值,单位任意,如果为数值,则为px单位 (默认 0 )
+ * @property {Boolean} lazyLoad 是否懒加载,仅微信小程序、App、百度小程序、字节跳动小程序有效 (默认 true )
+ * @property {Boolean} showMenuByLongpress 是否开启长按图片显示识别小程序码菜单,仅微信小程序有效 (默认 true )
+ * @property {String} loadingIcon 加载中的图标,或者小图片 (默认 'photo' )
+ * @property {String} errorIcon 加载失败的图标,或者小图片 (默认 'error-circle' )
+ * @property {Boolean} showLoading 是否显示加载中的图标或者自定义的slot (默认 true )
+ * @property {Boolean} showError 是否显示加载错误的图标或者自定义的slot (默认 true )
+ * @property {Boolean} fade 是否需要淡入效果 (默认 true )
+ * @property {Boolean} webp 只支持网络资源,只对微信小程序有效 (默认 false )
+ * @property {String | Number} duration 搭配fade参数的过渡时间,单位ms (默认 500 )
+ * @property {String} bgColor 背景颜色,用于深色页面加载图片时,为了和背景色融合 (默认 '#f3f4f6' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ * @event {Function} click 点击图片时触发
+ * @event {Function} error 图片加载失败时触发
+ * @event {Function} load 图片加载成功时触发
+ * @example <uv-image width="100%" height="300px" :src="src"></uv-image>
+ */
+ export default {
+ name: 'uv-image',
+ emits: ['click','load','error'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 图片是否加载错误,如果是,则显示错误占位图
+ isError: false,
+ // 初始化组件时,默认为加载中状态
+ loading: true,
+ // 图片加载完成时,去掉背景颜色,因为如果是png图片,就会显示灰色的背景
+ backgroundStyle: {},
+ // 用于fade模式的控制组件显示与否
+ show: false,
+ // 是否开启图片出现在可视范围进行加载(另一种懒加载)
+ observeShow: !this.observeLazyLoad,
+ elIndex: '',
+ // 因为props的值无法修改,故需要一个中间值
+ imgWidth: this.width,
+ // 因为props的值无法修改,故需要一个中间值
+ imgHeight: this.height,
+ thresholdValue: 50
+ };
+ },
+ watch: {
+ src: {
+ immediate: true,
+ handler(n) {
+ if (!n) {
+ // 如果传入null或者'',或者false,或者undefined,标记为错误状态
+ this.isError = true
+ } else {
+ this.isError = false;
+ this.loading = true;
+ }
+ }
+ },
+ width(newVal){
+ // 这样做的目的是避免在更新时候,某些平台动画会恢复关闭状态
+ this.show = false;
+ this.$uv.sleep(2).then(res=>{
+ this.show = true;
+ });
+ this.imgWidth = newVal;
+ },
+ height(newVal){
+ // 这样做的目的是避免在更新时候,某些平台动画会恢复关闭状态
+ this.show = false;
+ this.$uv.sleep(2).then(res=>{
+ this.show = true;
+ });
+ this.imgHeight = newVal;
+ }
+ },
+ computed: {
+ wrapStyle() {
+ let style = {};
+ // 通过调用addUnit()方法,如果有单位,如百分比,px单位等,直接返回,如果是纯粹的数值,则加上rpx单位
+ if(this.mode !== 'heightFix') {
+ style.width = this.$uv.addUnit(this.imgWidth);
+ }
+ if(this.mode !== 'widthFix') {
+ style.height = this.$uv.addUnit(this.imgHeight);
+ }
+ // 如果是显示圆形,设置一个很多的半径值即可
+ style.borderRadius = this.shape == 'circle' ? '10000px' : this.$uv.addUnit(this.radius)
+ // 如果设置圆角,必须要有hidden,否则可能圆角无效
+ style.overflow = this.radius > 0 ? 'hidden' : 'visible'
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+ },
+ imageStyle() {
+ let style = {};
+ style.borderRadius = this.shape == 'circle' ? '10000px' : this.$uv.addUnit(this.radius);
+ // #ifdef APP-NVUE
+ style.width = this.$uv.addUnit(this.imgWidth);
+ style.height = this.$uv.addUnit(this.imgHeight);
+ // #endif
+ return style;
+ }
+ },
+ created() {
+ this.elIndex = this.$uv.guid();
+ this.observer = {}
+ this.observerName = 'lazyLoadContentObserver'
+ },
+ mounted() {
+ this.show = true;
+ this.$nextTick(()=>{
+ if(this.observeLazyLoad) this.observerFn();
+ })
+ },
+ methods: {
+ // 点击图片
+ onClick() {
+ this.$emit('click')
+ },
+ // 图片加载失败
+ onErrorHandler(err) {
+ this.loading = false
+ this.isError = true
+ this.$emit('error', err)
+ },
+ // 图片加载完成,标记loading结束
+ onLoadHandler(event) {
+ if(this.mode == 'widthFix') this.imgHeight = 'auto'
+ if(this.mode == 'heightFix') this.imgWidth = 'auto'
+ this.loading = false
+ this.isError = false
+ this.$emit('load', event)
+ this.removeBgColor()
+ },
+ // 移除图片的背景色
+ removeBgColor() {
+ // 淡入动画过渡完成后,将背景设置为透明色,否则png图片会看到灰色的背景
+ this.backgroundStyle = {
+ backgroundColor: 'transparent'
+ };
+ },
+ // 观察图片是否在可见视口
+ observerFn(){
+ // 在需要用到懒加载的页面,在触发底部的时候触发tOnLazyLoadReachBottom事件,保证所有图片进行加载
+ this.$nextTick(() => {
+ uni.$once('onLazyLoadReachBottom', () => {
+ if (!this.observeShow) this.observeShow = true
+ })
+ })
+ setTimeout(() => {
+ // #ifndef APP-NVUE
+ this.disconnectObserver(this.observerName)
+ const contentObserver = uni.createIntersectionObserver(this)
+ contentObserver.relativeToViewport({
+ bottom: this.thresholdValue
+ }).observe(`.uv-image--${this.elIndex}`, (res) => {
+ if (res.intersectionRatio > 0) {
+ // 懒加载状态改变
+ this.observeShow = true
+ // 如果图片已经加载,去掉监听,减少性能消耗
+ this.disconnectObserver(this.observerName)
+ }
+ })
+ this[this.observerName] = contentObserver
+ // #endif
+ // #ifdef APP-NVUE
+ this.observeShow = true;
+ // #endif
+ }, 50)
+ },
+ disconnectObserver(observerName) {
+ const observer = this[observerName]
+ observer && observer.disconnect()
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-image-error-top:0px !default;
+ $uv-image-error-left:0px !default;
+ $uv-image-error-width:100% !default;
+ $uv-image-error-hight:100% !default;
+ $uv-image-error-background-color:$uv-bg-color !default;
+ $uv-image-error-color:$uv-tips-color !default;
+ $uv-image-error-font-size: 46rpx !default;
+
+ .uv-image {
+ position: relative;
+ transition: opacity 0.5s ease-in-out;
+
+ &__image {
+ width: 100%;
+ height: 100%;
+ }
+
+ &__loading,
+ &__error {
+ position: absolute;
+ top: $uv-image-error-top;
+ left: $uv-image-error-left;
+ width: $uv-image-error-width;
+ height: $uv-image-error-hight;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ background-color: $uv-image-error-background-color;
+ color: $uv-image-error-color;
+ font-size: $uv-image-error-font-size;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-image/package.json b/uni_modules/uv-image/package.json
new file mode 100644
index 0000000..796089e
--- /dev/null
+++ b/uni_modules/uv-image/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-image",
+ "displayName": "uv-image 图片 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.16",
+ "description": "uv-image 此组件为uni-app的image组件的加强版,在继承了原有功能外,增加observer懒加载功能,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。",
+ "keywords": [
+ "uv-image",
+ "uvui",
+ "uv-ui",
+ "image",
+ "图片"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-transition",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-image/readme.md b/uni_modules/uv-image/readme.md
new file mode 100644
index 0000000..0ecc120
--- /dev/null
+++ b/uni_modules/uv-image/readme.md
@@ -0,0 +1,15 @@
+## Image 图片
+
+> **组件名:uv-image**
+
+此组件为`uni-app`的`image`组件的加强版,在继承了原有功能外,增加`observer`懒加载功能,还支持淡入动画、加载中、加载失败提示、圆角值和形状等。
+
+# <a href="https://www.uvui.cn/components/image.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-index-list/changelog.md b/uni_modules/uv-index-list/changelog.md
new file mode 100644
index 0000000..deda715
--- /dev/null
+++ b/uni_modules/uv-index-list/changelog.md
@@ -0,0 +1,18 @@
+## 1.0.7(2023-11-07)
+1. 修复sticky属性不生效的BUG
+## 1.0.6(2023-09-01)
+1. 修复设置customNavHeight导致定位不准确的BUG
+## 1.0.5(2023-08-23)
+1. 修复ios端快速滑动+点击右侧导航会出现白屏的BUG
+## 1.0.4(2023-07-25)
+1. 修复全局设置成rpx存在的高度BUG
+2. 修复其他BUG
+## 1.0.3(2023-07-03)
+去除插槽判断,避免某些平台不显示的BUG
+## 1.0.2(2023-05-27)
+1. select事件修复
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-index-list 索引列表
diff --git a/uni_modules/uv-index-list/components/uv-index-anchor/props.js b/uni_modules/uv-index-list/components/uv-index-anchor/props.js
new file mode 100644
index 0000000..9ec949e
--- /dev/null
+++ b/uni_modules/uv-index-list/components/uv-index-anchor/props.js
@@ -0,0 +1,30 @@
+export default {
+ props: {
+ // 列表锚点文本内容
+ text: {
+ type: [String, Number],
+ default: ''
+ },
+ // 列表锚点文字颜色
+ color: {
+ type: String,
+ default: '#606266'
+ },
+ // 列表锚点文字大小,单位默认px
+ size: {
+ type: [String, Number],
+ default: 14
+ },
+ // 列表锚点背景颜色
+ bgColor: {
+ type: String,
+ default: '#dedede'
+ },
+ // 列表锚点高度,单位默认px
+ height: {
+ type: [String, Number],
+ default: 32
+ },
+ ...uni.$uv?.props?.indexAnchor
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-index-list/components/uv-index-anchor/uv-index-anchor.vue b/uni_modules/uv-index-list/components/uv-index-anchor/uv-index-anchor.vue
new file mode 100644
index 0000000..e566aa7
--- /dev/null
+++ b/uni_modules/uv-index-list/components/uv-index-anchor/uv-index-anchor.vue
@@ -0,0 +1,98 @@
+<template>
+ <!-- #ifdef APP-NVUE -->
+ <header>
+ <!-- #endif -->
+ <view
+ :class="['uv-index-anchor',{'uv-index-anchor-sticky': parentData.sticky}]"
+ :ref="`uv-index-anchor-${text}`"
+ :style="{
+ height: $uv.addUnit(height),
+ backgroundColor: bgColor
+ }"
+ >
+ <text
+ class="uv-index-anchor__text"
+ :style="{
+ fontSize: $uv.addUnit(size),
+ color: color
+ }"
+ >{{ text }}</text>
+ </view>
+ <!-- #ifdef APP-NVUE -->
+ </header>
+ <!-- #endif -->
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * IndexAnchor 列表锚点
+ * @description
+ * @tutorial https://www.uvui.cn/components/indexList.html
+ * @property {String | Number} text 列表锚点文本内容
+ * @property {String} color 列表锚点文字颜色 ( 默认 '#606266' )
+ * @property {String | Number} size 列表锚点文字大小,单位默认px ( 默认 14 )
+ * @property {String} bgColor 列表锚点背景颜色 ( 默认 '#dedede' )
+ * @property {String | Number} height 列表锚点高度,单位默认px ( 默认 32 )
+ * @example <uv-index-anchor :text="indexList[index]"></uv-index-anchor>
+ */
+ export default {
+ name: 'uv-index-anchor',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ parentData: {
+ sticky: true
+ }
+ }
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 此处会活动父组件实例,并赋值给实例的parent属性
+ const indexList = this.$uv.$parent.call(this, 'uv-index-list')
+ if (!indexList) {
+ return this.$uv.error('uv-index-anchor必须要搭配uv-index-list组件使用')
+ }
+ this.parentData.sticky = indexList.sticky;
+ // 将当前实例放入到uv-index-list中
+ indexList.anchors.push(this)
+ const indexListItem = this.$uv.$parent.call(this, 'uv-index-item')
+ // #ifndef APP-NVUE
+ // 只有在非nvue下,uv-index-anchor才是嵌套在uv-index-item中的
+ if (!indexListItem) {
+ return this.$uv.error('uv-index-anchor必须要搭配uv-index-item组件使用')
+ }
+ // 设置uv-index-item的id为anchor的text标识符,因为非nvue下滚动列表需要依赖scroll-view滚动到元素的特性
+ indexListItem.id = this.text.charCodeAt(0)
+ // #endif
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-index-anchor {
+ @include flex;
+ align-items: center;
+ padding-left: 15px;
+ z-index: 1;
+ &-sticky {
+ position: sticky;
+ top: 0;
+ }
+ &__text {
+ @include flex;
+ align-items: center;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-index-list/components/uv-index-item/uv-index-item.vue b/uni_modules/uv-index-list/components/uv-index-item/uv-index-item.vue
new file mode 100644
index 0000000..f6e8901
--- /dev/null
+++ b/uni_modules/uv-index-list/components/uv-index-item/uv-index-item.vue
@@ -0,0 +1,87 @@
+<template>
+ <!-- #ifdef APP-NVUE -->
+ <cell ref="uv-index-item">
+ <!-- #endif -->
+ <view
+ class="uv-index-item"
+ :id="`uv-index-item-${id}`"
+ :class="[`uv-index-item-${id}`]"
+ >
+ <slot />
+ </view>
+ <!-- #ifdef APP-NVUE -->
+ </cell>
+ <!-- #endif -->
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * IndexItem
+ * @description
+ * @tutorial https://www.uvui.cn/components/indexList.html
+ * @property {String}
+ * @event {Function}
+ * @example
+ */
+ export default {
+ name: 'uv-index-item',
+ mixins: [mpMixin, mixin],
+ data() {
+ return {
+ // 本组件到滚动条顶部的距离
+ top: 0,
+ height: 0,
+ id: ''
+ }
+ },
+ created() {
+ // 子组件uv-index-anchor的实例
+ this.anchor = {}
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 此处会活动父组件实例,并赋值给实例的parent属性
+ this.getParentData('uv-index-list')
+ if (!this.parent) {
+ return this.$uv.error('uv-index-item必须要搭配uv-index-list组件使用')
+ }
+ this.$uv.sleep().then(() =>{
+ this.getIndexItemRect().then(size => {
+ // 由于对象的引用特性,此处会同时生效到父组件的children数组的本实例的top属性中,供父组件判断读取
+ this.top = Math.ceil(size.top)
+ this.height = Math.ceil(size.height)
+ })
+ })
+ },
+ getIndexItemRect() {
+ return new Promise(resolve => {
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-index-item').then(size => {
+ resolve(size)
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-index-item']
+ dom.getComponentRect(ref, res => {
+ resolve(res.size)
+ })
+ // #endif
+ })
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+</style>
diff --git a/uni_modules/uv-index-list/components/uv-index-list/props.js b/uni_modules/uv-index-list/components/uv-index-list/props.js
new file mode 100644
index 0000000..0cf6356
--- /dev/null
+++ b/uni_modules/uv-index-list/components/uv-index-list/props.js
@@ -0,0 +1,30 @@
+export default {
+ props: {
+ // 右边锚点非激活的颜色
+ inactiveColor: {
+ type: String,
+ default: '#606266'
+ },
+ // 右边锚点激活的颜色
+ activeColor: {
+ type: String,
+ default: '#5677fc'
+ },
+ // 索引字符列表,数组形式
+ indexList: {
+ type: Array,
+ default: () => []
+ },
+ // 是否开启锚点自动吸顶
+ sticky: {
+ type: Boolean,
+ default: true
+ },
+ // 自定义导航栏的高度
+ customNavHeight: {
+ type: [String, Number],
+ default: 0
+ },
+ ...uni.$uv?.props?.indexList
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-index-list/components/uv-index-list/uv-index-list.vue b/uni_modules/uv-index-list/components/uv-index-list/uv-index-list.vue
new file mode 100644
index 0000000..e610323
--- /dev/null
+++ b/uni_modules/uv-index-list/components/uv-index-list/uv-index-list.vue
@@ -0,0 +1,461 @@
+<template>
+ <view class="uv-index-list">
+ <!-- #ifdef APP-NVUE -->
+ <list
+ :scrollTop="scrollTop"
+ enable-back-to-top
+ :offset-accuracy="1"
+ :style="{
+ maxHeight: $uv.addUnit(scrollViewHeight,'px')
+ }"
+ @scroll="scrollHandler"
+ ref="uvList"
+ >
+ <cell ref="header">
+ <slot name="header" />
+ </cell>
+ <slot />
+ <cell>
+ <slot name="footer" />
+ </cell>
+ </list>
+ <!-- #endif -->
+ <!-- #ifndef APP-NVUE -->
+ <scroll-view
+ :scrollTop="scrollTop"
+ :scrollIntoView="scrollIntoView"
+ :offset-accuracy="1"
+ :style="{
+ maxHeight: $uv.addUnit(scrollViewHeight,'px')
+ }"
+ scroll-y
+ @scroll="scrollHandler"
+ ref="uvList"
+ >
+ <view>
+ <slot name="header" />
+ </view>
+ <slot />
+ <view>
+ <slot name="footer" />
+ </view>
+ </scroll-view>
+ <!-- #endif -->
+ <view
+ class="uv-index-list__letter"
+ ref="uv-index-list__letter"
+ :style="{ top: $uv.addUnit(letterInfo.top || 100 ,'px') }"
+ @touchstart="touchStart"
+ @touchmove.stop.prevent="touchMove"
+ @touchend.stop.prevent="touchEnd"
+ @touchcancel.stop.prevent="touchEnd"
+ >
+ <view
+ class="uv-index-list__letter__item"
+ v-for="(item, index) in uIndexList"
+ :key="index"
+ :style="{
+ backgroundColor: activeIndex === index ? activeColor : 'transparent'
+ }"
+ >
+ <text
+ class="uv-index-list__letter__item__index"
+ :style="{color: activeIndex === index ? '#fff' : inactiveColor}"
+ >{{ item }}</text>
+ </view>
+ </view>
+ <uv-transition
+ mode="fade"
+ :show="touching"
+ :customStyle="{
+ position: 'fixed',
+ right: '40px',
+ top: $uv.addUnit(indicatorTop,'px'),
+ zIndex: 2
+ }"
+ >
+ <view class="uv-index-list__indicator__box">
+ <view
+ class="uv-index-list__indicator"
+ :class="['uv-index-list__indicator--show']"
+ :style="{
+ height: $uv.addUnit(indicatorHeight,'px'),
+ width: $uv.addUnit(indicatorHeight,'px')
+ }"
+ >
+ <text class="uv-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text>
+ </view>
+ </view>
+ </uv-transition>
+ </view>
+</template>
+
+<script>
+ const indexList = () => {
+ const indexList = [];
+ const charCodeOfA = 'A'.charCodeAt(0);
+ for (let i = 0; i < 26; i++) {
+ indexList.push(String.fromCharCode(charCodeOfA + i));
+ }
+ return indexList;
+ }
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * IndexList 索引列表
+ * @description 通过折叠面板收纳内容区域
+ * @tutorial https://www.uvui.cn/components/indexList.html
+ * @property {String} inactiveColor 右边锚点非激活的颜色 ( 默认 '#606266' )
+ * @property {String} activeColor 右边锚点激活的颜色 ( 默认 '#5677fc' )
+ * @property {Array} indexList 索引字符列表,数组形式
+ * @property {Boolean} sticky 是否开启锚点自动吸顶 ( 默认 true )
+ * @property {String | Number} customNavHeight 自定义导航栏的高度 ( 默认 0 )
+ * */
+ export default {
+ name: 'uv-index-list',
+ mixins: [mpMixin, mixin, props],
+ // #ifdef MP-WEIXIN
+ // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性
+ options: {
+ virtualHost: true
+ },
+ // #endif
+ data() {
+ return {
+ // 当前正在被选中的字母索引
+ activeIndex: -1,
+ touchmoveIndex: 1,
+ // 索引字母的信息
+ letterInfo: {
+ height: 0,
+ itemHeight: 0,
+ top: 0
+ },
+ // 设置字母指示器的高度,后面为了让指示器跟随字母,并将尖角部分指向字母的中部,需要依赖此值
+ indicatorHeight: 50,
+ // 字母放大指示器的top值,为了让其指向当前激活的字母
+ // indicatorTop: 0
+ // 当前是否正在被触摸状态
+ touching: false,
+ // 滚动条顶部top值
+ scrollTop: 0,
+ // scroll-view的高度
+ scrollViewHeight: 0,
+ // 系统信息
+ sys: '',
+ scrolling: false,
+ scrollIntoView: '',
+ hasHeight: 0,
+ timer: 0,
+ disTap: false
+ }
+ },
+ computed: {
+ // 如果有传入外部的indexList锚点数组则使用,否则使用内部生成A-Z字母
+ uIndexList() {
+ return this.indexList.length ? this.indexList : indexList()
+ },
+ // 字母放大指示器的top值,为了让其指向当前激活的字母
+ indicatorTop() {
+ const {
+ top,
+ itemHeight
+ } = this.letterInfo
+ return Math.floor(top + itemHeight * this.activeIndex + itemHeight / 2 - this.indicatorHeight / 2) - 8
+ }
+ },
+ watch: {
+ // 监听字母索引的变化,重新设置尺寸
+ uIndexList: {
+ immediate: true,
+ handler() {
+ this.$uv.sleep().then(() => {
+ this.setIndexListLetterInfo()
+ })
+ }
+ }
+ },
+ created() {
+ this.sys = this.$uv.sys()
+ this.children = []
+ this.anchors = []
+ this.init()
+ },
+ mounted() {
+ this.setIndexListLetterInfo()
+ },
+ methods: {
+ init() {
+ // 设置列表的高度为整个屏幕的高度
+ //减去this.customNavHeight,并将this.scrollViewHeight设置为maxHeight
+ //解决当uv-index-list组件放在tabbar页面时,scroll-view内容较少时,还能滚动
+ this.scrollViewHeight = this.sys.windowHeight - this.$uv.getPx(this.customNavHeight)
+ },
+ // 索引列表被触摸
+ touchStart(e) {
+ // 获取触摸点信息
+ const touchStart = e.changedTouches[0]
+ if (!touchStart || this.disTap) return
+ this.touching = true
+ const {
+ pageY
+ } = touchStart
+ // 根据当前触摸点的坐标,获取当前触摸的为第几个字母
+ const currentIndex = this.getIndexListLetter(pageY)
+ this.setValueForTouch(currentIndex)
+ },
+ // 索引字母列表被触摸滑动中
+ touchMove(e) {
+ // 获取触摸点信息
+ let touchMove = e.changedTouches[0]
+ if (!touchMove || this.disTap) return;
+
+ // 滑动结束后迅速开始第二次滑动时候 touching 为 false 造成不显示 indicator 问题
+ if (!this.touching) {
+ this.touching = true
+ }
+ const {
+ pageY
+ } = touchMove
+ const currentIndex = this.getIndexListLetter(pageY)
+ this.setValueForTouch(currentIndex)
+ },
+ // 触摸结束
+ touchEnd(e) {
+ // 延时一定时间后再隐藏指示器,为了让用户看的更直观,同时也是为了消除快速切换uv-transition的show带来的影响
+ this.$uv.sleep(300).then(() => {
+ this.touching = false
+ })
+ this.$emit('select',this.activeIndex);
+ },
+ // 获取索引列表的尺寸以及单个字符的尺寸信息
+ getIndexListLetterRect() {
+ return new Promise(resolve => {
+ // 延时一定时间,以获取dom尺寸
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-index-list__letter').then(size => {
+ resolve(size)
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-index-list__letter']
+ dom.getComponentRect(ref, res => {
+ resolve(res.size)
+ })
+ // #endif
+ })
+ },
+ // 设置indexList索引的尺寸信息
+ setIndexListLetterInfo() {
+ this.getIndexListLetterRect().then(size => {
+ const {
+ top,
+ height
+ } = size
+ const windowHeight = this.$uv.sys().windowHeight;
+ let customNavHeight = 0
+ // 消除各端导航栏非原生和原生导致的差异,让索引列表字母对屏幕垂直居中
+ if (this.customNavHeight == 0) {
+ // #ifdef H5
+ customNavHeight = this.$uv.sys().windowTop
+ // #endif
+ // #ifndef H5
+ // 在非H5中,为原生导航栏,其高度不算在windowHeight内,这里设置为负值,后面相加时变成减去其高度的一半
+ customNavHeight = 0
+ // #endif
+ } else {
+ customNavHeight = this.$uv.getPx(this.customNavHeight)
+ }
+ this.letterInfo = {
+ height,
+ // 为了让字母列表对屏幕绝对居中,让其对导航栏进行修正,也即往上偏移导航栏的一半高度
+ top: (windowHeight - height) / 2 + customNavHeight / 2,
+ itemHeight: Math.floor(height / this.uIndexList.length)
+ }
+ })
+ },
+ // 获取当前被触摸的索引字母
+ getIndexListLetter(pageY) {
+ const {
+ top,
+ height,
+ itemHeight
+ } = this.letterInfo
+ // 对H5的pageY进行修正,这是由于uni-app自作多情在H5中将触摸点的坐标跟H5的导航栏结合导致的问题
+ // #ifdef H5
+ pageY += this.$uv.sys().windowTop
+ // #endif
+ // #ifdef APP-NVUE
+ pageY += top
+ // #endif
+ // 对第一和最后一个字母做边界处理,因为用户可能在字母列表上触摸到两端的尽头后依然继续滑动
+ if (pageY < top) {
+ return 0
+ } else if (pageY >= top + height) {
+ // 如果超出了,取最后一个字母
+ return this.uIndexList.length - 1
+ } else {
+ // 将触摸点的Y轴偏移值,减去索引字母的top值,除以每个字母的高度,即可得到当前触摸点落在哪个字母上
+ return Math.floor((pageY - top) / itemHeight);
+ }
+ },
+ // 设置各项由触摸而导致变化的值
+ setValueForTouch(currentIndex) {
+ // 如果偏移量太小,前后得出的会是同一个索引字母,为了防抖,进行返回
+ if (currentIndex === this.activeIndex) return
+ this.activeIndex = currentIndex
+ // #ifndef APP-NVUE || MP-WEIXIN
+ // 在非nvue中,由于anchor和item都在uv-index-item中,所以需要对index-item进行偏移
+ this.scrollIntoView = `uv-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}`
+ // #endif
+ // #ifdef MP-WEIXIN
+ // 微信小程序下,scroll-view的scroll-into-view属性无法对slot中的内容的id生效,只能通过设置scrollTop的形式去移动滚动条
+ this.scrollTop = this.children[currentIndex].top
+ // #endif
+ // #ifdef APP-NVUE
+ // 在nvue中,由于cell和header为同级元素,所以实际是需要对header(anchor)进行偏移
+ const anchor = `uv-index-anchor-${this.uIndexList[currentIndex]}`
+ dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], {
+ offset: 0,
+ animated: false
+ })
+ // #endif
+ },
+ getHeaderRect() {
+ // 获取header slot的高度,因为list组件中获取元素的尺寸是没有top值的
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs.header, res => {
+ resolve(res.size)
+ })
+ })
+ },
+ // scroll-view的滚动事件
+ async scrollHandler(e) {
+ if (this.touching || this.scrolling) return
+ // 每过一定时间取样一次,减少资源损耗以及可能带来的卡顿
+ this.scrolling = true
+ this.disTap = true;
+ clearTimeout(this.timer);
+ this.timer = setTimeout(()=>{
+ this.disTap = false;
+ },200)
+ this.$uv.sleep(30).then(() => {
+ this.scrolling = false
+ })
+ let scrollTop = 0
+ const len = this.children.length
+ let children = this.children
+ const anchors = this.anchors
+ const customNavHeight = this.$uv.getPx(this.customNavHeight);
+ // #ifdef APP-NVUE
+ // nvue下获取的滚动条偏移为负数,需要转为正数
+ scrollTop = Math.abs(e.contentOffset.y)
+ // 获取header slot的尺寸信息
+ const header = await this.getHeaderRect()
+ // item的top值,在nvue下,模拟出的anchor的top,类似非nvue下的index-item的top
+ let top = header.height
+ // 由于list组件无法获取cell的top值,这里通过header slot和各个item之间的height,模拟出类似非nvue下的位置信息
+ children = this.children.map((item, index) => {
+ if(item.height>0) this.hasHeight = item.height;
+ item.height = item.height>0?item.height:this.hasHeight;
+ const child = {
+ height: item.height,
+ top
+ }
+ // 进行累加,给下一个item提供计算依据
+ top += item.height + anchors[index].height
+ return child
+ })
+ // #endif
+ // #ifndef APP-NVUE
+ // 非nvue通过detail获取滚动条位移
+ scrollTop = e.detail.scrollTop
+ // #endif
+ for (let i = 0; i < len; i++) {
+ const item = children[i],
+ nextItem = children[i + 1]
+ // 如果滚动条高度小于第一个item的top值,此时无需设置任意字母为高亮
+ if (scrollTop + customNavHeight <= children[0].top || scrollTop >= children[len - 1].top + children[len -
+ 1].height) {
+ this.activeIndex = -1
+ break
+ } else if (!nextItem) {
+ // 当不存在下一个item时,意味着历遍到了最后一个
+ this.activeIndex = len - 1
+ break
+ } else if (scrollTop + customNavHeight > item.top && scrollTop + customNavHeight < nextItem.top) {
+ this.activeIndex = i
+ break
+ }
+ }
+ },
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-index-list {
+
+ &__letter {
+ position: fixed;
+ right: 0;
+ text-align: center;
+ z-index: 3;
+ padding: 0 6px;
+
+ &__item {
+ width: 16px;
+ height: 16px;
+ border-radius: 100px;
+ margin: 1px 0;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+
+ &--active {
+ background-color: $uv-primary;
+ }
+
+ &__index {
+ font-size: 12px;
+ text-align: center;
+ line-height: 12px;
+ }
+ }
+ }
+ &__indicator__box {
+ width: 65px;
+ height: 65px;
+ padding: 6px;
+ transform: rotate(-45deg);
+ }
+ &__indicator {
+ width: 50px;
+ height: 50px;
+ border-radius: 100px 100px 0 100px;
+ text-align: center;
+ color: #ffffff;
+ background-color: #c9c9c9;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+
+ &__text {
+ font-size: 28px;
+ line-height: 28px;
+ font-weight: bold;
+ color: #fff;
+ transform: rotate(45deg);
+ text-align: center;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-index-list/package.json b/uni_modules/uv-index-list/package.json
new file mode 100644
index 0000000..133736b
--- /dev/null
+++ b/uni_modules/uv-index-list/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-index-list",
+ "displayName": "uv-index-list 索引列表 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.7",
+ "description": "该组件用于展示索引列表,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景。类似于微信通讯录页面",
+ "keywords": [
+ "uv-index-list",
+ "uvui",
+ "uv-ui",
+ "index-list",
+ "索引列表"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-transition"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-index-list/readme.md b/uni_modules/uv-index-list/readme.md
new file mode 100644
index 0000000..605741c
--- /dev/null
+++ b/uni_modules/uv-index-list/readme.md
@@ -0,0 +1,19 @@
+## IndexList 索引列表
+
+> **组件名:uv-index-list**
+
+用于展示索引列表,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景,类似于微信通讯录页面。
+
+# <a href="https://www.uvui.cn/components/indexList.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-keyboard/changelog.md b/uni_modules/uv-keyboard/changelog.md
new file mode 100644
index 0000000..1d1be01
--- /dev/null
+++ b/uni_modules/uv-keyboard/changelog.md
@@ -0,0 +1,17 @@
+## 1.0.5(2023-10-12)
+1. 增加disKeys参数,mode = "car"下,被禁用的键,如:['I','O']
+2. 增加customabc参数,mode = "car"下,是否启用自定义中英文切换内容模式,为了兼容支付宝等小程序不兼容嵌套插槽,导致同时显示自定义内容和原始内容
+3. 增加ref方法changeCarMode,mode = "car"下, 调用此方法可以切换中英文模式
+4. 增加@changeCarInputMode,mode = "car"下,调用此方法可以进行切换中英文
+5. 增加插槽abc,mode = "car"下,自定义中英文切换内容,具体参考[车牌键盘自定义中英文切换及禁用键等设置](https://www.uvui.cn/components/keyboard.html#车牌键盘自定义中英文切换及禁用键等设置)
+## 1.0.4(2023-09-04)
+1. 优化,修改文件名称
+## 1.0.3(2023-09-04)
+1. 修复键盘change回调事件产生冲突的BUG
+## 1.0.2(2023-07-02)
+uv-keyboard 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/keyboard.html
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-keyboard 键盘
diff --git a/uni_modules/uv-keyboard/components/uv-keyboard-car/props.js b/uni_modules/uv-keyboard/components/uv-keyboard-car/props.js
new file mode 100644
index 0000000..3dd93cc
--- /dev/null
+++ b/uni_modules/uv-keyboard/components/uv-keyboard-car/props.js
@@ -0,0 +1,24 @@
+export default {
+ props: {
+ // 是否打乱键盘按键的顺序
+ random: {
+ type: Boolean,
+ default: false
+ },
+ // 输入一个中文后,是否自动切换到英文
+ autoChange: {
+ type: Boolean,
+ default: false
+ },
+ // 被禁用的键
+ disKeys: {
+ type: Array,
+ default: ()=>[]
+ },
+ // 是否自定义abc
+ customabc: {
+ type: Boolean,
+ default: false
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-keyboard/components/uv-keyboard-car/uv-keyboard-car.vue b/uni_modules/uv-keyboard/components/uv-keyboard-car/uv-keyboard-car.vue
new file mode 100644
index 0000000..e99e910
--- /dev/null
+++ b/uni_modules/uv-keyboard/components/uv-keyboard-car/uv-keyboard-car.vue
@@ -0,0 +1,347 @@
+<template>
+ <view
+ class="uv-keyboard"
+ @touchmove.stop.prevent="noop"
+ >
+ <view
+ v-for="(group, i) in abc ? engKeyBoardList : areaList"
+ :key="i"
+ class="uv-keyboard__button"
+ :index="i"
+ :class="[i + 1 === 4 && 'uv-keyboard__button--center']"
+ >
+ <view
+ v-if="i === 3"
+ class="uv-keyboard__button__inner-wrapper"
+ >
+ <view
+ class="uv-keyboard__button__inner-wrapper__left"
+ hover-class="uv-hover-class"
+ :hover-stay-time="200"
+ @tap="changeCarInputMode"
+ >
+ <slot>
+ <template v-if="!customabc">
+ <text
+ class="uv-keyboard__button__inner-wrapper__left__lang"
+ :class="[!abc && 'uv-keyboard__button__inner-wrapper__left__lang--active']"
+ >中</text>
+ <text class="uv-keyboard__button__inner-wrapper__left__line">/</text>
+ <text
+ class="uv-keyboard__button__inner-wrapper__left__lang"
+ :class="[abc && 'uv-keyboard__button__inner-wrapper__left__lang--active']"
+ >英</text>
+ </template>
+ </slot>
+ </view>
+ </view>
+ <view
+ class="uv-keyboard__button__inner-wrapper"
+ v-for="(item, j) in group"
+ :key="j"
+ >
+ <view
+ :class="['uv-keyboard__button__inner-wrapper__inner',{'uv-keyboard__button__inner-wrapper__inner--disabled': isDisabled(i,j)}]"
+ :hover-stay-time="200"
+ @tap="carInputClick(i, j)"
+ :hover-class="isDisabled(i,j)?'none':'uv-hover-class'"
+ >
+ <text class="uv-keyboard__button__inner-wrapper__inner__text">{{ item }}</text>
+ </view>
+ <view class="uv-keyboard__button__inner-wrapper__disabled--mask" v-if="isDisabled(i,j)"></view>
+ </view>
+ <view
+ v-if="i === 3"
+ @touchstart="backspaceClick"
+ @touchend="clearTimer"
+ class="uv-keyboard__button__inner-wrapper"
+ >
+ <view
+ class="uv-keyboard__button__inner-wrapper__right"
+ hover-class="uv-hover-class"
+ :hover-stay-time="200"
+ >
+ <uv-icon
+ size="28"
+ name="backspace"
+ color="#303133"
+ ></uv-icon>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * keyboard 键盘组件
+ * @description 此为uvui自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。
+ * @tutorial https://www.uvui.cn/components/keyboard.html
+ * @property {Boolean} random 是否打乱键盘的顺序
+ * @event {Function} change 点击键盘触发
+ * @event {Function} backspace 点击退格键触发
+ * @example <uv-keyboard ref="uKeyboard" mode="car" v-model="show"></uv-keyboard>
+ */
+ export default {
+ name: "uv-keyboard",
+ mixins: [mpMixin, mixin, props],
+ emits: ['backspace','change','changeCarInputMode'],
+ data() {
+ return {
+ // 车牌输入时,abc=true为输入车牌号码,bac=false为输入省份中文简称
+ abc: false
+ };
+ },
+ computed: {
+ areaList() {
+ let data = [
+ '京',
+ '沪',
+ '粤',
+ '津',
+ '冀',
+ '豫',
+ '云',
+ '辽',
+ '黑',
+ '湘',
+ '皖',
+ '鲁',
+ '苏',
+ '浙',
+ '赣',
+ '鄂',
+ '桂',
+ '甘',
+ '晋',
+ '陕',
+ '蒙',
+ '吉',
+ '闽',
+ '贵',
+ '渝',
+ '川',
+ '青',
+ '琼',
+ '宁',
+ '挂',
+ '藏',
+ '港',
+ '澳',
+ '新',
+ '使',
+ '学'
+ ];
+ let tmp = [];
+ // 打乱顺序
+ if (this.random) data = this.$uv.randomArray(data);
+ // 切割成二维数组
+ tmp[0] = data.slice(0, 10);
+ tmp[1] = data.slice(10, 20);
+ tmp[2] = data.slice(20, 30);
+ tmp[3] = data.slice(30, 36);
+ return tmp;
+ },
+ engKeyBoardList() {
+ let data = [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 0,
+ 'Q',
+ 'W',
+ 'E',
+ 'R',
+ 'T',
+ 'Y',
+ 'U',
+ 'I',
+ 'O',
+ 'P',
+ 'A',
+ 'S',
+ 'D',
+ 'F',
+ 'G',
+ 'H',
+ 'J',
+ 'K',
+ 'L',
+ 'Z',
+ 'X',
+ 'C',
+ 'V',
+ 'B',
+ 'N',
+ 'M'
+ ];
+ let tmp = [];
+ if (this.random) data = this.$uv.randomArray(data);
+ tmp[0] = data.slice(0, 10);
+ tmp[1] = data.slice(10, 20);
+ tmp[2] = data.slice(20, 30);
+ tmp[3] = data.slice(30, 36);
+ return tmp;
+ },
+ isDisabled(i,j) {
+ return (i,j)=>{
+ let value = '';
+ if (this.abc) value = this.engKeyBoardList[i][j];
+ else value = this.areaList[i][j];
+ return this.disKeys.indexOf(value) > -1;
+ }
+ }
+ },
+ methods: {
+ // 点击键盘按钮
+ carInputClick(i, j) {
+ if(this.isDisabled(i,j)) return;
+ let value = '';
+ // 不同模式,获取不同数组的值
+ if (this.abc) value = this.engKeyBoardList[i][j];
+ else value = this.areaList[i][j];
+ // 如果允许自动切换,则将中文状态切换为英文
+ if (!this.abc && this.autoChange) this.$uv.sleep(200).then(() => this.abc = true)
+ this.$emit('change', value);
+ },
+ // 修改汽车牌键盘的输入模式,中文|英文
+ changeCarInputMode() {
+ this.abc = !this.abc;
+ this.$emit('changeCarInputMode',this.abc);
+ },
+ // 点击退格键
+ backspaceClick() {
+ this.$emit('backspace');
+ clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
+ this.timer = null;
+ this.timer = setInterval(() => {
+ this.$emit('backspace');
+ }, 250);
+ },
+ clearTimer() {
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ $show-hover: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-car-keyboard-background-color: rgb(224, 228, 230) !default;
+ $uv-car-keyboard-padding:6px 0 6px !default;
+ $uv-car-keyboard-button-inner-width:64rpx !default;
+ $uv-car-keyboard-button-inner-background-color:#FFFFFF !default;
+ $uv-car-keyboard-button-height:80rpx !default;
+ $uv-car-keyboard-button-inner-box-shadow:0 1px 0px #999992 !default;
+ $uv-car-keyboard-button-border-radius:4px !default;
+ $uv-car-keyboard-button-inner-margin:8rpx 5rpx !default;
+ $uv-car-keyboard-button-text-font-size:16px !default;
+ $uv-car-keyboard-button-text-color:$uv-main-color !default;
+ $uv-car-keyboard-center-inner-margin: 0 4rpx !default;
+ $uv-car-keyboard-special-button-width:134rpx !default;
+ $uv-car-keyboard-lang-font-size:16px !default;
+ $uv-car-keyboard-lang-color:$uv-main-color !default;
+ $uv-car-keyboard-active-color:$uv-primary !default;
+ $uv-car-keyboard-line-font-size:15px !default;
+ $uv-car-keyboard-line-color:$uv-main-color !default;
+ $uv-car-keyboard-line-margin:0 1px !default;
+ $uv-car-keyboard-uv-hover-class-background-color:#BBBCC6 !default;
+
+ .uv-keyboard {
+ @include flex(column);
+ justify-content: space-around;
+ background-color: $uv-car-keyboard-background-color;
+ align-items: stretch;
+ padding: $uv-car-keyboard-padding;
+
+ &__button {
+ @include flex;
+ justify-content: center;
+ flex: 1;
+ /* #ifndef APP-NVUE */
+ /* #endif */
+
+ &__inner-wrapper {
+ position: relative;
+ box-shadow: $uv-car-keyboard-button-inner-box-shadow;
+ margin: $uv-car-keyboard-button-inner-margin;
+ border-radius: $uv-car-keyboard-button-border-radius;
+
+ &__disabled--mask {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ width: $uv-car-keyboard-button-inner-width;
+ height: $uv-car-keyboard-button-height;
+ }
+
+ &__inner {
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ width: $uv-car-keyboard-button-inner-width;
+ background-color: $uv-car-keyboard-button-inner-background-color;
+ height: $uv-car-keyboard-button-height;
+ border-radius: $uv-car-keyboard-button-border-radius;
+
+ &--disabled {
+ opacity: 0.5;
+ }
+
+ &__text {
+ font-size: $uv-car-keyboard-button-text-font-size;
+ color: $uv-car-keyboard-button-text-color;
+ }
+ }
+
+ &__left,
+ &__right {
+ border-radius: $uv-car-keyboard-button-border-radius;
+ width: $uv-car-keyboard-special-button-width;
+ height: $uv-car-keyboard-button-height;
+ background-color: $uv-car-keyboard-uv-hover-class-background-color;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ box-shadow: $uv-car-keyboard-button-inner-box-shadow;
+ }
+
+ &__left {
+ &__line {
+ font-size: $uv-car-keyboard-line-font-size;
+ color: $uv-car-keyboard-line-color;
+ margin: $uv-car-keyboard-line-margin;
+ }
+
+ &__lang {
+ font-size: $uv-car-keyboard-lang-font-size;
+ color: $uv-car-keyboard-lang-color;
+
+ &--active {
+ color: $uv-car-keyboard-active-color;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .uv-hover-class {
+ background-color: $uv-car-keyboard-uv-hover-class-background-color;
+ }
+</style>
diff --git a/uni_modules/uv-keyboard/components/uv-keyboard-number/props.js b/uni_modules/uv-keyboard/components/uv-keyboard-number/props.js
new file mode 100644
index 0000000..60d63d4
--- /dev/null
+++ b/uni_modules/uv-keyboard/components/uv-keyboard-number/props.js
@@ -0,0 +1,19 @@
+export default {
+ props: {
+ // 键盘的类型,number-数字键盘,card-身份证键盘
+ mode: {
+ type: String,
+ default: 'number'
+ },
+ // 是否显示键盘的"."符号
+ dotDisabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否打乱键盘按键的顺序
+ random: {
+ type: Boolean,
+ default: false
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-keyboard/components/uv-keyboard-number/uv-keyboard-number.vue b/uni_modules/uv-keyboard/components/uv-keyboard-number/uv-keyboard-number.vue
new file mode 100644
index 0000000..b7c0b8b
--- /dev/null
+++ b/uni_modules/uv-keyboard/components/uv-keyboard-number/uv-keyboard-number.vue
@@ -0,0 +1,201 @@
+<template>
+ <view
+ class="uv-keyboard"
+ @touchmove.stop.prevent="noop"
+ >
+ <view
+ class="uv-keyboard__button-wrapper"
+ v-for="(item, index) in numList"
+ :key="index"
+ >
+ <view
+ class="uv-keyboard__button-wrapper__button"
+ :style="[itemStyle(index)]"
+ @tap="keyboardClick(item)"
+ hover-class="uv-hover-class"
+ :hover-stay-time="200"
+ >
+ <text class="uv-keyboard__button-wrapper__button__text">{{ item }}</text>
+ </view>
+ </view>
+ <view
+ class="uv-keyboard__button-wrapper"
+ >
+ <view
+ class="uv-keyboard__button-wrapper__button uv-keyboard__button-wrapper__button--gray"
+ hover-class="uv-hover-class"
+ :hover-stay-time="200"
+ @touchstart.stop="backspaceClick"
+ @touchend="clearTimer"
+ >
+ <uv-icon
+ name="backspace"
+ color="#303133"
+ size="28"
+ ></uv-icon>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * keyboard 键盘组件
+ * @description
+ * @tutorial
+ * @property {String} mode 键盘的类型,number-数字键盘,card-身份证键盘
+ * @property {Boolean} dotDisabled 是否显示键盘的"."符号
+ * @property {Boolean} random 是否打乱键盘按键的顺序
+ * @event {Function} change 点击键盘触发
+ * @event {Function} backspace 点击退格键触发
+ * @example
+ */
+ export default {
+ mixins: [mpMixin, mixin, props],
+ emits: ['backspace','change'],
+ data() {
+ return {
+ backspace: 'backspace', // 退格键内容
+ dot: '.', // 点
+ timer: null, // 长按多次删除的事件监听
+ cardX: 'X' // 身份证的X符号
+ };
+ },
+ computed: {
+ // 键盘需要显示的内容
+ numList() {
+ let tmp = [];
+ if (this.dotDisabled && this.mode == 'number') {
+ if (!this.random) {
+ return [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+ } else {
+ return this.$uv.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
+ }
+ } else if (!this.dotDisabled && this.mode == 'number') {
+ if (!this.random) {
+ return [1, 2, 3, 4, 5, 6, 7, 8, 9, this.dot, 0];
+ } else {
+ return this.$uv.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, this.dot, 0]);
+ }
+ } else if (this.mode == 'card') {
+ if (!this.random) {
+ return [1, 2, 3, 4, 5, 6, 7, 8, 9, this.cardX, 0];
+ } else {
+ return this.$uv.randomArray([1, 2, 3, 4, 5, 6, 7, 8, 9, this.cardX, 0]);
+ }
+ }
+ },
+ // 按键的样式,在非乱序&&数字键盘&&不显示点按钮时,index为9时,按键占位两个空间
+ itemStyle() {
+ return index => {
+ let style = {};
+ if (this.mode == 'number' && this.dotDisabled && index == 9) style.width = '464rpx';
+ return style;
+ };
+ },
+ // 是否让按键显示灰色,只在非乱序&&数字键盘&&且允许点按键的时候
+ btnBgGray() {
+ return index => {
+ if (!this.random && index == 9 && (this.mode != 'number' || (this.mode == 'number' && !this
+ .dotDisabled))) return true;
+ else return false;
+ };
+ },
+ },
+ created() {
+
+ },
+ methods: {
+ // 点击退格键
+ backspaceClick() {
+ this.$emit('backspace');
+ clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
+ this.timer = null;
+ this.timer = setInterval(() => {
+ this.$emit('backspace');
+ }, 250);
+ },
+ clearTimer() {
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+ // 获取键盘显示的内容
+ keyboardClick(val) {
+ // 允许键盘显示点模式和触发非点按键时,将内容转为数字类型
+ if (!this.dotDisabled && val != this.dot && val != this.cardX) val = Number(val);
+ this.$emit('change', val);
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ $show-hover: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-number-keyboard-background-color:rgb(224, 228, 230) !default;
+ $uv-number-keyboard-padding:8px 10rpx 8px 10rpx !default;
+ $uv-number-keyboard-button-width:222rpx !default;
+ $uv-number-keyboard-button-margin:4px 6rpx !default;
+ $uv-number-keyboard-button-border-top-left-radius:4px !default;
+ $uv-number-keyboard-button-border-top-right-radius:4px !default;
+ $uv-number-keyboard-button-border-bottom-left-radius:4px !default;
+ $uv-number-keyboard-button-border-bottom-right-radius:4px !default;
+ $uv-number-keyboard-button-height: 90rpx!default;
+ $uv-number-keyboard-button-background-color:#FFFFFF !default;
+ $uv-number-keyboard-button-box-shadow:0 2px 0px #BBBCBE !default;
+ $uv-number-keyboard-text-font-size:20px !default;
+ $uv-number-keyboard-text-font-weight:500 !default;
+ $uv-number-keyboard-text-color:$uv-main-color !default;
+ $uv-number-keyboard-gray-background-color:rgb(200, 202, 210) !default;
+ $uv-number-keyboard-uv-hover-class-background-color: #BBBCC6 !default;
+
+ .uv-keyboard {
+ @include flex;
+ flex-direction: row;
+ justify-content: space-around;
+ background-color: $uv-number-keyboard-background-color;
+ flex-wrap: wrap;
+ padding: $uv-number-keyboard-padding;
+
+ &__button-wrapper {
+ box-shadow: $uv-number-keyboard-button-box-shadow;
+ margin: $uv-number-keyboard-button-margin;
+ border-top-left-radius: $uv-number-keyboard-button-border-top-left-radius;
+ border-top-right-radius: $uv-number-keyboard-button-border-top-right-radius;
+ border-bottom-left-radius: $uv-number-keyboard-button-border-bottom-left-radius;
+ border-bottom-right-radius: $uv-number-keyboard-button-border-bottom-right-radius;
+
+ &__button {
+ width: $uv-number-keyboard-button-width;
+ height: $uv-number-keyboard-button-height;
+ background-color: $uv-number-keyboard-button-background-color;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ border-top-left-radius: $uv-number-keyboard-button-border-top-left-radius;
+ border-top-right-radius: $uv-number-keyboard-button-border-top-right-radius;
+ border-bottom-left-radius: $uv-number-keyboard-button-border-bottom-left-radius;
+ border-bottom-right-radius: $uv-number-keyboard-button-border-bottom-right-radius;
+
+ &__text {
+ font-size: $uv-number-keyboard-text-font-size;
+ font-weight: $uv-number-keyboard-text-font-weight;
+ color: $uv-number-keyboard-text-color;
+ }
+
+ &--gray {
+ background-color: $uv-number-keyboard-gray-background-color;
+ }
+ }
+ }
+ }
+
+ .uv-hover-class {
+ background-color: $uv-number-keyboard-uv-hover-class-background-color;
+ }
+</style>
diff --git a/uni_modules/uv-keyboard/components/uv-keyboard/props.js b/uni_modules/uv-keyboard/components/uv-keyboard/props.js
new file mode 100644
index 0000000..05dd76a
--- /dev/null
+++ b/uni_modules/uv-keyboard/components/uv-keyboard/props.js
@@ -0,0 +1,95 @@
+export default {
+ props: {
+ // 键盘的类型,number-数字键盘,card-身份证键盘,car-车牌号键盘
+ mode: {
+ type: String,
+ default: 'number'
+ },
+ // 是否显示键盘的"."符号
+ dotDisabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示顶部工具条
+ tooltip: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示工具条中间的提示
+ showTips: {
+ type: Boolean,
+ default: true
+ },
+ // 工具条中间的提示文字
+ tips: {
+ type: String,
+ default: ''
+ },
+ // 是否显示工具条左边的"取消"按钮
+ showCancel: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示工具条右边的"完成"按钮
+ showConfirm: {
+ type: Boolean,
+ default: true
+ },
+ // 是否打乱键盘按键的顺序
+ random: {
+ type: Boolean,
+ default: false
+ },
+ // 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距
+ safeAreaInsetBottom: {
+ type: Boolean,
+ default: true
+ },
+ // 是否允许通过点击遮罩关闭键盘
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: true
+ },
+ // 是否允许点击确认按钮关闭组件
+ closeOnClickConfirm: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示遮罩,某些时候数字键盘时,用户希望看到自己的数值,所以可能不想要遮罩
+ overlay: {
+ type: Boolean,
+ default: true
+ },
+ // z-index值
+ zIndex: {
+ type: [String, Number],
+ default: 10075
+ },
+ // 取消按钮的文字
+ cancelText: {
+ type: String,
+ default: '取消'
+ },
+ // 确认按钮的文字
+ confirmText: {
+ type: String,
+ default: '确定'
+ },
+ // 输入一个中文后,是否自动切换到英文
+ autoChange: {
+ type: Boolean,
+ default: false
+ },
+ // 被禁用的键
+ disKeys: {
+ type: Array,
+ default: ()=>[]
+ },
+ // 是否自定义abc
+ customabc: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.keyboard
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-keyboard/components/uv-keyboard/uv-keyboard.vue b/uni_modules/uv-keyboard/components/uv-keyboard/uv-keyboard.vue
new file mode 100644
index 0000000..60b15bf
--- /dev/null
+++ b/uni_modules/uv-keyboard/components/uv-keyboard/uv-keyboard.vue
@@ -0,0 +1,180 @@
+<template>
+ <uv-popup
+ ref="keyboardPopup"
+ mode="bottom"
+ :overlay="overlay"
+ :closeOnClickOverlay="closeOnClickOverlay"
+ :safeAreaInsetBottom="safeAreaInsetBottom"
+ :zIndex="zIndex"
+ :customStyle="{ backgroundColor: 'rgb(214, 218, 220)' }"
+ @change="popupChange"
+ >
+ <view class="uv-keyboard">
+ <slot />
+ <view
+ class="uv-keyboard__tooltip"
+ v-if="tooltip"
+ >
+ <view
+ hover-class="uv-hover-class"
+ :hover-stay-time="100"
+ >
+ <text
+ class="uv-keyboard__tooltip__item uv-keyboard__tooltip__cancel"
+ v-if="showCancel"
+ @tap="onCancel"
+ >{{showCancel && cancelText}}</text>
+ </view>
+ <view>
+ <text
+ v-if="showTips"
+ class="uv-keyboard__tooltip__item uv-keyboard__tooltip__tips"
+ >{{tips ? tips : mode == 'number' ? '数字键盘' : mode == 'card' ? '身份证键盘' : '车牌号键盘'}}</text>
+ </view>
+ <view
+ hover-class="uv-hover-class"
+ :hover-stay-time="100"
+ >
+ <text
+ v-if="showConfirm"
+ @tap="onConfirm"
+ class="uv-keyboard__tooltip__item uv-keyboard__tooltip__submit"
+ hover-class="uv-hover-class"
+ >{{showConfirm && confirmText}}</text>
+ </view>
+ </view>
+ <template v-if="mode == 'number' || mode == 'card'">
+ <uv-keyboard-number
+ :random="random"
+ @backspace="backspace"
+ @change="change"
+ :mode="mode"
+ :dotDisabled="dotDisabled"
+ ></uv-keyboard-number>
+ </template>
+ <template v-else>
+ <uv-keyboard-car
+ ref="uvKeyboardCarRef"
+ :random="random"
+ :autoChange="autoChange"
+ :disKeys="disKeys"
+ :customabc="customabc"
+ @backspace="backspace"
+ @change="change"
+ @changeCarInputMode="changeCarInputMode"
+ >
+ <slot name="abc"></slot>
+ </uv-keyboard-car>
+ </template>
+ </view>
+ </uv-popup>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * keyboard 键盘
+ * @description 此为uViw自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3中模式,都有可以打乱按键顺序的选项。
+ * @tutorial https://www.uvui.cn/components/keyboard.html
+ * @property {String} mode 键盘类型,见官网基本使用的说明 (默认 'number' )
+ * @property {Boolean} dotDisabled 是否显示"."按键,只在mode=number时有效 (默认 false )
+ * @property {Boolean} tooltip 是否显示键盘顶部工具条 (默认 true )
+ * @property {Boolean} showTips 是否显示工具条中间的提示 (默认 true )
+ * @property {String} tips 工具条中间的提示文字,见上方基本使用的说明,如不需要,请传""空字符
+ * @property {Boolean} showCancel 是否显示工具条左边的"取消"按钮 (默认 true )
+ * @property {Boolean} showConfirm 是否显示工具条右边的"完成"按钮( 默认 true )
+ * @property {Boolean} random 是否打乱键盘按键的顺序 (默认 false )
+ * @property {Boolean} safeAreaInsetBottom 是否开启底部安全区适配 (默认 true )
+ * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩收起键盘 (默认 true )
+ * @property {Boolean} show 控制键盘的弹出与收起(默认 false )
+ * @property {Boolean} overlay 是否显示遮罩 (默认 true )
+ * @property {String | Number} zIndex 弹出键盘的z-index值 (默认 1075 )
+ * @property {String} cancelText 取消按钮的文字 (默认 '取消' )
+ * @property {String} confirmText 确认按钮的文字 (默认 '确认' )
+ * @property {Object} customStyle 自定义样式,对象形式
+ * @event {Function} change 按键被点击(不包含退格键被点击)
+ * @event {Function} cancel 键盘顶部工具条左边的"取消"按钮被点击
+ * @event {Function} confirm 键盘顶部工具条右边的"完成"按钮被点击
+ * @event {Function} backspace 键盘退格键被点击
+ * @example <uv-keyboard mode="number" v-model="show"></uv-keyboard>
+ */
+ export default {
+ name: "uv-keyboard",
+ mixins: [mpMixin, mixin, props],
+ emits: ['close','change','confirm','cancel','backspace','changeCarInputMode'],
+ methods: {
+ open() {
+ this.$refs.keyboardPopup.open();
+ },
+ close() {
+ this.$refs.keyboardPopup.close();
+ },
+ popupChange(e) {
+ if(!e.show) this.$emit('close');
+ },
+ change(e) {
+ this.$emit('change', e);
+ },
+ // 输入完成
+ onConfirm() {
+ this.$emit('confirm');
+ if(this.closeOnClickConfirm) this.close();
+ },
+ // 取消输入
+ onCancel() {
+ this.$emit('cancel');
+ this.close();
+ },
+ // 退格键
+ backspace() {
+ this.$emit('backspace');
+ },
+ // car模式切换中文|英文方法
+ changeCarInputMode(e) {
+ this.$emit('changeCarInputMode',e);
+ },
+ changeCarMode() {
+ this.$refs.uvKeyboardCarRef && this.$refs.uvKeyboardCarRef.changeCarInputMode();
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-hover: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-keyboard {
+
+ &__tooltip {
+ @include flex;
+ justify-content: space-between;
+ background-color: #FFFFFF;
+ padding: 14px 12px;
+
+ &__item {
+ color: #333333;
+ flex: 1;
+ text-align: center;
+ font-size: 15px;
+ }
+
+ &__submit {
+ text-align: right;
+ color: $uv-primary;
+ }
+
+ &__cancel {
+ text-align: left;
+ color: #888888;
+ }
+
+ &__tips {
+ color: $uv-tips-color;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-keyboard/package.json b/uni_modules/uv-keyboard/package.json
new file mode 100644
index 0000000..69844c6
--- /dev/null
+++ b/uni_modules/uv-keyboard/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-keyboard",
+ "displayName": "uv-keyboard 键盘 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "uv-keyboard 该组件为自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。",
+ "keywords": [
+ "uv-keyboard",
+ "uvui",
+ "uv-ui",
+ "keyboard",
+ "键盘"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-popup",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-keyboard/readme.md b/uni_modules/uv-keyboard/readme.md
new file mode 100644
index 0000000..9cbef6c
--- /dev/null
+++ b/uni_modules/uv-keyboard/readme.md
@@ -0,0 +1,19 @@
+## Keyboard 键盘
+
+> **组件名:uv-keyboard**
+
+该组件为自定义的键盘面板,内含了数字键盘,车牌号键,身份证号键盘3种模式,都有可以打乱按键顺序的选项。
+
+# <a href="https://www.uvui.cn/components/keyboard.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-line-progress/changelog.md b/uni_modules/uv-line-progress/changelog.md
new file mode 100644
index 0000000..975acce
--- /dev/null
+++ b/uni_modules/uv-line-progress/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-06-20)
+1. 适配height参数携带单位
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-line-progress 线形进度条
diff --git a/uni_modules/uv-line-progress/components/uv-line-progress/props.js b/uni_modules/uv-line-progress/components/uv-line-progress/props.js
new file mode 100644
index 0000000..23138c7
--- /dev/null
+++ b/uni_modules/uv-line-progress/components/uv-line-progress/props.js
@@ -0,0 +1,29 @@
+export default {
+ props: {
+ // 激活部分的颜色
+ activeColor: {
+ type: String,
+ default: '#19be6b'
+ },
+ inactiveColor: {
+ type: String,
+ default: '#ececec'
+ },
+ // 进度百分比,数值
+ percentage: {
+ type: [String, Number],
+ default: 0
+ },
+ // 是否在进度条内部显示百分比的值
+ showText: {
+ type: Boolean,
+ default: true
+ },
+ // 进度条的高度,单位px
+ height: {
+ type: [String, Number],
+ default: 12
+ },
+ ...uni.$uv?.props?.lineProgress
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-line-progress/components/uv-line-progress/uv-line-progress.vue b/uni_modules/uv-line-progress/components/uv-line-progress/uv-line-progress.vue
new file mode 100644
index 0000000..229c2aa
--- /dev/null
+++ b/uni_modules/uv-line-progress/components/uv-line-progress/uv-line-progress.vue
@@ -0,0 +1,146 @@
+<template>
+ <view
+ class="uv-line-progress"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <view
+ class="uv-line-progress__background"
+ ref="uv-line-progress__background"
+ :style="[{
+ backgroundColor: inactiveColor,
+ height: $uv.addUnit($uv.getPx(height))
+ }]"
+ >
+ </view>
+ <view
+ class="uv-line-progress__line"
+ :style="[progressStyle]"
+ >
+ <slot>
+ <text v-if="showText && percentage >= 10" class="uv-line-progress__text">{{innserPercentage + '%'}}</text>
+ </slot>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * lineProgress 线型进度条
+ * @description 展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。
+ * @tutorial https://www.uvui.cn/components/lineProgress.html
+ * @property {String} activeColor 激活部分的颜色 ( 默认 '#19be6b' )
+ * @property {String} inactiveColor 背景色 ( 默认 '#ececec' )
+ * @property {String | Number} percentage 进度百分比,数值 ( 默认 0 )
+ * @property {Boolean} showText 是否在进度条内部显示百分比的值 ( 默认 true )
+ * @property {String | Number} height 进度条的高度,单位px ( 默认 12 )
+ *
+ * @example <uv-line-progress :percent="70" :show-percent="true"></uv-line-progress>
+ */
+ export default {
+ name: "uv-line-progress",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ lineWidth: 0,
+ }
+ },
+ watch: {
+ percentage(n) {
+ this.resizeProgressWidth()
+ }
+ },
+ computed: {
+ progressStyle() {
+ let style = {}
+ style.width = this.lineWidth
+ style.backgroundColor = this.activeColor
+ style.height = this.$uv.addUnit(this.$uv.getPx(this.height))
+ return style
+ },
+ innserPercentage() {
+ // 控制范围在0-100之间
+ return this.$uv.range(0, 100, this.percentage)
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.$uv.sleep(20).then(() => {
+ this.resizeProgressWidth()
+ })
+ },
+ getProgressWidth() {
+ // #ifndef APP-NVUE
+ return this.$uvGetRect('.uv-line-progress__background')
+ // #endif
+
+ // #ifdef APP-NVUE
+ // 返回一个promise
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs['uv-line-progress__background'], (res) => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ resizeProgressWidth() {
+ this.getProgressWidth().then(size => {
+ const {
+ width
+ } = size
+ // 通过设置的percentage值,计算其所占总长度的百分比
+ this.lineWidth = width * this.innserPercentage / 100 + 'px'
+ })
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-line-progress {
+ align-items: stretch;
+ position: relative;
+ @include flex(row);
+ flex: 1;
+ overflow: hidden;
+ border-radius: 100px;
+
+ &__background {
+ background-color: #ececec;
+ border-radius: 100px;
+ flex: 1;
+ }
+
+ &__line {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ align-items: center;
+ @include flex(row);
+ color: #ffffff;
+ border-radius: 100px;
+ transition: width 0.5s ease;
+ justify-content: flex-end;
+ }
+
+ &__text {
+ font-size: 10px;
+ align-items: center;
+ text-align: right;
+ color: #FFFFFF;
+ margin-right: 5px;
+ transform: scale(0.9);
+ }
+ }
+</style>
diff --git a/uni_modules/uv-line-progress/package.json b/uni_modules/uv-line-progress/package.json
new file mode 100644
index 0000000..01e550e
--- /dev/null
+++ b/uni_modules/uv-line-progress/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-line-progress",
+ "displayName": "uv-line-progress 线形进度条 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "uv-line-progress 该组件展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。",
+ "keywords": [
+ "uv-line-progress",
+ "uvui",
+ "uv-ui",
+ "progress",
+ "进度条"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-line-progress/readme.md b/uni_modules/uv-line-progress/readme.md
new file mode 100644
index 0000000..98e5d49
--- /dev/null
+++ b/uni_modules/uv-line-progress/readme.md
@@ -0,0 +1,11 @@
+## LineProgress 线形进度条
+
+> **组件名:uv-line-progress**
+
+展示操作或任务的当前进度,比如上传文件,是一个线形的进度条。
+
+### <a href="https://www.uvui.cn/components/lineProgress.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-link/changelog.md b/uni_modules/uv-link/changelog.md
new file mode 100644
index 0000000..ce52f84
--- /dev/null
+++ b/uni_modules/uv-link/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-08-13)
+1. 修复报错的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-link 超链接组件
diff --git a/uni_modules/uv-link/components/uv-link/props.js b/uni_modules/uv-link/components/uv-link/props.js
new file mode 100644
index 0000000..b3f56a1
--- /dev/null
+++ b/uni_modules/uv-link/components/uv-link/props.js
@@ -0,0 +1,40 @@
+export default {
+ props: {
+ // 文字颜色
+ color: {
+ type: String,
+ default: ''
+ },
+ // 字体大小,单位px
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 是否显示下划线
+ underLine: {
+ type: Boolean,
+ default: false
+ },
+ // 要跳转的链接
+ href: {
+ type: String,
+ default: ''
+ },
+ // 小程序中复制到粘贴板的提示语
+ mpTips: {
+ type: String,
+ default: '链接已复制,请在浏览器打开'
+ },
+ // 下划线颜色
+ lineColor: {
+ type: String,
+ default: ''
+ },
+ // 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色
+ text: {
+ type: String,
+ default: ''
+ },
+ ...uni.$uv?.props?.link
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-link/components/uv-link/uv-link.vue b/uni_modules/uv-link/components/uv-link/uv-link.vue
new file mode 100644
index 0000000..4c9258e
--- /dev/null
+++ b/uni_modules/uv-link/components/uv-link/uv-link.vue
@@ -0,0 +1,83 @@
+<template>
+ <text
+ class="uv-link"
+ @tap.stop="openLink"
+ :style="[linkStyle, $uv.addStyle(customStyle)]"
+ >{{text}}</text>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * link 超链接
+ * @description 该组件为超链接组件,在不同平台有不同表现形式:在APP平台会通过plus环境打开内置浏览器,在小程序中把链接复制到粘贴板,同时提示信息,在H5中通过window.open打开链接。
+ * @tutorial https://www.uvui.cn/components/link.html
+ * @property {String} color 文字颜色 (默认 color['uv-primary'] )
+ * @property {String | Number} fontSize 字体大小,单位px (默认 15 )
+ * @property {Boolean} underLine 是否显示下划线 (默认 false )
+ * @property {String} href 跳转的链接,要带上http(s)
+ * @property {String} mpTips 各个小程序平台把链接复制到粘贴板后的提示语(默认“链接已复制,请在浏览器打开”)
+ * @property {String} lineColor 下划线颜色,默认同color参数颜色
+ * @property {String} text 超链接的问题,不使用slot形式传入,是因为nvue下无法修改颜色
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @example <uv-link href="http://www.uvui.cn">蜀道难,难于上青天</uv-link>
+ */
+ export default {
+ name: "uv-link",
+ emits:['click'],
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ linkStyle() {
+ const style = {
+ color: this.color,
+ fontSize: this.$uv.addUnit(this.fontSize),
+ // line-height设置为比字体大小多2px
+ lineHeight: this.$uv.addUnit(this.$uv.getPx(this.fontSize) + 2),
+ textDecoration: this.underLine ? 'underline' : 'none'
+ }
+ return style
+ }
+ },
+ methods: {
+ openLink() {
+ // #ifdef APP-PLUS
+ plus.runtime.openURL(this.href)
+ // #endif
+ // #ifdef H5
+ window.open(this.href)
+ // #endif
+ // #ifdef MP
+ uni.setClipboardData({
+ data: this.href,
+ success: () => {
+ uni.hideToast();
+ this.$nextTick(() => {
+ this.$uv.toast(this.mpTips);
+ })
+ }
+ });
+ // #endif
+ this.$emit('click')
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-link-line-height:1 !default;
+
+ .uv-link {
+ /* #ifndef APP-NVUE */
+ line-height: $uv-link-line-height;
+ /* #endif */
+ @include flex;
+ flex-wrap: wrap;
+ flex: 1;
+ color: $uv-primary;
+ }
+</style>
diff --git a/uni_modules/uv-link/package.json b/uni_modules/uv-link/package.json
new file mode 100644
index 0000000..1b115f4
--- /dev/null
+++ b/uni_modules/uv-link/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-link",
+ "displayName": "uv-link 超链接 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "uv-link 该组件为超链接组件",
+ "keywords": [
+ "uv-link",
+ "uvui",
+ "uv-ui",
+ "link",
+ "超链接"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-link/readme.md b/uni_modules/uv-link/readme.md
new file mode 100644
index 0000000..6e8ce92
--- /dev/null
+++ b/uni_modules/uv-link/readme.md
@@ -0,0 +1,11 @@
+## Link 超链接
+
+> **组件名:uv-link**
+
+该组件为超链接组件,在不同平台有不同表现形式。
+
+### <a href="https://www.uvui.cn/components/link.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-list/changelog.md b/uni_modules/uv-list/changelog.md
new file mode 100644
index 0000000..6c130f0
--- /dev/null
+++ b/uni_modules/uv-list/changelog.md
@@ -0,0 +1,25 @@
+## 1.0.9(2023-11-10)
+1. 修复设置ellipsis不生效的BUG
+## 1.0.8(2023-09-20)
+1. listItem优化可使用customStyle变量进行样式控制
+## 1.0.7(2023-08-29)
+1. 修复边框的BUG
+## 1.0.6(2023-08-16)
+1. 修复switch开关返回undefined的问题
+2. 优化初始化可能导致的闪动
+## 1.0.5(2023-08-07)
+1. 修复分包页面在ios端,nvue编译不能滚动的BUG
+## 1.0.4(2023-08-04)
+1. nvue修复 触底不触发事件的BUG
+2. 更新文档说明事件触发
+## 1.0.3(2023-07-28)
+1. 修改可能造成样式污染的BUG
+## 1.0.2(2023-07-26)
+1. 全面重构,用法与之前保持一致,参数全部变化
+2. 新增多个功能参数,方便一键构建列表
+3. List列表组件,包含基本列表样式、默认插槽机制、可扩展插槽机制、长列表性能优化、多端兼容。
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-list 列表
diff --git a/uni_modules/uv-list/components/uv-list-item/uv-list-item.vue b/uni_modules/uv-list/components/uv-list-item/uv-list-item.vue
new file mode 100644
index 0000000..193b48d
--- /dev/null
+++ b/uni_modules/uv-list/components/uv-list-item/uv-list-item.vue
@@ -0,0 +1,535 @@
+<template>
+ <!-- #ifdef APP-NVUE -->
+ <cell :keep-scroll-position="keepScrollPosition">
+ <!-- #endif -->
+ <view
+ :class="{ 'uv-list-item--disabled': disabled }"
+ :style="[$uv.addStyle(customStyle),{'background-color':customStyle.backgroundColor?customStyle.backgroundColor:'#fff'}]"
+ :hover-class="(!clickable && !link) || disabled || showSwitch ? '' : 'uv-list-item--hover'"
+ class="uv-list-item" @click="onClick">
+ <view v-if="!isFirstChild" class="border--left" :class="{ 'uv-list--border': border }"></view>
+ <view class="uv-list-item__wrapper">
+ <slot>
+ <view class="uv-list-item__container"
+ :class="{ 'container--right': showArrow || link, 'flex--direction': directionData === 'column'}"
+ :style="{paddingTop:padding.top,paddingLeft:padding.left,paddingRight:padding.right,paddingBottom:padding.bottom}">
+ <slot name="header">
+ <view class="uv-list-item__header">
+ <view v-if="thumb" class="uv-list-item__icon">
+ <image :src="thumb" class="uv-list-item__icon-img" :class="['uv-list--' + thumbSize]" />
+ </view>
+ <view v-else-if="showExtraIcon" class="uv-list-item__icon">
+ <uv-icon :name="extraIcon.icon" :customPrefix="extraIcon.customPrefix" :color="extraIcon.color" :size="extraIcon.size" />
+ </view>
+ </view>
+ </slot>
+ <slot name="body">
+ <view class="uv-list-item__content"
+ :class="{ 'uv-list-item__content--center': thumb || showExtraIcon || showBadge || showSwitch }">
+ <text v-if="title" class="uv-list-item__content-title"
+ :class="[ellipsis && `uv-line-${ellipsis}`]">{{ title }}</text>
+ <text v-if="note" class="uv-list-item__content-note">{{ note }}</text>
+ </view>
+ </slot>
+ <slot name="footer">
+ <view v-if="rightText || showBadge || showSwitch" class="uv-list-item__extra"
+ :class="{ 'flex--justify': directionData === 'column' }">
+ <text v-if="rightText" class="uv-list-item__extra-text">{{ rightText }}</text>
+ <uv-badge
+ v-if="showBadge"
+ :show="!!(badge.show || badge.isDot || badge.value)"
+ :isDot="badge.isDot"
+ :value="badge.value"
+ :max="badge.max"
+ :type="badge.type"
+ :showZero="badge.showZero"
+ :bgColor="badge.bgColor"
+ :color="badge.color"
+ :shape="badge.shape"
+ :numberType="badge.numberType"
+ :inverted="badge.inverted"
+ customStyle="margin-left: 4px;"
+ ></uv-badge>
+ <uv-switch v-if="showSwitch" :value="switchChecked" :disabled="disabled" @change="onSwitchChange"></uv-switch>
+ </view>
+ </slot>
+ </view>
+ </slot>
+ </view>
+ <uv-icon v-if="showArrow || link" size="34rpx" class="uv-icon-wrapper" color="#bbb" name="arrow-right" />
+ </view>
+ <!-- #ifdef APP-NVUE -->
+ </cell>
+ <!-- #endif -->
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ /**
+ * ListItem 列表子组件
+ * @description 列表子组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=24
+ * @property {String} title 标题
+ * @property {String} note 描述
+ * @property {String} thumb 左侧缩略图,若thumb有值,则不会显示扩展图标
+ * @property {String} thumbSize = [lg|base|sm] 略缩图大小
+ * @value lg 大图
+ * @value base 一般
+ * @value sm 小图
+ * @property {String} rightText 右侧文字内容
+ * @property {Boolean} disabled = [true|false] 是否禁用
+ * @property {Boolean} clickable = [true|false] 是否开启点击反馈
+ * @property {String} link = [navigateTo|redirectTo|reLaunch|switchTab] 是否展示右侧箭头并开启点击反馈
+ * @value navigateTo 同 uni.navigateTo()
+ * @value redirectTo 同 uni.redirectTo()
+ * @value reLaunch 同 uni.reLaunch()
+ * @value switchTab 同 uni.switchTab()
+ * @property {String | PageURIString} to 跳转目标页面
+ * @property {Boolean} showBadge = [true|false] 是否显示数字角标
+ * @property {Object} badge 扩展数字角标的参数,格式为 :badge="{value: 122}"
+ * @property {Boolean} showSwitch = [true|false] 是否显示Switch
+ * @property {Boolean} switchChecked = [true|false] Switch是否被选中
+ * @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标
+ * @property {Object} extraIcon 扩展图标参数,格式为 :extraIcon="{icon: 'photo',size: '30px'}"
+ * @property {String} direction = [row|column] 排版方向
+ * @value row 水平排列
+ * @value column 垂直排列
+ * @event {Function} click 点击 uniListItem 触发事件
+ * @event {Function} switchChange 点击切换 Switch 时触发
+ */
+ export default {
+ name: 'uv-list-item',
+ mixins: [mpMixin, mixin],
+ emits: ['click', 'switchChange'],
+ props: {
+ direction: {
+ type: String,
+ default: 'row'
+ },
+ title: {
+ type: String,
+ default: ''
+ },
+ note: {
+ type: String,
+ default: ''
+ },
+ ellipsis: {
+ type: [Number, String],
+ default: 0
+ },
+ disabled: {
+ type: [Boolean, String],
+ default: false
+ },
+ clickable: {
+ type: Boolean,
+ default: false
+ },
+ showArrow: {
+ type: [Boolean, String],
+ default: false
+ },
+ link: {
+ type: [Boolean, String],
+ default: false
+ },
+ to: {
+ type: String,
+ default: ''
+ },
+ showSwitch: {
+ type: [Boolean, String],
+ default: false
+ },
+ switchChecked: {
+ type: [Boolean, String],
+ default: false
+ },
+ showBadge: {
+ type: [Boolean, String],
+ default: false
+ },
+ badge: {
+ type: Object,
+ default () {
+ return {}
+ }
+ },
+ rightText: {
+ type: String,
+ default: ''
+ },
+ thumb: {
+ type: String,
+ default: ''
+ },
+ thumbSize: {
+ type: String,
+ default: 'base'
+ },
+ showExtraIcon: {
+ type: [Boolean, String],
+ default: false
+ },
+ extraIcon: {
+ type: Object,
+ default () {
+ return {
+ name: '',
+ color: '#000000',
+ size: 20,
+ customPrefix: ''
+ };
+ }
+ },
+ border: {
+ type: Boolean,
+ default: false
+ },
+ customStyle: {
+ type: Object,
+ default () {
+ return {
+ padding: '',
+ backgroundColor: '#FFFFFF'
+ }
+ }
+ },
+ keepScrollPosition: {
+ type: Boolean,
+ default: false
+ }
+ },
+ computed: {
+ directionData(){
+ return this.direction ? this.direction : (this.parentData.direction ? this.parentData.direction : 'row');
+ }
+ },
+ watch: {
+ 'customStyle.padding': {
+ handler(padding) {
+ if(padding) this.setPadding(padding);
+ },
+ immediate: true
+ }
+ },
+ data() {
+ return {
+ isFirstChild: false,
+ padding: {
+ top: "",
+ right: "",
+ bottom: "",
+ left: ""
+ },
+ parentData: {
+ direction: 'row',
+ padding: 0
+ }
+ };
+ },
+ created() {
+ this.updateParentData();
+ },
+ mounted() {
+ this.init();
+ this.list = this.getForm()
+ // 判断是否存在 uv-list 组件
+ if (this.list) {
+ if (!this.list.firstChildAppend) {
+ this.list.firstChildAppend = true;
+ this.isFirstChild = true;
+ }
+ }
+ },
+ methods: {
+ init(){
+ if (!this.parent) {
+ this.$uv.error('uv-list-item必须搭配uv-list组件使用');
+ }
+ this.$nextTick(()=>{
+ if(!(this.padding.top || this.padding.right|| this.padding.bottom|| this.padding.left)){
+ this.setPadding(this.parentData.padding);
+ }
+ })
+ },
+ updateParentData() {
+ this.getParentData('uv-list');
+ },
+ setPadding(padding){
+ if(typeof padding == 'number'){
+ padding += ''
+ }
+ let paddingArr = padding.split(' ')
+ if (paddingArr.length === 1) {
+ const allPadding = paddingArr[0]
+ this.padding = {
+ "top": allPadding,
+ "right": allPadding,
+ "bottom": allPadding,
+ "left": allPadding
+ }
+ } else if (paddingArr.length === 2) {
+ const [verticalPadding, horizontalPadding] = paddingArr;
+ this.padding = {
+ "top": verticalPadding,
+ "right": horizontalPadding,
+ "bottom": verticalPadding,
+ "left": horizontalPadding
+ }
+ } else if (paddingArr.length === 4) {
+ const [topPadding, rightPadding, bottomPadding, leftPadding] = paddingArr;
+ this.padding = {
+ "top": topPadding,
+ "right": rightPadding,
+ "bottom": bottomPadding,
+ "left": leftPadding
+ }
+ }
+ },
+ /**
+ * 获取父元素实例
+ */
+ getForm(name = 'uniList') {
+ let parent = this.$parent;
+ let parentName = parent.$options.name;
+ while (parentName !== name) {
+ parent = parent.$parent;
+ if (!parent) return false
+ parentName = parent.$options.name;
+ }
+ return parent;
+ },
+ onClick() {
+ if (this.to !== '') {
+ this.openPage();
+ return;
+ }
+ if (this.clickable || this.link) {
+ this.$emit('click', {
+ data: {}
+ });
+ }
+ },
+ onSwitchChange(e) {
+ this.$emit('switchChange', e);
+ },
+ openPage() {
+ if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].indexOf(this.link) !== -1) {
+ this.pageApi(this.link);
+ } else {
+ this.pageApi('navigateTo');
+ }
+ },
+ pageApi(api) {
+ let callback = {
+ url: this.to,
+ success: res => {
+ this.$emit('click', {
+ data: res
+ });
+ },
+ fail: err => {
+ this.$emit('click', {
+ data: err
+ });
+ }
+ }
+ switch (api) {
+ case 'navigateTo':
+ uni.navigateTo(callback)
+ break
+ case 'redirectTo':
+ uni.redirectTo(callback)
+ break
+ case 'reLaunch':
+ uni.reLaunch(callback)
+ break
+ case 'switchTab':
+ uni.switchTab(callback)
+ break
+ default:
+ uni.navigateTo(callback)
+ }
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ $show-lines: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ $uv-font-size-sm:12px;
+ $uv-font-size-base:14px;
+ $uv-font-size-lg:16px;
+ $uv-spacing-col-lg: 12px;
+ $uv-spacing-row-lg: 15px;
+ $uv-img-size-sm:20px;
+ $uv-img-size-base:26px;
+ $uv-img-size-lg:40px;
+ $uv-border-color:#e5e5e5;
+ $uv-bg-color-hover:#f1f1f1;
+ $uv-text-color-grey:#999;
+ $list-item-pd: $uv-spacing-col-lg $uv-spacing-row-lg;
+
+ .uv-list-item {
+ @include flex(row);
+ font-size: $uv-font-size-lg;
+ position: relative;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #fff;
+ /* #ifdef H5 */
+ cursor: pointer;
+ /* #endif */
+ }
+
+ .uv-list-item--disabled {
+ opacity: 0.3;
+ }
+
+ .uv-list-item--hover {
+ background-color: $uv-bg-color-hover !important;
+ }
+
+ .uv-list-item__wrapper {
+ @include flex(column);
+ flex: 1;
+ }
+
+ .uv-list-item__container {
+ position: relative;
+ @include flex(row);
+ padding: $list-item-pd;
+ padding-left: $uv-spacing-row-lg;
+ flex: 1;
+ overflow: hidden;
+ }
+
+ .container--right {
+ padding-right: 0;
+ }
+
+ .uv-list--border {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ /* #ifdef APP-NVUE */
+ border-top-color: $uv-border-color;
+ border-top-style: solid;
+ border-top-width: 0.5px;
+ /* #endif */
+ }
+
+ /* #ifndef APP-NVUE */
+ .uv-list--border:after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ height: 1px;
+ content: '';
+ -webkit-transform: scaleY(0.5);
+ transform: scaleY(0.5);
+ background-color: $uv-border-color;
+ }
+
+ /* #endif */
+ .uv-list-item__content {
+ @include flex(column);
+ padding-right: 8px;
+ flex: 1;
+ color: #3b4144;
+ justify-content: space-between;
+ overflow: hidden;
+ }
+
+ .uv-list-item__content--center {
+ justify-content: center;
+ }
+
+ .uv-list-item__content-title {
+ font-size: $uv-font-size-base;
+ color: #3b4144;
+ overflow: hidden;
+ }
+
+ .uv-list-item__content-note {
+ margin-top: 6rpx;
+ color: $uv-text-color-grey;
+ font-size: $uv-font-size-sm;
+ overflow: hidden;
+ }
+
+ .uv-list-item__extra {
+ @include flex(row);
+ justify-content: flex-end;
+ align-items: center;
+ }
+
+ .uv-list-item__header {
+ @include flex(row);
+ align-items: center;
+ }
+
+ .uv-list-item__icon {
+ margin-right: 18rpx;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .uv-list-item__icon-img {
+ /* #ifndef APP-NVUE */
+ display: block;
+ /* #endif */
+ height: $uv-img-size-base;
+ width: $uv-img-size-base;
+ margin-right: 10px;
+ }
+
+ .uv-icon-wrapper {
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ align-items: center;
+ padding: 0 10px;
+ }
+
+ .flex--direction {
+ flex-direction: column;
+ /* #ifndef APP-NVUE */
+ align-items: initial;
+ /* #endif */
+ }
+
+ .flex--justify {
+ /* #ifndef APP-NVUE */
+ justify-content: initial;
+ /* #endif */
+ }
+
+ .uv-list--lg {
+ height: $uv-img-size-lg;
+ width: $uv-img-size-lg;
+ }
+
+ .uv-list--base {
+ height: $uv-img-size-base;
+ width: $uv-img-size-base;
+ }
+
+ .uv-list--sm {
+ height: $uv-img-size-sm;
+ width: $uv-img-size-sm;
+ }
+
+ .uv-list-item__extra-text {
+ color: $uv-text-color-grey;
+ font-size: $uv-font-size-sm;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-list/components/uv-list/uv-list.vue b/uni_modules/uv-list/components/uv-list/uv-list.vue
new file mode 100644
index 0000000..3f3e5c2
--- /dev/null
+++ b/uni_modules/uv-list/components/uv-list/uv-list.vue
@@ -0,0 +1,147 @@
+<template>
+ <!-- #ifndef APP-NVUE -->
+ <view
+ class="uv-list"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <view
+ v-if="border"
+ class="uv-list--border-top"
+ :style="[{ 'background-color': borderColor }]"
+ ></view>
+ <slot />
+ <view
+ v-if="border"
+ class="uv-list--border-bottom"
+ :style="[{ 'background-color': borderColor }]"
+ ></view>
+ </view>
+ <!-- #endif -->
+ <!-- #ifdef APP-NVUE -->
+ <list
+ :bounce="true"
+ :scrollable="true"
+ show-scrollbar
+ :render-reverse="false"
+ class="uv-list"
+ :class="{ 'uv-list--border': border }"
+ :style="[
+ { 'border-top-color': borderColor, 'border-bottom-color':borderColor },
+ $uv.addStyle(customStyle)
+ ]"
+ :enableBackToTop="false"
+ :loadmoreoffset="15"
+ @scroll="scroll"
+ @loadmore="loadMore">
+ <slot />
+ </list>
+ <!-- #endif -->
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ /**
+ * List 列表
+ * @description 列表组件
+ * @tutorial https://www.uvui.cn/components/list.html
+ * @property {Boolean} border = [true|false] 是否显示边框
+ * @property {String} borderColor 边框颜色
+ * @property {String} direction 排版方向,默认row,列表里面使用其他组件 最好设置成column
+ */
+ export default {
+ name: 'uv-list',
+ mixins: [mpMixin, mixin],
+ 'mp-weixin': {
+ options: {
+ multipleSlots: false
+ }
+ },
+ props: {
+ border: {
+ type: Boolean,
+ default: false
+ },
+ borderColor: {
+ type: String,
+ default: '#dadbde'
+ },
+ // 排版方向,默认row,列表里面使用其他组件 最好设置成column
+ direction: {
+ type: String,
+ default: 'row'
+ },
+ // 内边距
+ padding: {
+ type: [String,Number],
+ default: '20rpx 30rpx'
+ }
+ },
+ created() {
+ this.firstChildAppend = false;
+ },
+ computed: {
+ parentData() {
+ return [this.direction,this.padding];
+ }
+ },
+ methods: {
+ loadMore(e) {
+ this.$emit('scrolltolower');
+ },
+ scroll(e) {
+ this.$emit('scroll', e);
+ }
+ }
+ };
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+
+ .uv-list {
+ position: relative;
+ @include flex(column);
+ background-color: #fff;
+ }
+
+ .uv-list--border {
+ position: relative;
+ /* #ifdef APP-NVUE */
+ border-top-color: $uv-border-color;
+ border-top-style: solid;
+ border-top-width: 0.5px;
+ border-bottom-color: $uv-border-color;
+ border-bottom-style: solid;
+ border-bottom-width: 0.5px;
+ /* #endif */
+ z-index: -1;
+ }
+
+ /* #ifndef APP-NVUE */
+
+ .uv-list--border-top {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ height: 1px;
+ -webkit-transform: scaleY(0.5);
+ transform: scaleY(0.5);
+ background-color: $uv-border-color;
+ z-index: 1;
+ }
+
+ .uv-list--border-bottom {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ left: 0;
+ height: 1px;
+ -webkit-transform: scaleY(0.5);
+ transform: scaleY(0.5);
+ background-color: $uv-border-color;
+ }
+
+ /* #endif */
+</style>
diff --git a/uni_modules/uv-list/package.json b/uni_modules/uv-list/package.json
new file mode 100644
index 0000000..1b1156c
--- /dev/null
+++ b/uni_modules/uv-list/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-list",
+ "displayName": "uv-list 列表 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.9",
+ "description": "uv-list 多功能高性能列表组件",
+ "keywords": [
+ "uv-list",
+ "uvui",
+ "uv-ui",
+ "list",
+ "列表"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-list/readme.md b/uni_modules/uv-list/readme.md
new file mode 100644
index 0000000..930cbd4
--- /dev/null
+++ b/uni_modules/uv-list/readme.md
@@ -0,0 +1,27 @@
+## List 列表
+
+> **组件名:uv-list**
+
+List列表组件,包含基本列表样式、默认插槽机制、可扩展插槽机制、长列表性能优化、多端兼容。
+
+在vue页面里,它默认使用页面级滚动,这样做的目的是性能更加友好。在app-nvue页面里,它默认使用原生list组件滚动,这样的长列表,在滚动出屏幕外后,系统会回收不可见区域的渲染内存资源,不会造成滚动越长手机越卡的问题。
+
+uv-list组件是父容器,里面的核心是uv-list-item子组件,它代表列表中的一个可重复行,子组件可以无限循环。
+
+uv-list-item有很多风格,uv-list-item组件通过内置的属性,满足一些常用的场景。当内置属性不满足需求时,可以通过扩展插槽来自定义列表内容,插槽包括:默认插槽(完全自定义内容)、具名插槽(header | body | footer),根据需求进行扩展。
+
+内置属性可以覆盖的场景包括:导航列表、设置列表、小图标列表等,其他不能满足的场景使用插槽进行扩展。
+
+# <a href="https://www.uvui.cn/components/list.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-load-more/changelog.md b/uni_modules/uv-load-more/changelog.md
new file mode 100644
index 0000000..a2e0fed
--- /dev/null
+++ b/uni_modules/uv-load-more/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-06-21)
+1. 优化customStyle属性
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-load-more 加载更多
diff --git a/uni_modules/uv-load-more/components/uv-load-more/props.js b/uni_modules/uv-load-more/components/uv-load-more/props.js
new file mode 100644
index 0000000..c6bc287
--- /dev/null
+++ b/uni_modules/uv-load-more/components/uv-load-more/props.js
@@ -0,0 +1,95 @@
+export default {
+ props: {
+ // 组件状态,loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+ status: {
+ type: String,
+ default: 'loadmore'
+ },
+ // 组件背景色
+ bgColor: {
+ type: String,
+ default: 'transparent'
+ },
+ // 是否显示加载中的图标
+ icon: {
+ type: Boolean,
+ default: true
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 图标大小
+ iconSize: {
+ type: [String, Number],
+ default: 16
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: '#606266'
+ },
+ // 加载中状态的图标,spinner-花朵状图标,circle-圆圈状,semicircle-半圆
+ loadingIcon: {
+ type: String,
+ default: 'spinner'
+ },
+ // 加载前的提示语
+ loadmoreText: {
+ type: String,
+ default: '加载更多'
+ },
+ // 加载中提示语
+ loadingText: {
+ type: String,
+ default: '正在加载...'
+ },
+ // 没有更多的提示语
+ nomoreText: {
+ type: String,
+ default: '没有更多了'
+ },
+ // 在“没有更多”状态下,是否显示粗点
+ isDot: {
+ type: Boolean,
+ default: false
+ },
+ // 加载中图标的颜色
+ iconColor: {
+ type: String,
+ default: '#b7b7b7'
+ },
+ // 上边距
+ marginTop: {
+ type: [String, Number],
+ default: 10
+ },
+ // 下边距
+ marginBottom: {
+ type: [String, Number],
+ default: 10
+ },
+ // 高度,单位px
+ height: {
+ type: [String, Number],
+ default: 'auto'
+ },
+ // 是否显示左边分割线
+ line: {
+ type: Boolean,
+ default: false
+ },
+ // 线条颜色
+ lineColor: {
+ type: String,
+ default: '#E6E8EB'
+ },
+ // 是否虚线,true-虚线,false-实线
+ dashed: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.loadmore
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue b/uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue
new file mode 100644
index 0000000..2b5057c
--- /dev/null
+++ b/uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue
@@ -0,0 +1,152 @@
+<template>
+ <view
+ class="uv-loadmore"
+ :style="[{
+ backgroundColor: bgColor,
+ marginBottom: $uv.addUnit(marginBottom),
+ marginTop: $uv.addUnit(marginTop),
+ height: $uv.addUnit(height),
+ },
+ $uv.addStyle(customStyle)]"
+ >
+ <uv-line
+ length="140rpx"
+ :color="lineColor"
+ :hairline="false"
+ :dashed="dashed"
+ v-if="line"
+ ></uv-line>
+ <!-- 加载中和没有更多的状态才显示两边的横线 -->
+ <view
+ :class="status == 'loadmore' || status == 'nomore' ? 'uv-more' : ''"
+ class="uv-loadmore__content"
+ >
+ <view
+ class="uv-loadmore__content__icon-wrap"
+ v-if="status === 'loading' && icon"
+ >
+ <uv-loading-icon
+ :color="iconColor"
+ :size="iconSize"
+ :mode="loadingIcon"
+ ></uv-loading-icon>
+ </view>
+ <!-- 如果没有更多的状态下,显示内容为dot(粗点),加载特定样式 -->
+ <text
+ class="uv-line-1"
+ :style="[loadTextStyle]"
+ :class="[(status == 'nomore' && isDot == true) ? 'uv-loadmore__content__dot-text' : 'uv-loadmore__content__text']"
+ @tap="loadMore"
+ >{{ showText }}</text>
+ </view>
+ <uv-line
+ length="140rpx"
+ :color="lineColor"
+ :hairline="false"
+ :dashed="dashed"
+ v-if="line"
+ ></uv-line>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+
+ /**
+ * loadmore 加载更多
+ * @description 此组件一般用于标识页面底部加载数据时的状态。
+ * @tutorial https://www.uvui.cn/components/loadMore.html
+ * @property {String} status 组件状态(默认 'loadmore' )
+ * @property {String} bgColor 组件背景颜色,在页面是非白色时会用到(默认 'transparent' )
+ * @property {Boolean} icon 加载中时是否显示图标(默认 true )
+ * @property {String | Number} fontSize 字体大小(默认 14 )
+ * @property {String | Number} iconSize 图标大小(默认 17 )
+ * @property {String} color 字体颜色(默认 '#606266' )
+ * @property {String} loadingIcon 加载图标(默认 'circle' )
+ * @property {String} loadmoreText 加载前的提示语(默认 '加载更多' )
+ * @property {String} loadingText 加载中提示语(默认 '正在加载...' )
+ * @property {String} nomoreText 没有更多的提示语(默认 '没有更多了' )
+ * @property {Boolean} isDot 到上一个相邻元素的距离 (默认 false )
+ * @property {String} iconColor 加载中图标的颜色 (默认 '#b7b7b7' )
+ * @property {String} lineColor 线条颜色(默认 #E6E8EB )
+ * @property {String | Number} marginTop 上边距 (默认 10 )
+ * @property {String | Number} marginBottom 下边距 (默认 10 )
+ * @property {String | Number} height 高度,单位px (默认 'auto' )
+ * @property {Boolean} line 是否显示左边分割线 (默认 false )
+ * @property {Boolean} dashed // 是否虚线,true-虚线,false-实线 (默认 false )
+ * @event {Function} loadmore status为loadmore时,点击组件会发出此事件
+ * @example <uv-loadmore :status="status" icon-type="iconType" load-text="loadText" />
+ */
+ export default {
+ name: "uv-loadmore",
+ mixins: [mpMixin, mixin,props],
+ data() {
+ return {
+ // 粗点
+ dotText: "●"
+ }
+ },
+ computed: {
+ // 加载的文字显示的样式
+ loadTextStyle() {
+ return {
+ color: this.color,
+ fontSize: this.$uv.addUnit(this.fontSize),
+ lineHeight: this.$uv.addUnit(this.fontSize),
+ backgroundColor: this.bgColor,
+ }
+ },
+ // 显示的提示文字
+ showText() {
+ let text = '';
+ if (this.status == 'loadmore') text = this.loadmoreText
+ else if (this.status == 'loading') text = this.loadingText
+ else if (this.status == 'nomore' && this.isDot) text = this.dotText;
+ else text = this.nomoreText;
+ return text;
+ }
+ },
+ methods: {
+ loadMore() {
+ // 只有在“加载更多”的状态下才发送点击事件,内容不满一屏时无法触发底部上拉事件,所以需要点击来触发
+ if (this.status == 'loadmore') this.$emit('loadmore');
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-lines: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-loadmore {
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+ flex: 1;
+
+ &__content {
+ margin: 0 15px;
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+
+ &__icon-wrap {
+ margin-right: 8px;
+ }
+
+ &__text {
+ font-size: 14px;
+ color: $uv-content-color;
+ }
+
+ &__dot-text {
+ font-size: 15px;
+ color: $uv-tips-color;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-load-more/package.json b/uni_modules/uv-load-more/package.json
new file mode 100644
index 0000000..bc502e3
--- /dev/null
+++ b/uni_modules/uv-load-more/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-load-more",
+ "displayName": "uv-load-more 加载更多 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "uv-load-more 此组件一般用于标识页面底部加载数据时的状态,共有三种状态:加载前、加载中、加载后。",
+ "keywords": [
+ "uv-load-more",
+ "uvui",
+ "uv-ui",
+ "more",
+ "加载更多"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-line",
+ "uv-loading-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-load-more/readme.md b/uni_modules/uv-load-more/readme.md
new file mode 100644
index 0000000..2e9334e
--- /dev/null
+++ b/uni_modules/uv-load-more/readme.md
@@ -0,0 +1,11 @@
+## LoadMore 加载更多
+
+> **组件名:uv-load-more**
+
+此组件一般用于标识页面底部加载数据时的状态,共有三种状态:加载前、加载中、加载后。
+
+### <a href="https://www.uvui.cn/components/loadMore.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-modal/changelog.md b/uni_modules/uv-modal/changelog.md
new file mode 100644
index 0000000..d532286
--- /dev/null
+++ b/uni_modules/uv-modal/changelog.md
@@ -0,0 +1,28 @@
+## 1.0.11(2023-12-20)
+1. 优化
+## 1.0.10(2023-12-19)
+1. 修复confirm中快速使用closeLoading关闭加载状态失效的BUG
+## 1.0.9(2023-11-28)
+1. 修复上版本引出的确认和取消按钮均不显示,还有高度的BUG
+## 1.0.8(2023-09-08)
+1. 修复两个按钮之间竖线不显示的问题
+2. uv-ui项目自定义按钮示例修改
+## 1.0.7(2023-08-30)
+1. 增加align参数,设置文本对齐方式,left center right
+2. 增加textStyle参数,设置文本样式
+## 1.0.6(2023-08-23)
+1. 修复异步loading时,确认回调还会一直触发
+## 1.0.5(2023-07-03)
+去除插槽判断,避免某些平台不显示的BUG
+## 1.0.4(2023-07-02)
+uv-modal 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/modal.html
+## 1.0.3(2023-06-29)
+1. 增加closeLoading方法,方便异步加载手动取消
+2. 更新文档
+## 1.0.2(2023-06-11)
+1. 新增zIndex参数
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-modal 模态框
diff --git a/uni_modules/uv-modal/components/uv-modal/props.js b/uni_modules/uv-modal/components/uv-modal/props.js
new file mode 100644
index 0000000..6cb1f41
--- /dev/null
+++ b/uni_modules/uv-modal/components/uv-modal/props.js
@@ -0,0 +1,90 @@
+export default {
+ props: {
+ // 标题
+ title: {
+ type: [String],
+ default: ''
+ },
+ // 弹窗内容
+ content: {
+ type: String,
+ default: ''
+ },
+ // 确认文案
+ confirmText: {
+ type: String,
+ default: '确认'
+ },
+ // 取消文案
+ cancelText: {
+ type: String,
+ default: '取消'
+ },
+ // 是否显示确认按钮
+ showConfirmButton: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示取消按钮
+ showCancelButton: {
+ type: Boolean,
+ default: false
+ },
+ // 确认按钮颜色
+ confirmColor: {
+ type: String,
+ default: '#2979ff'
+ },
+ // 取消文字颜色
+ cancelColor: {
+ type: String,
+ default: '#606266'
+ },
+ // 对调确认和取消的位置
+ buttonReverse: {
+ type: Boolean,
+ default: false
+ },
+ // 是否开启缩放效果
+ zoom: {
+ type: Boolean,
+ default: true
+ },
+ // 层级
+ zIndex: {
+ type: [String, Number],
+ default: 10075
+ },
+ // 是否异步关闭,只对确定按钮有效
+ asyncClose: {
+ type: Boolean,
+ default: false
+ },
+ // 是否允许点击遮罩关闭modal
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: true
+ },
+ // 给一个负的margin-top,往上偏移,避免和键盘重合的情况
+ negativeTop: {
+ type: [String, Number],
+ default: 0
+ },
+ // modal宽度,不支持百分比,可以数值,px,rpx单位
+ width: {
+ type: [String, Number],
+ default: '650rpx'
+ },
+ // 文本对齐方式,默认left
+ align: {
+ type: String,
+ default: 'left'
+ },
+ // 文本自定义样式
+ textStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ ...uni.$uv?.props?.modal
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-modal/components/uv-modal/uv-modal.vue b/uni_modules/uv-modal/components/uv-modal/uv-modal.vue
new file mode 100644
index 0000000..4577b99
--- /dev/null
+++ b/uni_modules/uv-modal/components/uv-modal/uv-modal.vue
@@ -0,0 +1,245 @@
+<template>
+ <uv-popup
+ ref="modalPopup"
+ mode="center"
+ :zoom="zoom"
+ :zIndex="zIndex"
+ :customStyle="{
+ borderRadius: '6px',
+ overflow: 'hidden',
+ marginTop: `-${$uv.addUnit(negativeTop)}`
+ }"
+ :closeOnClickOverlay="closeOnClickOverlay"
+ :safeAreaInsetBottom="false"
+ :duration="400"
+ @change="popupChange"
+ >
+ <view
+ class="uv-modal"
+ :style="{
+ width: $uv.addUnit(width),
+ }"
+ >
+ <text
+ class="uv-modal__title"
+ v-if="title"
+ >{{ title }}</text>
+ <view
+ class="uv-modal__content"
+ :style="{
+ paddingTop: `${title ? 12 : 25}px`
+ }"
+ >
+ <slot>
+ <text
+ class="uv-modal__content__text"
+ :style="[
+ {
+ textAlign: align
+ },
+ nvueStyle,
+ $uv.addStyle(textStyle)
+ ]"
+ >{{ content }}</text>
+ </slot>
+ </view>
+ <slot name="confirmButton">
+ <uv-line></uv-line>
+ <view
+ v-if="showConfirmButton || showCancelButton"
+ class="uv-modal__button-group"
+ :style="{
+ flexDirection: buttonReverse ? 'row-reverse' : 'row'
+ }"
+ >
+ <view
+ class="uv-modal__button-group__wrapper uv-modal__button-group__wrapper--cancel"
+ :hover-stay-time="150"
+ hover-class="uv-modal__button-group__wrapper--hover"
+ :class="[showCancelButton && !showConfirmButton && 'uv-modal__button-group__wrapper--only-cancel']"
+ v-if="showCancelButton"
+ @tap="cancelHandler"
+ >
+ <text
+ class="uv-modal__button-group__wrapper__text"
+ :style="{
+ color: cancelColor
+ }"
+ >{{ cancelText }}</text>
+ </view>
+ <uv-line
+ direction="column"
+ v-if="showConfirmButton && showCancelButton"
+ ></uv-line>
+ <view
+ class="uv-modal__button-group__wrapper uv-modal__button-group__wrapper--confirm"
+ :hover-stay-time="150"
+ hover-class="uv-modal__button-group__wrapper--hover"
+ :class="[!showCancelButton && showConfirmButton && 'uv-modal__button-group__wrapper--only-confirm']"
+ v-if="showConfirmButton"
+ @tap="confirmHandler"
+ >
+ <uv-loading-icon v-if="loading"></uv-loading-icon>
+ <text
+ v-else
+ class="uv-modal__button-group__wrapper__text"
+ :style="{
+ color: confirmColor
+ }"
+ >{{ confirmText }}</text>
+ </view>
+ </view>
+ </slot>
+ </view>
+ </uv-popup>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Modal 模态框
+ * @description 弹出模态框,常用于消息提示、消息确认、在当前页面内完成特定的交互操作。
+ * @tutorial https://www.uvui.cn/components/modul.html
+ * @property {String} title 标题内容
+ * @property {String} content 模态框内容,如传入slot内容,则此参数无效
+ * @property {String} confirmText 确认按钮的文字 (默认 '确认' )
+ * @property {String} cancelText 取消按钮的文字 (默认 '取消' )
+ * @property {Boolean} showConfirmButton 是否显示确认按钮 (默认 true )
+ * @property {Boolean} showCancelButton 是否显示取消按钮 (默认 false )
+ * @property {String} confirmColor 确认按钮的颜色 (默认 '#2979ff' )
+ * @property {String} cancelColor 取消按钮的颜色 (默认 '#606266' )
+ * @property {Boolean} buttonReverse 对调确认和取消的位置 (默认 false )
+ * @property {Boolean} zoom 是否开启缩放模式 (默认 true )
+ * @property {Boolean} asyncClose 是否异步关闭,只对确定按钮有效,见上方说明 (默认 false )
+ * @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭该组件 (默认 true )
+ * @property {String | Number} negativeTop 往上偏移的值,给一个负的margin-top,往上偏移,避免和键盘重合的情况,单位任意,数值则默认为px单位 (默认 0 )
+ * @property {String | Number} width modal宽度,不支持百分比,可以数值,px,rpx单位 (默认 '650rpx' )
+ * @property {String} align 文本对齐方式 (默认'left')
+ * @property {String | Object} textStyle 文本扩展样式
+ * @event {Function} confirm 点击确认按钮时触发
+ * @event {Function} cancel 点击取消按钮时触发
+ * @event {Function} close 点击遮罩关闭出发,closeOnClickOverlay为true有效
+ * @example <uv-modal ref="modalPopup" title="title" content="content"></uv-modal>
+ */
+ export default {
+ name: 'uv-modal',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ loading: false
+ }
+ },
+ computed: {
+ nvueStyle() {
+ const style = {};
+ // 避免nvue中不能换行的问题
+ // #ifdef APP-NVUE
+ style.width = this.$uv.addUnit(this.$uv.getPx(this.width) - 50,'px');
+ // #endif
+ return style;
+ }
+ },
+ methods: {
+ open() {
+ this.$refs.modalPopup.open();
+ if (this.loading) this.loading = false;
+ },
+ close() {
+ this.$refs.modalPopup.close();
+ },
+ popupChange(e) {
+ if(!e.show) this.$emit('close');
+ },
+ // 点击确定按钮
+ confirmHandler() {
+ if(!this.loading) {
+ this.$emit('confirm');
+ }
+ // 如果配置了异步关闭,将按钮值为loading状态
+ if (this.asyncClose) {
+ this.loading = true;
+ } else {
+ this.close();
+ }
+ },
+ // 点击取消按钮
+ cancelHandler() {
+ this.$emit('cancel');
+ this.close();
+ },
+ closeLoading() {
+ this.$nextTick(()=>{
+ this.loading = false;
+ })
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-modal-border-radius: 6px;
+
+ .uv-modal {
+ width: 650rpx;
+ border-radius: $uv-modal-border-radius;
+ overflow: hidden;
+
+ &__title {
+ font-size: 16px;
+ font-weight: bold;
+ color: $uv-content-color;
+ text-align: center;
+ padding-top: 25px;
+ }
+
+ &__content {
+ padding: 12px 25px 25px 25px;
+ @include flex;
+ justify-content: center;
+
+ &__text {
+ line-height: 48rpx;
+ font-size: 15px;
+ color: $uv-content-color;
+ flex: 1;
+ }
+ }
+
+ &__button-group {
+ @include flex;
+ height: 48px;
+
+ &__wrapper {
+ flex: 1;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ height: 48px;
+
+ &--confirm,
+ &--only-cancel {
+ border-bottom-right-radius: $uv-modal-border-radius;
+ }
+
+ &--cancel,
+ &--only-confirm {
+ border-bottom-left-radius: $uv-modal-border-radius;
+ }
+
+ &--hover {
+ background-color: $uv-bg-color;
+ }
+
+ &__text {
+ color: $uv-content-color;
+ font-size: 16px;
+ text-align: center;
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-modal/package.json b/uni_modules/uv-modal/package.json
new file mode 100644
index 0000000..2a815c0
--- /dev/null
+++ b/uni_modules/uv-modal/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-modal",
+ "displayName": "uv-modal 模态框 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.11",
+ "description": "支持自定义内容,与uniapp提供的API uni.showModal类似,但是功能更强大,更加灵活",
+ "keywords": [
+ "uv-modal",
+ "uvui",
+ "uv-ui",
+ "modal",
+ "模态框"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-popup",
+ "uv-line",
+ "uv-loading-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-modal/readme.md b/uni_modules/uv-modal/readme.md
new file mode 100644
index 0000000..e1aec02
--- /dev/null
+++ b/uni_modules/uv-modal/readme.md
@@ -0,0 +1,23 @@
+## Modal 模态框
+
+> **组件名:uv-modal**
+
+弹出模态框,常用于消息提示、消息确认、在当前页面内完成特定的交互操作。
+
+特性:支持自定义内容,与uniapp提供的API `uni.showModal` 类似,但是功能更强大,更加灵活。
+
+运用场景:弹窗验证码输入等场景
+
+# <a href="https://www.uvui.cn/components/modal.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-navbar/changelog.md b/uni_modules/uv-navbar/changelog.md
new file mode 100644
index 0000000..01cc4fd
--- /dev/null
+++ b/uni_modules/uv-navbar/changelog.md
@@ -0,0 +1,17 @@
+## 1.0.7(2023-08-16)
+1. 修复ios可能存在点击返回按钮点不到的情况
+## 1.0.6(2023-08-07)
+1. 修复nvue在ios端可能存在背景图样式错乱的BUG
+## 1.0.5(2023-08-04)
+1. bgColor设置背景图片,增加imgMode属性
+## 1.0.4(2023-08-01)
+1. bgColor属性支持背景图片,在线图片或base64图片都可以
+## 1.0.3(2023-07-03)
+去除插槽判断,避免某些平台不显示的BUG
+## 1.0.2(2023-06-05)
+1. 兼容渐变背景色
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-navbar 自定义导航栏
diff --git a/uni_modules/uv-navbar/components/uv-navbar/props.js b/uni_modules/uv-navbar/components/uv-navbar/props.js
new file mode 100644
index 0000000..47fe150
--- /dev/null
+++ b/uni_modules/uv-navbar/components/uv-navbar/props.js
@@ -0,0 +1,89 @@
+export default {
+ props: {
+ // 是否开启顶部安全区适配
+ safeAreaInsetTop: {
+ type: Boolean,
+ default: true
+ },
+ // 固定在顶部时,是否生成一个等高元素,以防止塌陷
+ placeholder: {
+ type: Boolean,
+ default: false
+ },
+ // 是否固定在顶部
+ fixed: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示下边框
+ border: {
+ type: Boolean,
+ default: false
+ },
+ // 左边的图标
+ leftIcon: {
+ type: String,
+ default: 'arrow-left'
+ },
+ // 左边的提示文字
+ leftText: {
+ type: String,
+ default: ''
+ },
+ // 左右的提示文字
+ rightText: {
+ type: String,
+ default: ''
+ },
+ // 右边的图标
+ rightIcon: {
+ type: String,
+ default: ''
+ },
+ // 标题
+ title: {
+ type: [String, Number],
+ default: ''
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: '#ffffff'
+ },
+ imgMode: {
+ type: String,
+ default: 'aspectFill'
+ },
+ // 标题的宽度
+ titleWidth: {
+ type: [String, Number],
+ default: '400rpx'
+ },
+ // 导航栏高度
+ height: {
+ type: [String, Number],
+ default: '44px'
+ },
+ // 左侧返回图标的大小
+ leftIconSize: {
+ type: [String, Number],
+ default: 20
+ },
+ // 左侧返回图标的颜色
+ leftIconColor: {
+ type: String,
+ default: '#303133'
+ },
+ // 点击左侧区域(返回图标),是否自动返回上一页
+ autoBack: {
+ type: Boolean,
+ default: false
+ },
+ // 标题的样式,对象或字符串
+ titleStyle: {
+ type: [String, Object],
+ default: ''
+ },
+ ...uni.$uv?.props?.navbar
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue b/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue
new file mode 100644
index 0000000..a46ad7a
--- /dev/null
+++ b/uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue
@@ -0,0 +1,245 @@
+<template>
+ <view class="uv-navbar">
+ <view
+ class="uv-navbar__placeholder"
+ v-if="fixed && placeholder"
+ :style="{
+ height: $uv.addUnit($uv.getPx(height) + $uv.sys().statusBarHeight,'px'),
+ }"
+ ></view>
+ <view :class="[fixed && 'uv-navbar--fixed']">
+ <image class="uv-navbar--bgimg" :src="bgColor" :mode="imgMode" v-if="isImg" :style="[bgImgStyle]"></image>
+ <uv-status-bar
+ v-if="safeAreaInsetTop"
+ :bgColor="getStatusbgColor"
+ ></uv-status-bar>
+ <view
+ class="uv-navbar__content"
+ :class="[border && 'uv-border-bottom']"
+ :style="[{
+ height: $uv.addUnit(height)
+ },getBgColor]"
+ >
+ <view
+ class="uv-navbar__content__left"
+ hover-class="uv-navbar__content__left--hover"
+ hover-start-time="150"
+ @tap="leftClick"
+ >
+ <slot name="left">
+ <uv-icon
+ v-if="leftIcon"
+ :name="leftIcon"
+ :size="leftIconSize"
+ :color="leftIconColor"
+ ></uv-icon>
+ <text
+ v-if="leftText"
+ :style="{
+ color: leftIconColor
+ }"
+ class="uv-navbar__content__left__text"
+ >{{ leftText }}</text>
+ </slot>
+ </view>
+ <slot name="center">
+ <text
+ class="uv-line-1 uv-navbar__content__title"
+ :style="[{
+ width: $uv.addUnit(titleWidth),
+ flex: '0 1 auto'
+ }, $uv.addStyle(titleStyle)]"
+ >{{ title }}</text>
+ </slot>
+ <view
+ class="uv-navbar__content__right"
+ @tap="rightClick"
+ >
+ <slot name="right">
+ <uv-icon
+ v-if="rightIcon"
+ :name="rightIcon"
+ size="20"
+ ></uv-icon>
+ <text
+ v-if="rightText"
+ class="uv-navbar__content__right__text"
+ >{{ rightText }}</text>
+ </slot>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Navbar 自定义导航栏
+ * @description 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用uni-app带的导航栏。
+ * @tutorial https://www.uvui.cn/components/navbar.html
+ * @property {Boolean} safeAreaInsetTop 是否开启顶部安全区适配 (默认 true )
+ * @property {Boolean} placeholder 固定在顶部时,是否生成一个等高元素,以防止塌陷 (默认 false )
+ * @property {Boolean} fixed 导航栏是否固定在顶部 (默认 false )
+ * @property {Boolean} border 导航栏底部是否显示下边框 (默认 false )
+ * @property {String} leftIcon 左边返回图标的名称,只能为uvui自带的图标 (默认 'arrow-left' )
+ * @property {String} leftText 左边的提示文字
+ * @property {String} rightText 右边的提示文字
+ * @property {String} rightIcon 右边返回图标的名称,只能为uvui自带的图标
+ * @property {String} title 导航栏标题,如设置为空字符,将会隐藏标题占位区域
+ * @property {String} bgColor 导航栏背景设置 (默认 '#ffffff' )
+ * @property {String | Number} titleWidth 导航栏标题的最大宽度,内容超出会以省略号隐藏 (默认 '400rpx' )
+ * @property {String | Number} height 导航栏高度(不包括状态栏高度在内,内部自动加上)(默认 '44px' )
+ * @property {String | Number} leftIconSize 左侧返回图标的大小(默认 20px )
+ * @property {String | Number} leftIconColor 左侧返回图标的颜色(默认 #303133 )
+ * @property {Boolean} autoBack 点击左侧区域(返回图标),是否自动返回上一页(默认 false )
+ * @property {Object | String} titleStyle 标题的样式,对象或字符串
+ * @event {Function} leftClick 点击左侧区域
+ * @event {Function} rightClick 点击右侧区域
+ * @example <uv-navbar title="剑未配妥,出门已是江湖" left-text="返回" right-text="帮助" @click-left="onClickBack" @click-right="onClickRight"></uv-navbar>
+ */
+ export default {
+ name: 'uv-navbar',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+
+ }
+ },
+ computed: {
+ getBgColor(){
+ const style = {};
+ if(this.bgColor){
+ if (this.bgColor.indexOf("gradient") > -1) {// 渐变色
+ style.backgroundImage = this.bgColor;
+ }else if(this.isImg){
+ style.background = 'transparent';
+ }else {
+ style.background = this.bgColor;
+ }
+ }
+ return style;
+ },
+ getStatusbgColor() {
+ if(this.bgColor){
+ if(this.isImg){
+ return 'transparent';
+ }else {
+ return this.bgColor;
+ }
+ }
+ },
+ // 判断传入的bgColor属性,是否图片路径,只要带有"/"均认为是图片形式
+ isImg() {
+ const isBase64 = this.bgColor.indexOf('data:') > -1 && this.bgColor.indexOf('base64') > -1;
+ return this.bgColor.indexOf('/') !== -1 || isBase64;
+ },
+ bgImgStyle() {
+ const style = {};
+ if(this.safeAreaInsetTop) {
+ style.height = this.$uv.addUnit(this.$uv.sys().statusBarHeight + 44, 'px');
+ } else {
+ style.height = '44px';
+ }
+ return style;
+ }
+ },
+ methods: {
+ // 点击左侧区域
+ leftClick() {
+ // 如果配置了autoBack,自动返回上一页
+ this.$emit('leftClick')
+ if(this.autoBack) {
+ uni.navigateBack()
+ }
+ },
+ // 点击右侧区域
+ rightClick() {
+ this.$emit('rightClick')
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-border: 1;
+ $show-border-bottom: 1;
+ $show-lines: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-navbar {
+ position: relative;
+ &--fixed {
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ z-index: 11;
+ }
+
+ &--bgimg {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ /* #ifndef APP-NVUE */
+ width: 100%;
+ height: 100%;
+ /* #endif */
+ /* #ifdef APP-NVUE */
+ width: 750rpx;
+ /* #endif */
+ }
+
+ &__content {
+ @include flex(row);
+ align-items: center;
+ height: 44px;
+ background-color: #9acafc;
+ position: relative;
+ justify-content: center;
+
+ &__left,
+ &__right {
+ padding: 0 13px;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ @include flex(row);
+ align-items: center;
+ }
+
+ &__left {
+ left: 0;
+
+ &--hover {
+ opacity: 0.7;
+ }
+
+ &__text {
+ font-size: 15px;
+ margin-left: 3px;
+ }
+ }
+
+ &__title {
+ text-align: center;
+ font-size: 16px;
+ color: $uv-main-color;
+ }
+
+ &__right {
+ right: 0;
+
+ &__text {
+ font-size: 15px;
+ margin-left: 3px;
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-navbar/package.json b/uni_modules/uv-navbar/package.json
new file mode 100644
index 0000000..8d429de
--- /dev/null
+++ b/uni_modules/uv-navbar/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-navbar",
+ "displayName": "uv-navbar 自定义导航栏 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.7",
+ "description": "uv-navbar 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用自带的原生导航栏。",
+ "keywords": [
+ "uv-navbar",
+ "uvui",
+ "uv-ui",
+ "navbar",
+ "自定义导航栏"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-status-bar"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-navbar/readme.md b/uni_modules/uv-navbar/readme.md
new file mode 100644
index 0000000..412ea7b
--- /dev/null
+++ b/uni_modules/uv-navbar/readme.md
@@ -0,0 +1,19 @@
+## Navbar 自定义导航栏
+
+> **组件名:uv-navbar**
+
+此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用自带的原生导航栏,支持渐变色、透明色、图片背景。
+
+# <a href="https://www.uvui.cn/components/navbar.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-no-network/changelog.md b/uni_modules/uv-no-network/changelog.md
new file mode 100644
index 0000000..b6fc0ed
--- /dev/null
+++ b/uni_modules/uv-no-network/changelog.md
@@ -0,0 +1,5 @@
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-no-network 无网络提示
diff --git a/uni_modules/uv-no-network/components/uv-no-network/props.js b/uni_modules/uv-no-network/components/uv-no-network/props.js
new file mode 100644
index 0000000..595b1fc
--- /dev/null
+++ b/uni_modules/uv-no-network/components/uv-no-network/props.js
@@ -0,0 +1,20 @@
+export default {
+ props: {
+ // 页面文字提示
+ tips: {
+ type: String,
+ default: '哎呀,网络信号丢失'
+ },
+ // 一个z-index值,用于设置没有网络这个组件的层次,因为页面可能会有其他定位的元素层级过高,导致此组件被覆盖
+ zIndex: {
+ type: [String, Number],
+ default: ''
+ },
+ // image 没有网络的图片提示
+ image: {
+ type: String,
+ default: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABLKADAAQAAAABAAABLAAAAADYYILnAABAAElEQVR4Ae29CZhkV3kefNeq6m2W7tn3nl0aCbHIAgmQPGB+sLCNzSID9g9PYrAf57d/+4+DiW0cy8QBJ06c2In/PLFDHJ78+MGCGNsYgyxwIwktwEijAc1ohtmnZ+2Z7p5eq6vu9r/vuXWrq25VdVV1V3dXVX9Hmj73nv285963vvOd75yraeIEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQaD8E9PbrkvRopSMwMBBYRs+5O/yJS68cPnzYXel4tFP/jXbqjPRFEAiCQNe6Bw/6gdFn9Oy9Q90LLG2DgBBW2wyldIQIPPPCte2a5q3jtR+4ff/4wuBuXotrDwSEsNpjHKUXQODppy+udYJMEUEZgbd94DvnNwlA7YGAEFZ7jOOK78Xp06eTTkq7sxwQhmXuf/754VXl4iSstRAQwmqt8ZLWlkHg0UcD49qYfUjXfLtMtOZ7npExJu4iqZWLl7DWQUAIq3XGSlpaAYHD77q8xwuCOSUoXw8Sl0eMux977DGzQjES3AIICGG1wCBJEysj8PXnz230XXdr5RQFMYbRvWnv6w8UhMhliyGwYghr4Pjg3oEXL34ey9zyC9tiD2ml5h47dr1LN7S6CMjz/A3PvHh1Z6UyJby5EVgRhKUe7Kz/JU0LfvrJo5f+Y3MPibSuFgQGBgasYSd9l6GDsup0WS/T/9RTp9fXmU2SNwECdQ92E7S57iaMeJnPQLK6ixkDLfjlb7546RfrLkQyNBcC3dsP6oHWMd9G+V3JgwPHh7rnm1/yLQ8CbU9Y33zp0j+nZFUMb/DHmB7+SHGY3LUKAk8cObtD00xlHDrfNge+Z2ozU3c9dvx4Yr5lSL6lR6CtCWvg6OAPw9z538ZhhZRl6XrwhW8du1KX/iNejtwvPQIDR8+vSRqJ/obU7GupjdNdh2gW0ZDypJBFR6BtB2rg2OVtuub9JcmpHIpBoK1xfffLzx4f7C0XL2HNiYDp6bs9z23Ypn1fC1Y/9PCFDc3ZW2lVHIG2JKzTp4Ok7nv/G6Q054MIvda+bNb74pEgKGtwGAdL7pcfAa8vOKEZ2kyjWuLr7uDh+/qvN6o8KWdxEWhLwroyeek/g4zuqwU6kNrhyZcu/UktaSXN8iNwuL9/RuvVXtJ9PbPQ1vhmcP6t9+47u9ByJP/SIdB2hDVw9MJHQFYfrQdCph84evFX68kjaZcPAZJWwjMXRFpJ2zr91tfuvrh8vZCa54NA2xGWrunvmg8QWCJ/N4ir7fCYDxatkOeBB7an501agXbygVdvv9IK/ZQ2FiPQdi9osGbH+zRNf7y4m9Xu9Me7N9nv0HXdr5ZS4psHgXpJC9P/wDRTx0Vn1TxjWG9LGrbaUm/Fi5meSvcrkxf/Cg/ow9XqAUk91v3qHT97r6471dJKfHMi8Oyzgx1Z03t1YAQVT2MwgsC3u+yXHzi0faQ5eyGtqgWBtpOw2Ol9+/TM+sTOn8L08MtzgQCy+tOHXr3jA0JWc6HU/HF5Scssr4jXcYqfP6V/T8iq+ceyWgvbUsKKOn38eJAYyl56TAuCEr2WYei//9Crd/5GlFb81kdASVopSFrerKRlaoZj9HR+700H10+0fg+lB21NWBxe2lhNHsUpDZr27mi4dV379R9+za4/iO7Fbx8ECknLCPTsTDJ17O33bJpqnx6u7J60PWFxeAcCbMV56dJfQKf1bkMLfuGh1+76zMoe9vbuPUnLsb2DtmOe5HSxvXsrvWtLBEhaTx29+Ma27Jx0ShAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQaEsEVoQdVluO3BJ06ptHL34b1XRjp4Ch6Rq24+kmjG4Nwwg+9uA9u/73EjRBqhAEihAoe3xwUQq5WTYEzp0b3ZnV/Ncf6O/9AvY9wlh/6dy3X7ncN512Zw9BVLXjuAP4np44vnQtkZoEgVkEhLBmsWiKqwsXpjbPBOn3gRfenwnc+7GBe+zsjclvonFDS9nA9Iy/u3x9+vAP3735VPk4CRUEFhcBIazFxbfm0k9fHD7k+v4nQFaPQIrx8Gmyx/GJ0J/t7ez7mw0b9MmaC2pQQgh0/ZSm4g5TwueWWtqLt0HuVy4CQljLPPYnB0depTn+b3t+8B4t0AdBUv93h2H9xc6da0aXs2m+r1WQsLRnl7NdUvfKRkAIa5nG//r1oGtsZvjTgev/kqYHF/TA+AXoqv4npJemOEiQU1Eo2l+G0movBK1UBBPU7s9E1+ILAkuNgKwSLjXiqO/khVtvARH8dxDBRkMzPrF/V+9/BlG5y9CUqlXinHv9mRPXtvuus88L9H3JPv2zD2yXExCqAicJBIFWRwAvv3Xqwq0/Pnn+lv/K+ZvfPH3p9p5W75O0fxaBp793ce3AwIDMWmYhafiVgNtwSMsXeHp4eNXJC8Nf0PAdRCiuf/XgrnWUqsqotcvnl9DmRkCdweX4b9N7+m/ih+mbMraLM14yJVwcXItKpT1VRve+ArC3Qqn+3gM7132jKEGZm6tXg86J7OhDfuA/iHwPUpfUZSfu2L59tXxEoQxeyxkEgjKeOnLxHb4RqC+NY5H3+2953d4XlrNN7Vq3ENYij+yZwbG9jpt9GkBPQ5H9zgP9607OVeWp87cOQtn9zwJf+xDMNFfj+jryPqXpxj8c2Nn7P+SXey70lidu4IXzb0DNB4tr9751+HV7zxSHyd1CERDCWiiCc+QPjUCnsaqmZ62O5IN7N/VUNP48ee7mAZDTf4Tt049iUG4Guv4ZfNLos9UIbo7qJWoJEHjy+bP7fNsoOcnW0A0/aacef8PdG28sQTNWTBVCWIs01OfPj66BpfqTmq732UnjgT1bei+Vq4pTv7HM8Ceg2/o1qLQug7T+FaaM3IqTLZdewpoHgYEjV9fphvOj+OShWa5V+CxvZtpzv/LwG/aNl4uXsPoRwI+4uEYjAJ2GmdG8L0FK2mYa+tsrkdXZy+P7x2ZuHdW14P+BLdank9q6Qwd3rf+ckFWjR6Tx5Q2cP58K9Jm3VCIr1ogt48lO237r3//96YofeG18y9q7RFklXITxPXV+5DchKb3ZDMy37Nu5tuxG4R9cHH6b42QfAzlds+3EPXu2rfrBIjRFilwkBIIR7SHoJDurFU89ZOd680Gke6JaWomvjoBIWNUxqivFD87fej0e0n8Fwvr0/t1rnyqX+QfnRz7g+8FX8Rv8vL3auF/IqhxKzR2WCPxXqKeq3krDTdj2ierpJEUtCIgOqxaUakwzNBR0D09yiqePHOjveyOkpxLr9VMXb73V97S/h3nDXx7Y2fdPkAYbncW1IgIDxy5vM7LZt/hgrnLtxyaBrJNxv/72N+6tuNhSLp+EVUZACKsyNnXHvHL+1qcgNf2KbSXu2bt9dcmS9qlzo/fARgcmCtpzB3b1/Vg5QiuslLowENyDWDn8cSjl98PgdBviu03N+rl9/WufLEwr18uDwLdevLTF1YK3xnVZ2HI1bUxrT7z5zTuXdRP78qCyeLUKYTUI25OXbm4JPO00TBj+6I7+db8ZL3ZwMOiYdG4dA1lN9HWte2iuI2NAVPapC8O/CGPR34Ip/AZIbIMo7yX8G9QMbcS09P+2b1vf5XgdrXaPfiYns9oeLLEd8D1/B7Dp0E1jGP042pXQj7RKf546cmGzp+tv1TRf6YQD35/QO3seP3xow5IfC9QqmM23naJ0ny9ysXwgq98BWc0kVhv/Nhalbqe8kd/Fr8MOSEr3zEVWrwyO3I29hl+E9LUHGf+nAXI6sGPdd8uV2YphIKnE5IyL6bLxk7cn3bdkHHefrpvJAExMZ1uBZmqeNzXtfzUzk/m/ens7LjV7Px+8d9e1579/44l0duZtge+Np5zEEw8c2pBu9na3YvtEwmrAqNE8IZvNHsep5//yjl3r/0O8yFOXbv0QCO05gP0JGIL+fjw+uj91YeRh/Dp/PtCDM7Zpfmjvjt6Xo7hW9ycmJjaYduf7Hdf/8HTGfa3rG9rYxLSWnsloPg7fijZV8oFM2Ja2a9t6EJd7bCztvHP7us4rrdD/r3/7ct9I99jEI4cOiQ3dIg2YEFYDgOUJDFj1e8TqX7cT4kImXuQr5279A4DeBEX8ayvprU4N3rovcALot/TH13T0fXDTJn0qXk4r3k9OTm4y7a6PzjjORzOOvn1kbEqbnEprPhRzwAKzwFLHk05hv6Yd6N+o3R6beG50aPSdr3qV6IJKkVp5ITIlXOCYn4Yexr0w/DO6YXymHFlR0e5r7tsM3fxgJbI6fW1ivTeT+SsYmr54cFff+5Cu5X+hb94Merp6/J/PusGvTE6724eGJ7RpSFOkKPCUZvBPBccoHBet3Rwe13rX9tw/PjXzZ5hKvr8SfhWKkeA2REAIa4GD6p0feRdWBnvxjv2PckVhVfBf4A29uG/X2i+Ui2eYn8n8NryuDr3jPfWSFV5k44UT137eshIP2K7/64cObbheqZ6lCp+Ydt8TBO7vTM5od1+/NR4SFVhoLpKKt410lnE8LTMzo3V2dLznxLkhYgQ9obiVjEDln7mVjEodfYcpw+MAsftg/7qSDbAnb97sCSb0Yei2fqOcbovVqKNnNO8HmAE9Cv3Wp+uoWjt27HpXNqH9WTKR+kBHKqEFbvo5y3N/avfu4g23R45f3WGa1k9ZicTd0zPTf/f6O7f8dT311Jp2fHzmgJlI/N70jPPe4bEZ6Kg4qw0lqlrLiNKBiLWerpTW25PUbkPXZViW62ecHz+4d8PXojTirzwEyhq8rTwYFtRjvpX/rlwJ+iSXugPbMuyKBOHo3geRJtuT7PujcmVUCuPJlhnL/9NUqvMD2eyM5sxMaIlE4n7XML907tyNjcxHQjty4sZv66Z1xEok/xNW5n4uZSf+8sT5m++vVO58wkEu5sR09pd9w/rWyET2vReujiqygrSopn/zKZN5qMeirotKeTyolm7p/+X06Wvr51ue5Gt9BISwFjiGsLl6N6SrvylXDNTK70D4mX071pwtF88w6Jd/DG/1E1u26NOV0pQL71y3/8PJVOcHMzPTWkcCH2YGOaTTaS2RTN6f1fQvvvDK1bdnbO2JZCr1SeRfn05Pa1PTU0gXJBKW+ecnzlxvCGndhFQ1NRP8bcY1/vjS9bF1V26MwHwsVKiXa3etYVw1TNhYJ3TDjQCO42jJVMcez7J+t9YyJF37ISCEtahjGjxkGDr2DJZ31D8h5vUQJL5RPkXlUMM07u3qSGidICvkzzuSlmlZb0olrK9hD9v9JCrPC196JoPMAolFg6CV+PPj54YeyWecx8Vk2v1Q0rSfhFT18LnBmzBRyNalp5qrSuq7kiAsh4SFa7oZ9M0wzI+cPHOjZPo9V1kS1z4ICGEt4lhiCvZrSa2jol7qzPXJPk6nIGbVbWfUvcr7hO9MP97ZVXpggOu6ajplYStj7l1XvbRMXbPAbp6HzSSBlkraNknrvfVCcPt2sHYi7f3pTDb47KUbYxuvKqkKpYBXKBnV869c3WgbDEixAck0FGFFfEzJzbIsO9C1TyrcymWWsLZGIHoW2rqTzdo5dXyykz0NC8l779i5vu4zwM+eHVntGP5jqVTq/6AkVc5NZ3wNH2lVxNWZNIukMSjiNd9z0+CHp5DXAdX4SAg203w8GB5IATtODHzdK8C15kEjhXvNS9rWA11dnfcMDY9prscss48RySakrOLWqODCoIKAgkuVgsS0urtD60haeV1YYVbbtjUn6/74HXvW/11huFy3PwKzT1r797Upe3jq4sib9u9Y+wxe+vh7W1N7jx49v6ZzbffnQD4/Cj1Pfjx54XiBls6GVuTUc9mQsOIO9mPQFdkIRlz4fy5JLm2ZMOqTcJaXIqpcqnixVe+rdbZ3dbc2OT0D0wZIibHSksmklslknvx+//q3PiKnXcTQae/b+LPQ3r1t0969cOL6G7o6E09qgZegdMJBpVQ1DbKCpyUt6oPKz/4NEJalCAuZFIuEVBJd+jgLh4rvAiFqUVGkhJZMWFp3Z0obGSu/d5gSnWmavuO6h+/cvYHSobgVgoAYjrb4QPMUiGtj1/79jBMkLBwiTlMASlYzTkhWCJyTrGAyMOFkst/BoYMmuIIyGJYcMXMMdNwHPhYN1qWS1t6ZLGaKZL8yzFXTr15BooLLMugHMBRNKgW+It8y9TEcJGt4rvcRFCCEVQbFdg0Swmrxkb0+cf2XOzq73kgdFieEXF2jdEUJKQH6SVWQrNjtZDKlpTPp38U58iUbthk/Ph7sN6zg/xudSGvD4xkq6otcnnjyF0XRRTflkyC0IIJE1JG0QbqGNpMNp5xFhRTcZDNoj66988SFm5vv3LX+WkGUXLYxAuXnCW3c4XbqGs9hwjv+a9lsuN+ahOJSCoLjNDAFvVUll0p1aNPp6adTweSflEszPO48oFn+4yOTmR+6enOshKyYhzWpf/jDuuf6x2aV/qNRaPG/1d0gUXWCA0uu7GhMmkqmerEc8KOVU0lMuyFQ+Ylut562YX9Sncmf7Ojo3BDZWbGLtMkiUVXSWTFNuMqWuYG530f7+/tnGFboxsfdd9mm8XdDo9O7rg6NFq0CFqZr5DWlK9qV0fZqGvZchSuPlevB2VmG/hOV4yWm3RAQwmrhEcW64qu4ykfJho52Vp3J8quBYQooqWDKADftBd6HD+5efyoKj/zR8ew/hWXY56/cnFh7a3RCTTGjuMX0SVB9qzu1qfQM+jO3dBW1g6uVSHv/qVNX10Vh4rc3AkJYLTy+WA/8ou9kJjo7bOh+DLVFZ64TEbCyBktxI5PJZj56R//Gx+NdH5vM4vuI+p8NXh9LjU1iw3EZhXc8TyPuuV9wDaaCfBjTM06N0hVWQmHBDzvSDZ5tvqYR7ZAymh8BIazmH6OKLbzv0KZvJEz3ZzEFnEolaEtV2XEaCLKadrIz//TQnk1/EU85NuH8th8Yf4j9gMZUOrNkZEVZCnsbtTU9KW18GqcKFyjh420sd2+j33pg3F8uTsLaDwEhrBYf04O7N/2t7/o/C2FoGnsIy/YGlvAwSfCvZzLOe+8oR1ZT3u/5uvHJC9dGtJlMrfqjslXVHwjpat2aLi2rjFFLjUSrFUjlO0juddXSSXx7ICCE1QbjiHO0/hofbPgwpnDTOR2V6hWNQqGUx34890noet5yaO+Gko3Y45PO7/uB/lvnrwxrWdha1absbgxo1FWtwplXqYSJY5Nn5lU3bLHQmGA/yko0plVSSjMjIITVzKNTR9sO7dv8RSeb/T9BWmMkKv4D+YzBXuljV7yxd+zfte6VeHGKrHTz4+cv38JWmyUmKzSGG5z7VndoE7kz3uPtq+Welvhwm39weVjOyaoFsBZPI4TV4gNY2Pw79mz8KyebeRIH+VEZTaX0sf27+v794TKmCxNTzr/2NOPj5wZBVjjdYSklq6jN69dyKuhqmWztivYob+RTSkPbe/xMdlMUJn77IiCE1W5jq+s4dYEO6mzsYAmvi/+CrH7LDYxPcBq4HGTFVcG1ULLT5orS1ULIkoSFI2cMHKG8obiXcteOCAhhtdmo6gaOh4EWWlkyYU9gvHswXfgV19d/7+LVkSWfBrItJJhObL/p7elQR8fUZnEV70XxPc01sM+xrzhU7toRgZIHuh07uZL6xA3LBaYB+Ar8rBsfz34YX1j+D5eu317QNGy2xPquSE4mDuXb2IujY2AgytNE67RiKFshzuwCR5s9ZSMlsK0QEMJqq+GkBKOF5yFzRoidK5BoFCeMjM/8mG+a//Xy0Li55KYLBRiTrGjwOQ1br4VMBQuKVJeQKVPxMLlvPwSEsNpsTEECmBLSgbHUpwD1YGwse59l2p+9fmuig4fiNZIowrqq/6Xeqm9Vh9JbjcOKvqFtACX7gV8kTVZvkaRoRQSEsFpx1OZoM2iKxxuHLtDcsZlgLzYZfv7m7XSv+r7fIm234XSP/8o5ktWqzqSyZr89PoXPYDTYkZvziw0NLluKayoEyq4iNVULpTF1IaDjHHZmoAW4aep9geN8fiLt998cGYdtVp7K6iqzXGJFUCAi7jdkuapsBJKcPBwgyP8YRyV7B04Q3dDbpY3jg6gupoMNla5U41BbUN9n0sr1ScKaHwEhrOYfo7paCAW0WiWknihhW/0Tabf/6tDtxpIVSIhGnz1dSXUkDL8fSHKi4/lWPId9Kp3Vxqegp8J/m9f14D6DQ/nmb281FwgkZ1Dj7bnSSFx7ICCE1R7jmO8FJJr8jCvjeNrIxFjDJBpKVaSlXhwDw384MyucBoLAGEfHI5ptO6n1YAq4FjorH9IWjUOnFlF3pj62aui3whbI33ZGQAir/UY3XCVEvzgdw/8NcSyGUhSlpVWQrFg2p39xp0JYLyIohaXxdZ2FGofG6yi85/QS32F0Asu8URgu1+2JgCjd22xcsVElPC85169Gaa1YTkRWJKpSqooBiQQzONvq9sRULKKxtzzAEJw1api2EFZjoW3K0oSwmnJY5tcoSD09HanEDztubnfO/IopyUWC6sUmZUpW5aSqkgwgK04DxxaZrFivacCaIdAuH9zaM1rSDgloOwSEsNpoSMenvU93dXb+EE5taFivKElRqd67qrNmsqIF+yjMF/i56MV2JqadYKxXMDXM6+4Wu04pf/kQEMJaPuwbWvPticwj4Il/NnTrdl7JrqaDC5wTUle1GmdWWVCw1+JotjA6PgnThsIdQrXknF8arkJi/+R355dbcrUaArU9ha3WqxXW3tHR9C5dN//T9eEJ3aGdUwP7T0V7F86Mr0VW4mF6o2NTS/ilaB2HDmb8wA2+08AuS1FNjIAQVhMPTi1NgwRkGKbxRxMz3uaJSRzVUkumOtLwo6Zc7aOkVdEhynN9NQ1cyuNqeEqD67mX9TXGyxXbJhFthYAQVosP58S0909czfqJqzdGODVqaG/IUbCWr2p0yukfp4FUtDfeir1yl8IPUGjPHFy/fqJyKolpJwSEsFp4NEfT6Z3YBvOp8MvMc0hAi9hHNQ1cBrJil5TUZxhfXsTuSdFNhoAQVpMNSD3NMTzzU1PZYAM/ProYkg3UV5rHT8lXmA7SwnwEq4FLLVkRI04HM+n0LdvzvlEPZpK2tREQwmrR8ZucCd7hePr7rw2N5PfxLUZXON1zHKz4kb0KnIttP6Njk8tyaimbwXPrsW/yq3v3bhoqaJZctjkCQlgtOMCYCnU4GedTI+NpQ32XbxH7QOmKG5nzdIWZJz8HNkKygqI9TmSL2JSiovGVn0A39c8WBcpN2yMghNWCQ4zPc0HRbr6GEs6chJFnmfl3knZO4/hmII1B6fiFG9br0s6qAeXPp2WUrhzHeXH/jr6n5pNf8rQuAkJYLTZ2kK7Wul7w6zeGx9DyUsZovOodOizosTg1TM9k1Wogpa7lIisOF+w48E/7E5B1Y/cgtdizsBKbK6c1tNioT6X9n3MDcyePOo7OoJqrC6S0+ZIYV+GSOHxvc18PJCxXG4ed13I727axqTp9yk9rX1jutkj9S4+ASFhLj/m8axwdDdbgELxfGsLpoZyqVXPVU1QugVJUV0dC27p+FaaBWWxknq6ceAljTNMiAf/BoUMbJpewWqmqSRAQCatJBqKWZpgJ731Zx9pJM4aK0hXe5vlKVFEbKFlxs3PvqpSSqpbzKztRm+gnEkktnU6/2GFMfa4wXK5XDgJCWC0y1iAR6/Z49iOjY7C5qkG6mk+3SFQGlEP8FFdnygrNFqBsn1OxP5+K5pGHbcBhqhT8fqu/v39mHkVIljZAQAirRQYx7Wj3Zj3tddQjVVJ4l50CMjHe8mqOTJCCvmoTyIrENXx7Uinbm4Gs2PZUqkObnp76i0N7N36tWl8kvn0RaGnCGhgILKPn3B3+xKVXDh8+nPseX3sOlpt13+P4uonv71WeDqLr1ampFB8S1JrulNaHc9rTMxltcpofOeWns0rTLkeIZUHRnpm5YibMf7kc9UudzYNAyyrd8ZLpWvfgQT8w+oyevXeo++bBtaEtQd9s1/ffRsV3I6eDJCp+nourgH04UZQnhIYfWm1o8xdUGCU8/E/bil89sH3dlQUVJplbHoGWJaxnXri2HTvd1nEEcCBS3z++MLi75UejQgcmJjL92ax/gNJPo6QekhVXAbdvXI3D+XQ1Bcxiu02zTAEjKFIdHTQS/S8Hd2/4YhQm/spFoCUJ6+mnL651gkwRQRmBt33gO+c3teNQYin/oG6aKX5rcKEukqqoWN+Ij5vy81v8UATDG0WGC21jlJ96K6wKPpWd8H8jChN/ZSPQcoR1+vTppJPS7iw3bIZl7n/++eFV5eJaOczX9Z2YvM1LPxWpocBHKv8qHHdMqSphGUqqahaThfj40ITBcbLnsDj6oXvu2bS4n96JVy73TYtASxHWo48GxrUx+5Cu+XY5RH3PMzLGxF0ktXLxrRoGNVPPfNtOolIrgElLGYH2wbZqcipdIFVFlDbfGhqfj9bskCaHHS/7gTt3r73Y+BqkxFZFoKUI6/C7Lu/Bl1jmlKB8PUhcHjHufuyxx/g5lbZw+BL7bX4EoiZqyS0T0uM0j1+82QSl+ua+bhxj7GjD2LicwWkLzaarigbKsmDJ7gcTmezMBw/t3ixntUfAiK8QaBmzhq8/f26j77pbaxo3w+jetPf1B5D2RE3pmzyR4/nH+Mti4Wx1dUrCHO0lSVGqskFUnakkpn6mhu086jgYHkWTW3Wbo4Tli6L5gqYHE47vfeDufVv+YflaIjU3KwItIWEdO3a9Szc0ElDNDqcLbHjmxas7a87QxAnX9ljfxcr+Mzs29ykpi1O8iJjoR/cm5o7dnUl89LRLW93dyWmVIip+Kp7pmlWqIvQ8Mga9Gslm3Efu3LX+K008HNK0ZUSgplnGMrZPGxgYsIKeXa/TA61jPu0w0+7xBx/cd3M+eZspD0wbDgWm+RXP13cODY/jWGKuGAb48jG+agNpilbqlKZoWDqDY2AyjtNUlupzYZlKpXgaxIVMNv0zd+/d+uxcaSVuZSPQ/IT13TN34QRvZW81n6HSDdMLUqmjh9tgd//Fi8OHEl3JL3Z2dh3MzGA7XU664llVWRz/QhLjNYmsmaWp/DjCjqIDdlaZTOZZ1/A+fGj7hjP5OLkQBMog0NSE9cSRszuswNhdpt31BRnazM3U9IuPHDrUuG+419eChqU+cvzqjp7u5P9KJpMPpqc51Zv9QntLkFQBEqZluVCw/7nhaP9i376+8YIouRQEyiLQtIQ1cPT8GjOw7vE8tyFtxBrb2MBXdh579FF99g0vC0nzB548ebNHT2l/aFmJj1BPBYyav9EFLaQ+jdPAVNL8/pZ13a8qiJLLOhAAjvrTRy/d0enbF+69d0tzHFhWR/vnk7Rple6mp+9uFFkRGF8LVj/08IUN8wGp2fIcPLh+4sCu9R+F3ucj0MLf4vaVVnChqYWmdaQS2jpY2vd0djh86Vqh7c3Yxm8dudTPxaW0lrn7yJEjZW0Tm7HdC2lT0xKW1xecgHE3FDWNcb7uDh6+r/96Y0prjlIO7ur7TOD5b3ayzt9ylY0Gl83qKFXZsCXrXdOlrV3djf2LBr556JOshLDmMWhPPXV6vav5O5jVxYLUhNl3iIbV8yiqpbI0bQcP85C2Xu0l3dczC0XUN4Pzb71339mFltOM+Q/0rzu5f2fvu1zH+QDOt3uZ0pbVRMRFouJK5qqeTkhVqyBdtdUmhGV5JI4cudrpd5kHiyp3tTU/8s6r+4rC2vCmaQmLWJO0Ep65INJK2tbpt75298U2HLuiLh3oX/95L+0/kHUyvwTieiUJHVEimVzy1UKeWMqv2pCoKEVFRNXT1aHawnBx80eAZj7TwcxdAc5Gi5fiaNnNT37nCk4xaV/X1IRF2B94YHt63qQVaCcfePX2K+07fMU9U7qtHev+xE/7r3cc70O+6w1gxuV0dHZiusgvJS/O7IskRXLs6KCxqj+B26t9a3uUREWi4plbQlTFYzXvu+7tB3EIUGel/L6e3TNw5NS8zYAqldss4YvzBC9C7559drAja3qvDoyg6pwCP+KBZaVOPPjazS1vMLpQKE9fuPnawDB+EqehPwzWuAuSl8LPg90WVxhJJPWQCUmPBAWTBEz1TFUGpqO3wYYvIPgr2az35a2b1/50V6f1e1NTlVcvEzB0xRekj67usu5FmS2/crvQcaol/zeeObfTSOj91dIq28PxiaOHDx9quy8LtQxhcZBqIS0Dhkl2l/3yA4e2j1Qb2JUUD1Iyz1waOQib0vsxKXsAFvH3wMB0JySwtZC+DBPTN5BOCEnhrI1BuKe9l6tIzsVCiD6E0DOabrwI2elZ09aP7N3aNxjheXvK+a1OENa0EFYEyYL9rz072Ju03ZpNQKj7Xd899cKhNrA9LASvZTY/s9GcHoK0XsrakLS8UklLxyl+/rj+/Qfu2367sJNyTS7SuZfneO7ffweBGScu3NwAqWgrTvTc5jjBZmw87tMCfRXYKQWOgula4OiBOQUZ7DZuhrAGdQXxV0zPuCaGnkv3VPGHOpPw7+QPR62OM5HhdNddGOeX2kmCbSnC4mDlSStVTFr4eLljdHV+702vWz9R66Cu5HS5h5hmHvz3QiOxwJTRo2BGgY06dm7OVhewYGAY6s75oD+ZDs4JPY9JyqSCQ7ABqftd5VFM3/j2Ja4mtsWpJQSq6ZXu5UZTKeJnsHpohiYPRqBn04nkS2+CQWW59BK2dAjwS0Y4IHDz2ERWG8Gnwm7iK9W3sFmbvrqGPzw6gW8eTmvTM07XmTPX28KYd7EQ3rjnvv1QFHbPt3zT9DcMPHd+13zzN1s+/hC2rKOo7NjeQdsxT5LEWrYjbdLw05eHtwWe9jl0542u62HZHZIVpalY/yIlP5X3MHYddLLZfy4fmYiBhNuB509vw+rG3tKY+kOwGHLi7W/cS91jS7v4s9TSnZHGLx8CICH9lXNDX+zpWfXuycnaBV2e3e567nAm4973qv0bzy1fD5qr5oEB7KXt0u7B3Loh7yhWVfypbOalh9+wr6U3mbfklLC5Hi1pDRE4ef7Wj+EEiZ+amqpvJT2bzWjJRLIPR3n9riA5i4DZg720DSIrlsrvHXSZ9p7ZGlrzSgirNcetqVp9/vz5FJTqj6JRejTdq6eBMzNpHP9s//QrF4bvrydfO6f1JrCX1mvcXlo98Kembjotr3wXwmrnp36J+pYNeh5JdqRem83O77gxkpxtW3bgOZ/g1HKJmt3U1Rw+3D+zrc89aunagnWzpq6PdxujLz388L4F78tdbtCEsJZ7BFq8/sHBoMPX/I9hyrGgnuDUUZzrnnz7yQu3HlxQQW2Ued++fZmJ1e5LoPB5k5ZpWCPXz+08du+99zrtAI0QVjuM4jL2YcIZeh+2+9wF49MFtYJSlgmHE0g/JlLWLJQPg7RmhtyXsJ18eja0tivsXhj6xy9ve/mRR5TRcG2ZmjyViN9NPkDN3Dz1FW5z9XM4i+s1ME1YcFNpUIrVLHzJzHnwjl0bn1twgW1UwPHjxxPXpztejR0HFTc+F3YXRwxdfdM9W08D0zrs4wtLaM5rkbCac1xaolWOvurhZIPIih0OdVm2haNTfqUlAFjCRnJP4HBn+iUqz6tVa2nGpTe/etsP2o2s2G8hrGqjL/FlEQC5GHghfplSUSMdvwaEA/9+4vjpa3c2stx2KIsfUek2dr+EuXNF2xEjSJx98w/tbFt7NiGsdniSl6EPp84O3W/Z1oPzXRms1GRKWdCJdeCIlJ+vlGYlh997r+70+EPH8NHJEtLCauCph+7bmj81ox1xEsJqx1Fdij4Zxi9AT2KSYBrtslgxhOD2gWOyz7AstFzx6zFHj1mGobYUYAgC9cHge3ddK5uhjQKFsNpoMJeqK6+8cm0X6noXiWUxHA8WxAdWNyQM45HFKL8dyiRpueM7jllmMGpnjO+1w9fNaxmXxiogaqlR0jQdAkeOBPjczrnOiQ6jw88ESSOA6KT7iQzOHEvavu1pZsLQg4QPP/DdZG9Xx/vWrOr+mfR03SvtNffdxleAQIgvTzjBT0w409Mpu2faufZy+vDhw5WPMa25dEnYqggIYbXqyNXY7i/jCyvdfmaVb5hdVsLp9LJGp43j1/1A7/RdvdMwPRzEboRnLVHe9vEvL3eXBOB4ZMta22H+TiqV2LJQ26u5u6Bju44Z3J7O/Lvp6cwPmBanOwQ4uNHRTWMK21bSvh1Mm642nTWCtKkH07rnTE72aOO0XZq7bIltVQSEsFp15HLthg5J/+aJE12m3tVjOPYq1/dW4cTjHnwMYhXOce8xDd3y/PJW6OpMdsTRVy4iK/rKMR/jwvz825VIHFzT3fkx13UW/dnhRy3GJyeeHEs7n1XNibUPFvY6vtGDw5vV9w0Vofn81qGhZfDhi3HX8SfQ/3HPMse9CWcCX0gel2OIFJIt+2fRH7qWRaYJG85NxldGzV4tGayFSLQ24+q9ULyu9gJfMU5ELTn6wUISTl03NHz1KzyiJLqmX657OLLdSJgoXTO7cBxyN172blier4YCvBsFdSNXV2dC35tKJrbzfPfFdjwvC/qs9MSMxxNRsSqmT6LhUDQHE+jUBE7UnATXTuLsrRn01K2l/x6+qItiR3TNG8V59KNB0DGSfNXGUXwJY2Gm+osNhpSvEBDCasIHgVLTt75/aQ0MnXpBNb2QgNYEntfr4wu/nBYpKQLtxtdwAh0SBX3VDe7nM/Ha5vf1Fb/CURS2bCTAWWuxR229qRsbQQQbUed61LfW14JVKKsTJ5sk8WUcHbtlNANyTOhgcmAGKH7p3m1FWpqtuZCu+LByVdKHVMjpKEQrBwIW9tnpXOIH+QTDSH/D9f0bmCLewDn1I4HmwtAypPDZ/oe9oXKf/aMPsWxSs/RR13FHrURiZE1gDR86tKHEdCDMKX+XCwEhrOVCvqBeHNaW6ui11/mWDtLQ1kEiWodXE4rwYgepAPssTPCMOjIdAk94TZ8pMZjch8HjDorGFUTUAwlkh64be0A9/ZCatiDZWtOyE7ClQmIdJICJFYhA+TRV4Fo5/QIHiUvrTEbkVRCxiJfsSBbfYk87OTExXxdazY5yUgiRKfpHQ1YSkONmAZY+gV4NIeVFfCXoLNA5h/Plb5LzWAyzF+IVXdNnvO/6GcsyhjC1vmWZ7s2pO3fdOqzriy9asnJxZREoerDLppDAhiIAEtCfO3F5rW0a6z1PX4/nf53nG5RqqrpieSnULEVh8cx4E7ugH78H8tG9eP/24oVezY+pkpA8b/abhPF8le75BqdsXUtaFeaTlTI2IByEoU1l8oq1mkokcZHElIRoWmpejMMCMyCvQXyy7JjjuUcgOl4tLCzCMpTHgFpcgkViX/dH/ax2Szf8m2Yqc/MN+1r7BM/C/rfCtRDWEozSkbMjq7NTY5t13dqE6dhG3wsSqlp+C9DDi0ifLrqmT1f6BgUaPjiHN0lJAGAfvpWcI4XjiHIMF6ocO/EjmMa9HeelQ1LT1PRpoce/sJwOTCQtc+kfGQp6Uxl+9JWtmL+jNEaJ0gKBgbsygR58B4sHfwV5aliVWg3vCHv6ymHcdG868IzrVsK6pnd71+/dsmXxbD3m3/W2ybn0T1/bQFe5I8euX+9ybuqbXMPbDA7ZCKV4uMOecyz+9OfmWvj9x9zEw6JW+JuOX298WhE6qtwLEV3TL1tb/AWj7sqwfqaro/sdmcyM+vBp2XzzDEzaBiQsNH+e+eeTjQ+ohwqnG0BYhfVzNYKrkOmpyauYYH8KvD8G6RPBszrC6Jq+ystl0ghzXEZjR5+O4+iZwTh+eG7Yqa5rq/3hGzzTSkXKn4YgIITVABjBP+ZzP7i8ydasrZCetuCHvIvFRs92SEdlpnCYE2LOQi12OA7RNf1yjrphHIyE9yOXPnfNMDg70DpdTf8DWDKs5rRvMVwChAWrUgh21HzllD0NrigqlxKVC7bKQuOOWeGiuI7OTkhb6T8C/Xw3xkel9cXxj6eIxiY3Hhx3X9dHsWJwDaa3l1+zd9Mt/F4tUk/ijWnP+/DBb8++LWqvnh0c7NDGta0pO7kl6zpb8AJzEUr91kYEFdeBRCt69Nm4+AsSl6jwjVGckY6VwPwUpLhLURx9xliWvxFHi/w+zB0SWCnLsVpxnoXesSI2ngp4zmRJXPgf/0IleGH51R6uwjeX5MR76qtITh7+8N9Cp4GF7Sm8Zl1s35pVXVomm/5c1vG+Wm284njHJeJq44/FjixUAld8w7uijW6+xo3MhW2S6+oIVHumqpewglJ87+LFtcFUcqur+1vxwPcZJqYPMOyhXw6GKI4+4/GwQpjCBhe+6XDIpFb06PM+np5hhS5eXzw9bLJ2pBLGv4Fe36BU4kA6IQGw8MUY6MJywVeqDs54Z69zrWdY7jI3G1ZtUiSV6zzDI3IqLLew/wu9jspl+yywrA1pEed5QceXPT3jBb/DLrA5ua5UHZ/4eMTbFx+fwvE3DJO8fANrjlctL7giJhRx9MrfR89R+VgJ1Y6currONuwd0FNsxwtV02mPlWGLy1TxlPHf6Hh8PH9xesvw9yRM+5PIRT2ZIgVKKZxWUY/PT8aTFPji0i3m4Ed1hDWV/7uY9bNGtiGqAyorJRWSqCgdkrQiR5KddrwPlsq8xfhG6efvx8dvtiQczDdmmPaldDBxSVYeZ3GJXxUMWzxq5d4fPz7Ym7X1HTAL2A7NqtJHEQ3qtCPjw3LoxB/v+OMZ5VVzR5aHWRuErYA+y4uu6fM+Xl9J/lh7bFvbY+vmv0bWos9tsXAWSLIiaSnyApHxJz6SbFSFuXTw8i86r5vVRW1m+6IHmUREAuI0lcREP5q2ztWPrO9/YK54xsXHI56+cePvj3qBfimZNS+J5FWMcrjptThsRd4dPX9+DcwEd5iQphwozfkCwJKaLv9ewHYKeicfSudwShcnJDBBOD3MTwGRO0cqLIj73jQTaejDBYaPHTBgJ/i5+HyYijd95sFhRzkzB7yL2IrCtGwezj9nOQVTUlfPwiicifnu5J0qHHd8mXHIG6ZD7JQqIk9kJK6QwAokMWRUhMaSeJ0vcfaiXNhs7PyuwpYV51Vh+EM/Pu2M9GckpyiOuZm2Wvtom+Y4me8xPbvIIujzPu6Wbvyt1ejL3U7Sv/v754ZHsORwaX3KGdwiJhO5pzY+Mivk/urVq52jTnIXlEc78LKu8qAMx/G8kHhyOicosz0ovM3IrIDKb15HSvDoOoqv+hMLYCOWI8ash0vmufryZVcqLz4u8fym3ov1xT/EVp4UDUTn4/iS0xW+sZTMojASmLqGp64iH4FRXJQ2TKj+lv7JVRTVxwQkm9APyaboGnGMzSVR6VR87ipsVT645ovOzi5tamb6zzB1/nqzjz+s9YetwLioZW5C8jq08K9+1IxS8yQsfF6ap1WL2BK8VOaJc6NbPcPrx7wJ++hmHQUPvOaQgMJ3ETtVlERDP0wVsQ19uPgcLQyt/Dc+p4jlL6k/1xa2qVyh5ApEzEoErm/DsPOTXV3de6anq36roFyRdYWVbVSshHJEMt98saIXfIu9koplYZL6m/hUz7kS/Jt0/PE8+Jj6X/Y6k+fv2tA1BKIvB/OC8WnGAmp5dpqx3XW36fjgYK/upXbhFd+BrRlqn16MfkrspkoC4hnirYjbUVWzs4rHx8uL3cerjwt0TA4RcBcsuX8Rn97q54okVsCKJJ9YkSvy1gJR4aOtnAr6OJP+L13d+BKBKMEzHhAfgDh6yzD+vqHjTDDvYpAxLqwEfVdbE9bpIEi6V27tdLP+LnzPrWS/XrRTnz5d4e79+LNY7r4kP+Z7Jv7z1LyPL0B4Tb+ci9cXLy+eJ54e8Rw//rqqcUR+HOrgYVprJbBl5E2w63oI64J7k8mUDZLGhmAXs19ucVkxP8gKQu4ptCxbMy2TW3KAGI4u1P207ztH3CDx/7bL+Cdse8h1Zy5ev7Dp8uHD7blJuy0J69TV8XW6l92Dl3cbLG6g98idbhDgdANcY1ZY9o2N4mpNr96GRf1Da3Wui0RW69F1bWslvp81LD2xDTOGu9DhQzBc7AcYfYlkAqo6A6ozqHNBYJTESGitTGShsp0qQSxT4AcoPJQw0LBlEPhBFakHDjoLvY+XgVIyg7WK77tG8n9pvpHXBbXL+OMBd7FN6KLu+uf27esbX9RHdIkLbxvCGhgYsDb3v2a7obt7YHakpKmYiqgE2ioqJbzIOszXcSov/DAzRRNehyJKvPx4+igv/ZLKEaCkoZxUFMYXE1I8f7Xyq/UHp9CkAlfbCF3NdlhS7IQguA0N2wiJYy1ktC5IISb1Okr5jSYruy2SGlYkIkKLSC3yy/WrUWGzSnjaTUX/QEhYQuNewLCdwBFKRkpOuAfr4sBnwwfDg6B0MHagORhBHNqHw5WxTwYav6lAt/42MBLfrYZXHO9w3Ftr/B0Hp0pY+tkD29ddAz5ln8NGjddSlNPyhHV8aKjbzAS7Dd3egRcvgRHJWyrHASw9Pyp+vlSxEluH0jWAGQF9VVZMpxHVRZ/xSKQU4PR5Xy0+/sLQZCFS9DN/XKtSeh5WrL2x+sMyZv+W67+vwz5eC7oDx12rm9pakNg639B68XL3Qh+2Bm94DySxHhg0daBHSQhiCbyyyMS9SDi8RhEHyYP1qD9qak0S4VGn5VYrSTRKEkKHWYYiHuQmCYb/YKYLqS+3H5LYckxJmz6qhSYJ5yNgzgtuclESpncBfN8Fj3lgJdCSGpHcGECoxrouMoHjzO+4evLLMB1VKxJV8Wyj8Q80Ix043jnTu32hlTdkh08Yn7UWcnio9Qs3pzZm0lN7LCOxIdIZxbuQ1+lAVFFxJB7aMeUIiPkiPRPjo2v6dPF4FVjHnxi/oQK0Az/bymf5uI7ayGLj6eM63nrbF5VNXzV7nv3HViQL3JAEaSV1z0iBNJIgJBCYkSKJYbdjEiSHw7a0BI5s6QBBbINUswMUsQ6E11UojZGccA9dcZDBdQY+TgyFTgkiEKYyIBvstAQzIRk8cBJ+A2j4gZFDFWAqjAp3V5IhQYYwwUJ57ByS0QINzMYK8FyrRxt3KNbXb2qG/UVNT5wDyCt6/A0boGbdqzPA4tD21SPquWihPy1FWHjQzYs3xnZkM95ePIZd8RccBx1xez/UPowp46I4+uVcLD9/8Plq0Gfy6Jp+uez5uqPyY+UtNN5DuVQc06drpv4bIDXsjtsMpdkOSC79QK4Xog3PzwF4IBNCBiIhpBSpoE8jioqWaM2KCRuOqwLXgIQItKIe0lCYD/lZjoqgGIo0+J++SsmMKA8eqQ21qHuUh2PfzQHN6vgG6vVK8GfmQhcbr3Yff+AEi3rtdCtNF8u/eIWD2ATXx4Mg0XH1Vr/hm7sDQw8PvyvTrriKWocEE0C6oM/kJRJHrAykgj6WGlq+JUifu6YfS6pu4/UVa6AgQcXKi78ApekhcWFBwMstEkTX9MvVHw+Lt2ex+4+Pg62CxgsHEwZbAdgWIJfA+ICkfDRYtyAwWWB7Ay8F8VT/KB0bOJ4Gx/CQfUKSwZGrJJs8iZHYgB0zMB+zk8hopQ8hEcEog2ERASIBAOL5fIrVIKLxXKtzKPZLgZUckvGf+/nH5HsK0+Uz3316zeAjj3D23Lwu90w0ZwNpiZ72UnvwfO/AXIFnXfLBxLOsHn6yiLqmr3oQ04LHX9hq6TFHI6txrlYWkHj98UT1lh8vryR/rIKq6aO204drdP8hRWF3itmLUw42QnW1CSTSA2IAIXkWOBYKLWw8wjVqNkEaFqjFwLQNJhWI4ZiFoiq6QX0SbsEo6HMoWVFCYprwjw6FP65BXCSoXJwiOwpnFK9A6yiWkQhRDwA9XAfpwLS/AqnqSKP7jwapquiznXFXMn6x8Yg/X/HySvLHKqiaPlZfvf0H6BloAM/v3tpzHkJwUx59Uxb4GE5Lfnt2ZGS16SX3+F5mq4llfegtwnaSR6J5EC8hPUV6IDaS6aDnoZ5DpYe6AtdgOr4pyhXLNPH0KKCo/DDP7N+S+mI6qHzbQr7AbdgW+iylWn0l5cf6E29ftfSN6L9lGl04x30tOtMHklmLhxpClW9BL4S1T+i2uNPRp+0FflD0AN9A9LHnmHGBBfJCE3QL9ALiguoJqiu+64gDzWGIIAlhzhaSDsMV/yjJi3BxyY9khP9BXBSzEMY/AFORGMmM1yyKZfmm+ZKuJf4uMHV1THEj+o+S864E7zYd/8Dliqp2MamvPbt9uw4dY/M4DnXTuMuXx/scK9iHLcbryzfKwvOJBSGNPl10Tb8WV0xYyMFymDdXXv46Kq+ueChJQI4WlSUqf8StOf5CNdXqr9afxe8/Gm6AoLAqGKyCGLSG350ACFzKM2FvaeOseEhFOsjItdQ2S6wYYmkOdl2+CfLBvmpIV55vYY2Qn6uAxAWC40zbhxSmWArcQj0TSIiSU37mx0kgVesgLereOSz8E5EWJa6Qzyh1hZEcO7xY4Ct9WLfNvwa+5xA2h6uGP6vMPxMsZ8WNf0Gf+cOCw9usq51a5+kNG9Sn1IjJsjoO0LI7EpVra/vxhPdFs7JyjYriohlbTAKGxO1C6oJEljseOLqmTxfPX66OucJK66OUNzuDjK7p05UIbGwX25I/vrj4BYrnD0uZ/Rtvfzz9fPsPIkgkbL0DZNMFRVEHFEY2ZCBTcwMLdfCsCCVN4SwpE9YG+ARNgD24IDHYSYB1yNCYDkLRFoC8oOUG40AKQx5IYyAmlQ6SF7dDoSof0hbJiApzqLs43aPc5UG+AvVQ/4T7nGQFQiJ5kdbAkmgH2Sz0FaWB4gLrad22v4nmuvPt/yzCc1+V4t0e4z93r8PYwDCvNANxLSthkai0jmCf5+jq6y6Y4SkjTfoKprgWufj9Dg3AozBmiK7pl3H8WDH3u0YfLY6u6c/HVS2vSvsxoygyTF2q/qNenEyjJ5NJPYGPRidME1M1/JYqwyoNq32Ihu4J0z5M+WA2DoqwEI9wfmEaEhQJzPNsKNOh0jJwrfRVJqbnNOrC6IGwQFzgHiKrpCuq2kE+FizrMXWE7IWCEKemg7hSiimOQchNIC3EchqpHlBO95TshQThkwF5TL9k+Mm/MZLGzVo3AlQdLzagDle1vCYd/wU9/5Z5ZcyZPnNow/J8ZHZZCGtsbKw3rdn7nIzTx42o0WfP1cPKuYJ6XPFs5q7p8zmKx5v8cdcxDeMPOR1fj+gh4X10TV/dukiC+nJPeLy8eH1hrtm/UVvpKxcrP2oL/dlcs1eQ9PCeo73wGcp+R2Xyvlp74vH19B9EkoA2CYKUlcQqJCQj6vkoyBjh/IurcJiy4Zxy2FMptRBO7sK3kClR0UYUZAX+wMqfC1ICiYHMYBsKSQsSFKaAUEqZLoiK00ASFsgpN0UEUWE6yOkiiArE6NmUb91OWwAAEuNJREFUszCNxA0c/uBoF04W86YOarWQAYjGmHBBEIkUiXEqib025hNmInWknv6zKo77Sh3/RvcfSx5Xl4O4yr5Y7NxiuEEQFT4uvs8yrF5VvosX28LLS185vsiRHkc9YPiJtrCbJIzHyx3gJdfpl80flZWPR6qIxJghus7xjSqj4E9UNn2VvN76Csqq6XIR+48OYEeGlcAaXhLfQwxNQcgQEI9IErOOxBUuCuDLz9Arm5iyOTaYy7Jty8hAb2VCm43ZmwnwQTbgFpAWyA4SGEKhaMdgYNpngKAcpeMCAfFjYGE4yAqco3RZ0LorUqOkxVkf6AgzvFBPFbISSsOUD+WRrWijpcwbmI4Gomj4yxAIv4bPVU+q9sfxk/EP36UlfP49N3vNWr/m9CZdX/zzjDDofAoW3XHVr9NPHdB8p2+uORl/mjFLUktMbBTtkSJbpLCRxYyD5OpJps/4+DJuvq5IIgoLqfi3pLzcRuloM7QSzKImsBSWG80LVKkxkSvOkFHaCjL5QvrPN9rwvaSVtEg2ICmQCNRQkGjwnlOpNktMxdds+GxcRFrIyCmhTQMEUJjl4qwtzPbAOVC8o0DUZroGiMmBpEUfRBZ4DvRUJC4/1GOpij1ML9XU0PJdFxIZGsOpJkkOQ0YdFh5CPodKl0WfRqQkVUhTIEf1iN4GkdJU4Rx/xsJfHkpfMv4cd+IAUJb1+YdkfSU7NXp6+/bti7qquKiEdfVq0Gl2TO2DonYzAcUTCv0slCB8FuGia/q8j7iAPl30aNIPHVKq55w+00MvjFLo05WmV8H5P9XLzydVF/H0xbGl9UGfjm226B98po2u6fO+0f3H9M7SbT1h+FoS00ybSmm+5/RZHxzbwWvVHtSvNuLRR4BKl0vPtHRhWh1SESUsNBkH0qjvNiAx4MA1JDBc4yBmTPmwJArJCFM+dA1SE5XsmFIqRTzKUrZYkMio78IUkauFoW6Mcbin1GWrOR8nqOEUEUQFmuK3ZdEw6NFg92s9j3XLp0CIsAuS8VdPkcKhCZ9/KAc81x/c3NdzFjy6KHZc0YPNh7VhDg9jYnh4co9n2dvx1nLalys7Rimx2xLGigfEJBQ0Xr149FkBVb04BQiTlPAFbTiDxRGKM1pJf5AgarPKG0sQu413N07hkCANO5m0fSebtCwziW5DqMISHTRMJCDF23inYbmsauNCHq+Vn1ta5dErzKN8psP/RiIXVpAegKJQ30Y06AQSEXdAIpdL0wbTNsLpoSIeCwRJHZYBpTusIFAIlPC0iqL5AxoCcmLPQkkLdITRCc0dSFqQD1A51g4pLOXmhZCwDMO2BpH9q6ZtDoU4oKQIy5yEynFnv+mzw+0+/q3Sf5yT4aYs89zq1alLIK7wYeQANcCpgW5AOaqIARzxcudrXrMTz+cuFAxBI1Rw06eLKz3xsnDikt+Mmr9mWBlXrbySeJAlTt8MXJImXHRNv0zx2GpWZ3r0KKqzXHlRHH26+fQf+mkbg56ADjppUuihMJl7BEhGtmnj+4Phj1lEUAzjaQcgJkzcqPPmlI/yjdJV8Trf/+hbeYyP0uMS0zSVF8SEaSELxkhR6a7IC1IVHkNMBWEkCljxYQ7YXgWKrDCHw2ohJDDKSkr5Tst3TANBp7DdgkTFKSOpxYMtV2i3hXQoJjwbBo3L4oibAajdXmSbCl01PEvi6x3PetMvwfi3cv+xHpPRk8GZvo6Oq5y5FvZlvtfqQZ5v5igfH7iRdHqrn/H24McyEb6ejCUxkCwqEATi8JDNKtWRIxI6wrLj+aOyQgIqLT/KTZ+OLYnCFGHE60PdSgzIgVmcfrbt5evjYkB97VeNyv8plx/UYoChElhYgB7KtD3PAUWRpejIVNzNAjNzyDuYRqnrMF5dIx4CkTrlAJQRps2FhZIX5lqYwfFLOygTBeSmkUhDEgNvIC7MR5ML6JhozoCpn+858G1utbH4j7BRT0Z9VlZzbTyOKJCKeCjkqYbkFBJh+DXCPVcKuXKIFURlm8WBoZSFOBCYmk6i33ioT+Kw1CegEMspcFfe+M8+rRySNum/YUwm9I7TPT04NWOBDg/nwtz16xMbEp3mPswIOuI6G7wBSlynz1pQWZEIP0smIcEEWN3QsfJDn+nj9FFSPh73wilgdE2f+eOumo4pPqWI2kI/LKu4RVXLq7H/kJopRUFhnkj4joNT9KC/BlZgAIVD1I+cwASVUBgCIsF1KEQxJLpGPKHGP5LYrAs5ikREnmJ61KF4K5cG1+REVS6HC1JauGroYYcOrLWUEp6MSF0UpoZgK5hV2dgEzeNLYbMBnRQZEUPnOwGMT6GOp57Kg/0WTCMYjnsQHpDmlJFTR5IcNt/alvV1PdF5NsKcLSpGG03L6QcjnWDpeIXqgFYb//A9wGi1+fMPDeqY7nae6uvT530KKp+JebkhHJyX6Fqz33X83tCgRr1d6gXBH+XnFtEwDmEVMBfAtbK7UvHxVTb1gGLQokbFVBZMDtUJHmT+dsPxmqSRU2nkrxkWxhfbOfEVwLov4sIaonSRr1qZy6vy8xliPbn+qPjYHxSm6mJwdB357DfaVtJ/BMLeW0/ayVQSR6TA5AB7h8kwmFeRrFBUSFYkJk7GsM+F5SuiCQmFBEriCskHYcxfEM9ozBjBS/yaKD//rBzndjD3BHswAcmqwFdhOWGugCw5owwpEt9sxMlVGWQEK4GlcAOi1XAcL6eLICfdcMFmNDnH7xdO/YTCHTkxM2B6EiSPbuXmHrZO5eJy4Iu6lfo2Gu8orFfA+PM9UMjnHpBIx9v+/Q9Wm8nMfcMTE1d7u7vP4Ec6fzy1wqOGP3xI63JHjgT2/rsy/boTbMP0pe78dVUWS5wjK0VUjIqNN3kA62ZYeIcfxofXDFNFUZBTT4W6m71mWBlXrb4yWSoEYWh0jVIUdJEmzA6o18mRDN7dCplCEkK8IiP4WRAU9OO8j5wimZB3SAhKYlJEphLkJCaSEP7PEdxsfVG5UWFxP6qPPngTlvBED6IWLN8dTPmg8ocFPPRXWBdlFWqqCEmLlhAgLRtKdLaAkpQNfRUM6DUQGOUiTimNEaT7FvRVw/F6K91XG4/mHf9KPaovvJ36jzfSS1mpc6mUdhnvhZL4a0GjZsKBKK+n0+kt0AHvztCAsIzjeeAeUKVPF1l101cBWCICxcGmcPalUeHRnyguIsJYej79fFnpKxdjrKhu+spVK69Ke+OW6SXlh7Xk/8b7D5umJKY6nUiQAEmp5ZKoD5Ay8kTFzcAsJIrL+ZREYCWAaU4ubXRNP8wfpuSuGubHMwCJhSuGPCiYJIMw5GV6xkfY0Wd+WoPiBAlEhvnzNluw3SKZYTkQHIQ5J1RQDg7Lw/QQGUIdFp4wcC9KgQ/7KkxjucEHROVmc3ZaCFfEjMxUvlPvBZ0WhT1Q1zG06hQKyGPA9qEh4bPRJuO/0p//WvoPyXpa77BPr9L1mn64QiJRT0vlP3jg1oyn0/th1dnN6VOkQyh8wVRuPpLUH9GHi+sckD4vLaj43NSHLwfv8cKjbGxdgc97JUpFpIRbpovKYHTUltkpHYkyEqNYf1gWfZU+Vn+JiMZERS4qKyTAMv1hmwoItLT/aL6OL9cn8A4mknhDkR5CUuh43ExhAXjnIQVxRQ9UwnU1JM73meHISINzlY/1Ir3jwNQBtui5IpU3K2mFZbEUEhgJiHlZhkqI8rws7hPFxBHlZ5romu1CGRSv2HyQEQiLPkwefJcSk2o0mU+F8Z46KswbKd8qvRUWiq7BsuoYlF/q+Jd839p4/KNnFHhw+Fbc819r/y3dHO7qsk9D2lLPBvEq59SLXC6CYSCq1OTk5F48g+FxLyQSvvyzhFK8taaYL1ACiYdkkSOg/HVO4irmAySLlR8+yHy5wnaWysTF7YmnRxdyecMXFDcxx3KjNCUEGUtb2r4Iixwh5qebxEG58v2Hkh0ERqlLp5kClNLkngLSyF8XExrZi089SYbFm9DRg1FCbEKyoxQE8sqFkTOgTwrDVIPCP/k8qpRcGrxMEXmxnpwjUeXbhjpgA2bBNsp0HPQWOiwNOnddw5YcNIdSFyzTlUKehEbrLDxDNn7osjCXPw5FO22qgPfKHn/pf8XxxxetvSvYlX8BxBVKCdGDmPPDhz0W+Oijjxof//jHt+Hh2oko/qKqFx4l0BJQmQIwS3RNn/fxZXqGFbq4nQzimI9tKFs+S1S1KJ9XoQkEfUQwtKg98fSzefMMwmx5F28/IqK2RLjM2b54/gX0H0v6+IiDZSVgHJogfYWNzDMUpCtsUkKg4pKIUJAsnNTlkjNWzfBCPMOhi8JAiCSqPBmyMFVQ1OdctQwLywNZ5cPCpDl80D6IhjzBASQF0sUeREpSJCyE4ceSpJXbEO2612AHepaTSRn/YrtEAD3n8xV/ntv4+S96nyGRO9gccQZmEPiBK3bRi5kPHcG+v2T32n2+53bxNY8oQyWIB0SR9OmqxMeTh5lm/8azx8srEbCQNSqTpUTX+eagwCiPqiWeQAXO/olHV2tPaYUFjWCxsQJjt7MV564K6iOB2Xj1adNGa3PqDMFl4XwSSnAQCUIibqFPlwtTwbiOkoSR+JvLx3KYv9BXaSrlLyifSegQBNMFTAWhiIeFArRZnoX+8Y2EzKhbnuNlYO9wFpZXkwoH5Kmj/6qOFTz+0n8+Y4Y/2pVIcJqY35+YJ6wjEN33ZzL9kPY3hWjx6Sv+RcByLIQAZZYQJSn2C944FRF/QkvjQ31XZDcV04GVPOGl+WdJEhVGbaNPV3d7Va7ZP83U/1ACgzTjkg4gjUFvHhGWkrPAPnnBLNeFSEKKfAbzOu9yBAUdVj6cZURpZuU3XOUILioD93x2IEnxxFGc9c6M+M93cHSNZVzHquBQDeMn4x898wQ2us7pgGvAbyU8/z5e5EupVEqtJirCgp4KHxVI7sbrQIYKHyKF3+yvIvEEX8FsQNk9qXwgBpgQwNo7p9OKrukzfdzF08+WTmYrV35YF+tU8bEpYImInGtLVH+8PkzZ8iQcVpjrawXCLOHH5uo/9JmWjbXHJMQcNhVW8bOklbsumnJw7Q+cgtVK2mJxAUNNKKncp54KHuzAwnjCE01B1UIHA1A80ik/IkdIfTj6mE8MXh2sSKZhdHUd+IcDykwFLj4eMv7Fv+il75c8/xEmeHaojD+jZ4LgbsPVVvO5iutg4oSAFCCiAqVp/jrUKRU8mzVexsube05ff3tiD0Q1wkP/ojrYgeiaftiheHsjLKL4GrudTxYvb0H9h94bpzeAwCD4cAqJf5SmlBjFH5D8ChVC1Q8KyIkrjtgbE64y4lqtINJHel5Hq4q4ZdsYzsWBWaU+rkFWtFzQbiNNnWciNbT/qD4+Hitq/FdE/3mWzmvQU+W4hZZPenQuRHRNfylcvfVjpUqz0Tj6dNE1/fm4euufTx1z5am3/hr6z6lj9A9ElneKwPJ3IYEVEpqKys0YFeUhoDBP4TV/+bjVIkfqKuu8/ixC/+tqR73111V4DYnrrb+G8a+h1tkk9dY/m7MxV7XUzwdP3ApBgCYG6Co+L6/+kcB4X0g0ERFFzwXjojBc5q8ZhqOKtWEoROmLEwSWBIHowVySyqSS5kIABEYhisRFEov8SgRWGD6K9OMgq8IwBIkTBBYXASGsxcW3pUoHgfF5iIiLPv9x+03kuLxMqaqsUj1KJL4gsFgICGEtFrJtUG6OwDhtJHHhqLOl+dBAG0AnXRAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBAFBQBAQBAQBQUAQEAQEAUFAEBAEBIGVhMD/D0fV/fpMMM+gAAAAAElFTkSuQmCC'
+ },
+ ...uni.$uv?.props?.noNetwork
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue b/uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue
new file mode 100644
index 0000000..5f134fa
--- /dev/null
+++ b/uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue
@@ -0,0 +1,222 @@
+<template>
+ <uv-overlay
+ :show="!isConnected"
+ :zIndex="zIndex"
+ @touchmove.stop.prevent="noop"
+ :customStyle="{
+ backgroundColor: '#fff',
+ display: 'flex',
+ justifyContent: 'center',
+ }"
+ >
+ <view
+ class="uv-no-network"
+ >
+ <uv-icon
+ :name="image"
+ size="150"
+ imgMode="widthFit"
+ class="uv-no-network__error-icon"
+ ></uv-icon>
+ <text class="uv-no-network__tips">{{tips}}</text>
+ <!-- 只有APP平台,才能跳转设置页,因为需要调用plus环境 -->
+ <!-- #ifdef APP-PLUS -->
+ <view class="uv-no-network__app">
+ <text class="uv-no-network__app__setting">请检查网络,或前往</text>
+ <text
+ class="uv-no-network__app__to-setting"
+ @tap="openSettings"
+ >设置</text>
+ </view>
+ <!-- #endif -->
+ <view class="uv-no-network__retry">
+ <uv-button
+ size="mini"
+ text="重试"
+ type="primary"
+ plain
+ @click="retry"
+ ></uv-button>
+ </view>
+ </view>
+ </uv-overlay>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+
+ /**
+ * noNetwork 无网络提示
+ * @description 该组件无需任何配置,引入即可,内部自动处理所有功能和事件。
+ * @tutorial https://www.uvui.cn/components/noNetwork.html
+ * @property {String} tips 没有网络时的提示语 (默认:'哎呀,网络信号丢失' )
+ * @property {String | Number} zIndex 组件的z-index值
+ * @property {String} image 无网络的图片提示,可用的src地址或base64图片
+ * @event {Function} retry 用户点击页面的"重试"按钮时触发
+ * @example <uv-no-network></uv-no-network>
+ */
+ export default {
+ name: "uv-no-network",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ isConnected: true, // 是否有网络连接
+ networkType: "none", // 网络类型
+ }
+ },
+ mounted() {
+ this.isIOS = (uni.getSystemInfoSync().platform === 'ios')
+ uni.onNetworkStatusChange((res) => {
+ this.isConnected = res.isConnected
+ this.networkType = res.networkType
+ this.emitEvent(this.networkType)
+ })
+ uni.getNetworkType({
+ success: (res) => {
+ this.networkType = res.networkType
+ this.emitEvent(this.networkType)
+ if (res.networkType == 'none') {
+ this.isConnected = false
+ } else {
+ this.isConnected = true
+ }
+ }
+ })
+ },
+ methods: {
+ retry() {
+ // 重新检查网络
+ uni.getNetworkType({
+ success: (res) => {
+ this.networkType = res.networkType
+ this.emitEvent(this.networkType)
+ if (res.networkType == 'none') {
+ this.$uv.toast('无网络连接')
+ this.isConnected = false
+ } else {
+ this.$uv.toast('网络已连接')
+ this.isConnected = true
+ }
+ }
+ })
+ this.$emit('retry')
+ },
+ // 发出事件给父组件
+ emitEvent(networkType) {
+ this.$emit(networkType === 'none' ? 'disconnected' : 'connected')
+ },
+ async openSettings() {
+ if (this.networkType == "none") {
+ this.openSystemSettings()
+ return
+ }
+ },
+ openAppSettings() {
+ this.gotoAppSetting()
+ },
+ openSystemSettings() {
+ // 以下方法来自5+范畴,如需深究,请自行查阅相关文档
+ // https://ask.dcloud.net.cn/docs/
+ if (this.isIOS) {
+ this.gotoiOSSetting()
+ } else {
+ this.gotoAndroidSetting()
+ }
+ },
+ network() {
+ var result = null
+ var cellularData = plus.ios.newObject("CTCellularData")
+ var state = cellularData.plusGetAttribute("restrictedState")
+ if (state == 0) {
+ result = null
+ } else if (state == 2) {
+ result = 1
+ } else if (state == 1) {
+ result = 2
+ }
+ plus.ios.deleteObject(cellularData)
+ return result
+ },
+ gotoAppSetting() {
+ if (this.isIOS) {
+ var UIApplication = plus.ios.import("UIApplication")
+ var application2 = UIApplication.sharedApplication()
+ var NSURL2 = plus.ios.import("NSURL")
+ var setting2 = NSURL2.URLWithString("app-settings:")
+ application2.openURL(setting2)
+ plus.ios.deleteObject(setting2)
+ plus.ios.deleteObject(NSURL2)
+ plus.ios.deleteObject(application2)
+ } else {
+ var Intent = plus.android.importClass("android.content.Intent")
+ var Settings = plus.android.importClass("android.provider.Settings")
+ var Uri = plus.android.importClass("android.net.Uri")
+ var mainActivity = plus.android.runtimeMainActivity()
+ var intent = new Intent()
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ var uri = Uri.fromParts("package", mainActivity.getPackageName(), null)
+ intent.setData(uri)
+ mainActivity.startActivity(intent)
+ }
+ },
+ gotoiOSSetting() {
+ var UIApplication = plus.ios.import("UIApplication")
+ var application2 = UIApplication.sharedApplication()
+ var NSURL2 = plus.ios.import("NSURL")
+ var setting2 = NSURL2.URLWithString("App-prefs:root=General")
+ application2.openURL(setting2)
+ plus.ios.deleteObject(setting2)
+ plus.ios.deleteObject(NSURL2)
+ plus.ios.deleteObject(application2)
+ },
+ gotoAndroidSetting() {
+ var Intent = plus.android.importClass("android.content.Intent")
+ var Settings = plus.android.importClass("android.provider.Settings")
+ var mainActivity = plus.android.runtimeMainActivity()
+ var intent = new Intent(Settings.ACTION_SETTINGS)
+ mainActivity.startActivity(intent)
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-no-network {
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+ margin-top: -100px;
+
+ &__tips {
+ color: $uv-tips-color;
+ font-size: 14px;
+ margin-top: 15px;
+ }
+
+ &__app {
+ @include flex(row);
+ margin-top: 6px;
+
+ &__setting {
+ color: $uv-light-color;
+ font-size: 13px;
+ }
+
+ &__to-setting {
+ font-size: 13px;
+ color: $uv-primary;
+ margin-left: 3px;
+ }
+ }
+
+ &__retry {
+ @include flex(row);
+ justify-content: center;
+ margin-top: 15px;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-no-network/package.json b/uni_modules/uv-no-network/package.json
new file mode 100644
index 0000000..cb48aa9
--- /dev/null
+++ b/uni_modules/uv-no-network/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-no-network",
+ "displayName": "uv-no-network 无网络提示 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.1",
+ "description": "uv-no-network 该组件在没有任何网络的情况下,显示在内容上方,无需任何配置,引入即可,内部自动处理所有功能和事件。",
+ "keywords": [
+ "uv-no-network",
+ "uvui",
+ "uv-ui",
+ "network",
+ "无网络"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-overlay",
+ "uv-icon",
+ "uv-button"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-no-network/readme.md b/uni_modules/uv-no-network/readme.md
new file mode 100644
index 0000000..0e4a1a2
--- /dev/null
+++ b/uni_modules/uv-no-network/readme.md
@@ -0,0 +1,11 @@
+## NoNetwork 无网络提示
+
+> **组件名:uv-no-network**
+
+该组件在没有任何网络的情况下,显示在内容上方,无需任何配置,引入即可,内部自动处理所有功能和事件。
+
+### <a href="https://www.uvui.cn/components/noNetwork.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-notice-bar/changelog.md b/uni_modules/uv-notice-bar/changelog.md
new file mode 100644
index 0000000..cdfec7d
--- /dev/null
+++ b/uni_modules/uv-notice-bar/changelog.md
@@ -0,0 +1,19 @@
+## 1.0.7(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.6(2023-08-03)
+1. 竖向滚动时候增加change回调
+## 1.0.5(2023-07-21)
+1. 增加icon类型,支持设置false不显示图标
+2. 优化文档
+## 1.0.4(2023-07-03)
+1. 增加disableScroll 属性,禁止自动滚动
+2. 优化文档
+## 1.0.3(2023-06-04)
+1. 修复text传值为null报错的问题
+## 1.0.2(2023-05-30)
+1. 修复error报错的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-notice-bar 滚动通知
diff --git a/uni_modules/uv-notice-bar/components/uv-column-notice/props.js b/uni_modules/uv-notice-bar/components/uv-column-notice/props.js
new file mode 100644
index 0000000..d93917c
--- /dev/null
+++ b/uni_modules/uv-notice-bar/components/uv-column-notice/props.js
@@ -0,0 +1,61 @@
+export default {
+ props: {
+ // 显示的内容,字符串
+ text: {
+ type: [Array],
+ default: ''
+ },
+ // 是否显示左侧的音量图标
+ icon: {
+ type: [String, Boolean, null],
+ default: 'volume'
+ },
+ // 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ mode: {
+ type: String,
+ default: ''
+ },
+ // 文字颜色,各图标也会使用文字颜色
+ color: {
+ type: String,
+ default: '#f9ae3d'
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: '#fdf6ec'
+ },
+ // 字体大小,单位px
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度
+ speed: {
+ type: [String, Number],
+ default: 80
+ },
+ // direction = row时,是否使用步进形式滚动
+ step: {
+ type: Boolean,
+ default: false
+ },
+ // 滚动一个周期的时间长,单位ms
+ duration: {
+ type: [String, Number],
+ default: 1500
+ },
+ // 是否禁止用手滑动切换,仅`direction="column"生效`
+ // 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序
+ disableTouch: {
+ type: Boolean,
+ default: true
+ },
+ // 是否禁止滚动,仅`direction="column"生效`
+ disableScroll: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.columnNotice
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue b/uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue
new file mode 100644
index 0000000..73c6bfc
--- /dev/null
+++ b/uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue
@@ -0,0 +1,176 @@
+<template>
+ <view
+ class="uv-notice"
+ @tap="clickHandler"
+ >
+ <slot name="icon">
+ <view
+ class="uv-notice__left-icon"
+ v-if="icon"
+ >
+ <uv-icon
+ :name="icon"
+ :color="color"
+ size="19"
+ ></uv-icon>
+ </view>
+ </slot>
+ <swiper
+ :disable-touch="disableTouch"
+ :vertical="step ? false : true"
+ circular
+ :interval="duration"
+ :autoplay="!disableScroll"
+ class="uv-notice__swiper"
+ :style="[swiperStyle]"
+ @change="noticeChange"
+ >
+ <swiper-item
+ v-for="(item, index) in text"
+ :key="index"
+ class="uv-notice__swiper__item"
+ >
+ <text
+ class="uv-notice__swiper__item__text uv-line-1"
+ :style="[textStyle]"
+ >{{ item }}</text>
+ </swiper-item>
+ </swiper>
+ <view
+ class="uv-notice__right-icon"
+ v-if="['link', 'closable'].includes(mode)"
+ >
+ <uv-icon
+ v-if="mode === 'link'"
+ name="arrow-right"
+ :size="17"
+ :color="color"
+ ></uv-icon>
+ <uv-icon
+ v-if="mode === 'closable'"
+ name="close"
+ :size="16"
+ :color="color"
+ @click="close"
+ ></uv-icon>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * ColumnNotice 滚动通知中的垂直滚动 内部组件
+ * @description 该组件用于滚动通告场景,是其中的垂直滚动方式
+ * @tutorial https://www.uvui.cn/components/noticeBar.html
+ * @property {Array} text 显示的内容,字符串
+ * @property {String} icon 是否显示左侧的音量图标 ( 默认 'volume' )
+ * @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ * @property {String} color 文字颜色,各图标也会使用文字颜色 ( 默认 '#f9ae3d' )
+ * @property {String} bgColor 背景颜色 ( 默认 '#fdf6ec' )
+ * @property {String | Number} fontSize 字体大小,单位px ( 默认 14 )
+ * @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 ( 默认 80 )
+ * @property {Boolean} step direction = row时,是否使用步进形式滚动 ( 默认 false )
+ * @property {String | Number} duration 滚动一个周期的时间长,单位ms ( 默认 1500 )
+ * @property {Boolean} disableTouch 是否禁止用手滑动切换 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序 ( 默认 true )
+ * @example
+ */
+ export default {
+ emits: ['click','close','change'],
+ mixins: [mpMixin, mixin, props],
+ watch: {
+ text: {
+ immediate: true,
+ handler(newValue, oldValue) {
+ if(!this.$uv.test.array(newValue)) {
+ this.$uv.error('noticebar组件direction为column时,要求text参数为数组形式')
+ }
+ }
+ }
+ },
+ computed: {
+ // 文字内容的样式
+ textStyle() {
+ let style = {}
+ style.color = this.color
+ style.fontSize = this.$uv.addUnit(this.fontSize)
+ return style
+ },
+ // 垂直或者水平滚动
+ vertical() {
+ if (this.mode == 'horizontal') return false
+ else return true
+ },
+ // NVUE中的swiper在css中样式不生效
+ swiperStyle(){
+ const style = {};
+ // #ifdef APP-NVUE
+ style.flex = 1;
+ style.height = '16px';
+ // #endif
+ return style;
+ }
+ },
+ data() {
+ return {
+ index:0
+ }
+ },
+ methods: {
+ noticeChange(e){
+ this.index = e.detail.current
+ this.$emit('change', this.index);
+ },
+ // 点击通告栏
+ clickHandler() {
+ this.$emit('click', this.index)
+ },
+ // 点击关闭按钮
+ close() {
+ this.$emit('close')
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ $show-lines: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-notice {
+ @include flex;
+ align-items: center;
+ justify-content: space-between;
+
+ &__left-icon {
+ align-items: center;
+ margin-right: 5px;
+ }
+
+ &__right-icon {
+ margin-left: 5px;
+ align-items: center;
+ }
+
+ &__swiper {
+ height: 16px;
+ @include flex;
+ align-items: center;
+ flex: 1;
+
+ &__item {
+ @include flex;
+ align-items: center;
+ overflow: hidden;
+
+ &__text {
+ font-size: 14px;
+ color: $uv-warning;
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-notice-bar/components/uv-notice-bar/props.js b/uni_modules/uv-notice-bar/components/uv-notice-bar/props.js
new file mode 100644
index 0000000..3fe0b44
--- /dev/null
+++ b/uni_modules/uv-notice-bar/components/uv-notice-bar/props.js
@@ -0,0 +1,76 @@
+export default {
+ props: {
+ // 显示的内容,数组
+ text: {
+ type: [Array, String],
+ default: () => []
+ },
+ // 通告滚动模式,row-横向滚动,column-竖向滚动
+ direction: {
+ type: String,
+ default: 'row'
+ },
+ // direction = row时,是否使用步进形式滚动
+ step: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示左侧的音量图标
+ icon: {
+ type: [String, Boolean, null],
+ default: 'volume'
+ },
+ // 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ mode: {
+ type: String,
+ default: ''
+ },
+ // 文字颜色,各图标也会使用文字颜色
+ color: {
+ type: String,
+ default: '#f9ae3d'
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: '#fdf6ec'
+ },
+ // 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度
+ speed: {
+ type: [String, Number],
+ default: 80
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 滚动一个周期的时间长,单位ms
+ duration: {
+ type: [String, Number],
+ default: 2000
+ },
+ // 跳转的页面路径
+ url: {
+ type: String,
+ default: ''
+ },
+ // 页面跳转的类型
+ linkType: {
+ type: String,
+ default: 'navigateTo'
+ },
+ // 是否禁止用手滑动切换
+ // 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序
+ disableTouch: {
+ type: Boolean,
+ default: true
+ },
+ // 是否禁止滚动,仅`direction="column"生效`
+ disableScroll: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.noticeBar
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue b/uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue
new file mode 100644
index 0000000..14dd7c5
--- /dev/null
+++ b/uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue
@@ -0,0 +1,110 @@
+<template>
+ <view
+ class="uv-notice-bar"
+ v-if="show"
+ :style="[{
+ backgroundColor: bgColor
+ }, $uv.addStyle(customStyle)]"
+ >
+ <template v-if="direction === 'column' || (direction === 'row' && step)">
+ <uv-column-notice
+ :color="color"
+ :bgColor="bgColor"
+ :text="text"
+ :mode="mode"
+ :step="step"
+ :icon="icon"
+ :disable-touch="disableTouch"
+ :disable-scroll="disableScroll"
+ :fontSize="fontSize"
+ :duration="duration"
+ @close="close"
+ @click="click"
+ @change="change"
+ ></uv-column-notice>
+ </template>
+ <template v-else>
+ <uv-row-notice
+ :color="color"
+ :bgColor="bgColor"
+ :text="text"
+ :mode="mode"
+ :fontSize="fontSize"
+ :speed="speed"
+ :url="url"
+ :linkType="linkType"
+ :icon="icon"
+ @close="close"
+ @click="click"
+ ></uv-row-notice>
+ </template>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+
+ /**
+ * noticeBar 滚动通知
+ * @description 该组件用于滚动通告场景,有多种模式可供选择
+ * @tutorial https://www.uvui.cn/components/noticeBar.html
+ * @property {Array | String} text 显示的内容,数组
+ * @property {String} direction 通告滚动模式,row-横向滚动,column-竖向滚动 ( 默认 'row' )
+ * @property {Boolean} step direction = row时,是否使用步进形式滚动 ( 默认 false )
+ * @property {String} icon 是否显示左侧的音量图标 ( 默认 'volume' )
+ * @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ * @property {String} color 文字颜色,各图标也会使用文字颜色 ( 默认 '#f9ae3d' )
+ * @property {String} bgColor 背景颜色 ( 默认 '#fdf6ec' )
+ * @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(px),这有利于控制文字无论多少时,都能有一个恒定的速度 ( 默认 80 )
+ * @property {String | Number} fontSize 字体大小 ( 默认 14 )
+ * @property {String | Number} duration 滚动一个周期的时间长,单位ms ( 默认 2000 )
+ * @property {Boolean} disableTouch 是否禁止用手滑动切换 目前HX2.6.11,只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、字节跳动小程序(默认34) ( 默认 true )
+ * @property {String} url 跳转的页面路径
+ * @property {String} linkType 页面跳转的类型 ( 默认 navigateTo )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} click 点击通告文字触发
+ * @event {Function} close 点击右侧关闭图标触发
+ * @example <uv-notice-bar :more-icon="true" :list="list"></uv-notice-bar>
+ */
+ export default {
+ name: "uv-notice-bar",
+ emits: ['click','close','change'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ show: true
+ }
+ },
+ methods: {
+ // 点击通告栏
+ click(index) {
+ this.$emit('click', index)
+ if (this.url && this.linkType) {
+ // 此方法写在mixin中,另外跳转的url和linkType参数也在mixin的props中
+ this.openPage()
+ }
+ },
+ // 点击关闭按钮
+ close() {
+ this.show = false
+ this.$emit('close')
+ },
+ // 竖向滚动时触发
+ change(index){
+ this.$emit('change',index)
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-notice-bar {
+ overflow: hidden;
+ padding: 9px 12px;
+ flex: 1;
+ }
+</style>
diff --git a/uni_modules/uv-notice-bar/components/uv-row-notice/props.js b/uni_modules/uv-notice-bar/components/uv-row-notice/props.js
new file mode 100644
index 0000000..cba1823
--- /dev/null
+++ b/uni_modules/uv-notice-bar/components/uv-row-notice/props.js
@@ -0,0 +1,40 @@
+export default {
+ props: {
+ // 显示的内容,字符串
+ text: {
+ type: String,
+ default: ''
+ },
+ // 是否显示左侧的音量图标
+ icon: {
+ type: [String, Boolean, null],
+ default: 'volume'
+ },
+ // 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ mode: {
+ type: String,
+ default: ''
+ },
+ // 文字颜色,各图标也会使用文字颜色
+ color: {
+ type: String,
+ default: '#f9ae3d'
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: '#fdf6ec'
+ },
+ // 字体大小,单位px
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度
+ speed: {
+ type: [String, Number],
+ default: 80
+ },
+ ...uni.$uv?.props?.rowNotice
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue b/uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue
new file mode 100644
index 0000000..16117dd
--- /dev/null
+++ b/uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue
@@ -0,0 +1,341 @@
+<template>
+ <view
+ class="uv-notice"
+ @tap="clickHandler"
+ >
+ <slot name="icon">
+ <view
+ class="uv-notice__left-icon"
+ v-if="icon"
+ >
+ <uv-icon
+ :name="icon"
+ :color="color"
+ size="19"
+ ></uv-icon>
+ </view>
+ </slot>
+ <view
+ class="uv-notice__content"
+ ref="uv-notice__content"
+ >
+ <view
+ ref="uv-notice__content__text"
+ class="uv-notice__content__text"
+ :style="[animationStyle]"
+ >
+ <text
+ v-for="(item, index) in innerText"
+ :key="index"
+ :style="[textStyle]"
+ >{{item}}</text>
+ </view>
+ </view>
+ <view
+ class="uv-notice__right-icon"
+ v-if="['link', 'closable'].includes(mode)"
+ >
+ <uv-icon
+ v-if="mode === 'link'"
+ name="arrow-right"
+ :size="17"
+ :color="color"
+ ></uv-icon>
+ <uv-icon
+ v-if="mode === 'closable'"
+ @click="close"
+ name="close"
+ :size="16"
+ :color="color"
+ ></uv-icon>
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const animation = uni.requireNativePlugin('animation')
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * RowNotice 滚动通知中的水平滚动模式
+ * @description 水平滚动
+ * @tutorial https://www.uvui.cn/components/noticeBar.html
+ * @property {String | Number} text 显示的内容,字符串
+ * @property {String} icon 是否显示左侧的音量图标 (默认 'volume' )
+ * @property {String} mode 通告模式,link-显示右箭头,closable-显示右侧关闭图标
+ * @property {String} color 文字颜色,各图标也会使用文字颜色 (默认 '#f9ae3d' )
+ * @property {String} bgColor 背景颜色 (默认 ''#fdf6ec' )
+ * @property {String | Number} fontSize 字体大小,单位px (默认 14 )
+ * @property {String | Number} speed 水平滚动时的滚动速度,即每秒滚动多少px(rpx),这有利于控制文字无论多少时,都能有一个恒定的速度 (默认 80 )
+ *
+ * @event {Function} click 点击通告文字触发
+ * @event {Function} close 点击右侧关闭图标触发
+ * @example
+ */
+ export default {
+ name: 'uv-row-notice',
+ emits: ['click','close'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ animationDuration: '0', // 动画执行时间
+ animationPlayState: 'paused', // 动画的开始和结束执行
+ // nvue下,内容发生变化,导致滚动宽度也变化,需要标志为是否需要重新计算宽度
+ // 不能在内容变化时直接重新计算,因为nvue的animation模块上一次的滚动不是刚好结束,会有影响
+ nvueInit: true,
+ show: true
+ };
+ },
+ watch: {
+ text: {
+ immediate: true,
+ handler(newValue, oldValue) {
+ // #ifdef APP-NVUE
+ this.nvueInit = true
+ // #endif
+ // #ifndef APP-NVUE
+ this.vue()
+ // #endif
+
+ if(!this.$uv.test.string(newValue)) {
+ this.$uv.error('noticebar组件direction为row时,要求text参数为字符串形式')
+ }
+ }
+ },
+ fontSize() {
+ // #ifdef APP-NVUE
+ this.nvueInit = true
+ // #endif
+ // #ifndef APP-NVUE
+ this.vue()
+ // #endif
+ },
+ speed() {
+ // #ifdef APP-NVUE
+ this.nvueInit = true
+ // #endif
+ // #ifndef APP-NVUE
+ this.vue()
+ // #endif
+ }
+ },
+ computed: {
+ // 文字内容的样式
+ textStyle() {
+ let style = {}
+ style.color = this.color
+ style.fontSize = this.$uv.addUnit(this.fontSize)
+ return style
+ },
+ animationStyle() {
+ let style = {}
+ style.animationDuration = this.animationDuration
+ style.animationPlayState = this.animationPlayState
+ return style
+ },
+ // 内部对用户传入的数据进一步分割,放到多个text标签循环,否则如果用户传入的字符串很长(100个字符以上)
+ // 放在一个text标签中进行滚动,在低端安卓机上,动画可能会出现抖动现象,需要分割到多个text中可解决此问题
+ innerText() {
+ let result = [],
+ // 每组text标签的字符长度
+ len = 20
+ const textArr = this.text? this.text.split(''):[]
+ for (let i = 0; i < textArr.length; i += len) {
+ // 对拆分的后的text进行slice分割,得到的为数组再进行join拼接为字符串
+ result.push(textArr.slice(i, i + len).join(''))
+ }
+ return result
+ }
+ },
+ mounted() {
+ // #ifdef APP-PLUS
+ // 在APP上(含nvue),监听当前webview是否处于隐藏状态(进入下一页时即为hide状态)
+ // 如果webivew隐藏了,为了节省性能的损耗,应停止动画的执行,同时也是为了保持进入下一页返回后,滚动位置保持不变
+ var pages = getCurrentPages()
+ var page = pages[pages.length - 1]
+ var currentWebview = page.$getAppWebview()
+ currentWebview.addEventListener('hide', () => {
+ this.webviewHide = true
+ })
+ currentWebview.addEventListener('show', () => {
+ this.webviewHide = false
+ })
+ // #endif
+
+ this.init()
+ },
+ methods: {
+ init() {
+ // #ifdef APP-NVUE
+ this.nvue()
+ // #endif
+
+ // #ifndef APP-NVUE
+ this.vue()
+ // #endif
+
+ if(!this.$uv.test.string(this.text)) {
+ this.$uv.error('noticebar组件direction为row时,要求text参数为字符串形式')
+ }
+ },
+ // vue版处理
+ async vue() {
+ // #ifndef APP-NVUE
+ let boxWidth = 0,
+ textWidth = 0
+ // 进行一定的延时
+ await this.$uv.sleep()
+ // 查询盒子和文字的宽度
+ textWidth = (await this.$uvGetRect('.uv-notice__content__text')).width
+ boxWidth = (await this.$uvGetRect('.uv-notice__content')).width
+ // 根据t=s/v(时间=路程/速度),这里为何不需要加上#uv-notice-box的宽度,因为中设置了.uv-notice-content样式中设置了padding-left: 100%
+ // 恰巧计算出来的结果中已经包含了#uv-notice-box的宽度
+ this.animationDuration = `${textWidth / this.$uv.getPx(this.speed)}s`
+ // 这里必须这样开始动画,否则在APP上动画速度不会改变
+ this.animationPlayState = 'paused'
+ setTimeout(() => {
+ this.animationPlayState = 'running'
+ }, 10)
+ // #endif
+ },
+ // nvue版处理
+ async nvue() {
+ // #ifdef APP-NVUE
+ this.nvueInit = false
+ let boxWidth = 0,
+ textWidth = 0
+ // 进行一定的延时
+ await this.$uv.sleep()
+ // 查询盒子和文字的宽度
+ textWidth = (await this.getNvueRect('uv-notice__content__text')).width
+ boxWidth = (await this.getNvueRect('uv-notice__content')).width
+ // 将文字移动到盒子的右边沿,之所以需要这么做,是因为nvue不支持100%单位,否则可以通过css设置
+ animation.transition(this.$refs['uv-notice__content__text'], {
+ styles: {
+ transform: `translateX(${boxWidth}px)`
+ },
+ }, () => {
+ // 如果非禁止动画,则开始滚动
+ !this.stopAnimation && this.loopAnimation(textWidth, boxWidth)
+ });
+ // #endif
+ },
+ loopAnimation(textWidth, boxWidth) {
+ // #ifdef APP-NVUE
+ animation.transition(this.$refs['uv-notice__content__text'], {
+ styles: {
+ // 目标移动终点为-textWidth,也即当文字的最右边贴到盒子的左边框的位置
+ transform: `translateX(-${textWidth}px)`
+ },
+ // 滚动时间的计算为,时间 = 路程(boxWidth + textWidth) / 速度,最后转为毫秒
+ duration: (boxWidth + textWidth) / this.$uv.getPx(this.speed) * 1000,
+ delay: 10
+ }, () => {
+ animation.transition(this.$refs['uv-notice__content__text'], {
+ styles: {
+ // 重新将文字移动到盒子的右边沿
+ transform: `translateX(${this.stopAnimation ? 0 : boxWidth}px)`
+ },
+ }, () => {
+ // 如果非禁止动画,则继续下一轮滚动
+ if (!this.stopAnimation) {
+ // 判断是否需要初始化计算尺寸
+ if (this.nvueInit) {
+ this.nvue()
+ } else {
+ this.loopAnimation(textWidth, boxWidth)
+ }
+ }
+ });
+ })
+ // #endif
+ },
+ getNvueRect(el) {
+ // #ifdef APP-NVUE
+ // 返回一个promise
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs[el], (res) => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ // 点击通告栏
+ clickHandler(index) {
+ this.$emit('click')
+ },
+ // 点击右侧按钮,需要判断点击的是关闭图标还是箭头图标
+ close() {
+ this.$emit('close')
+ }
+ },
+ // #ifdef APP-NVUE
+ // #ifdef VUE2
+ beforeDestroy() {
+ this.stopAnimation = true
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ this.stopAnimation = true
+ }
+ // #endif
+ // #endif
+ };
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-notice {
+ @include flex;
+ align-items: center;
+ justify-content: space-between;
+
+ &__left-icon {
+ align-items: center;
+ margin-right: 5px;
+ }
+
+ &__right-icon {
+ margin-left: 5px;
+ align-items: center;
+ }
+
+ &__content {
+ text-align: right;
+ flex: 1;
+ @include flex;
+ flex-wrap: nowrap;
+ overflow: hidden;
+
+ &__text {
+ font-size: 14px;
+ color: $uv-warning;
+ /* #ifndef APP-NVUE */
+ // 这一句很重要,为了能让滚动左右连接起来
+ padding-left: 100%;
+ word-break: keep-all;
+ white-space: nowrap;
+ animation: uv-loop-animation 10s linear infinite both;
+ /* #endif */
+ @include flex(row);
+ }
+ }
+
+ }
+ /* #ifndef APP-NVUE */
+ @keyframes uv-loop-animation {
+ 0% {
+ transform: translate3d(0, 0, 0);
+ }
+
+ 100% {
+ transform: translate3d(-100%, 0, 0);
+ }
+ }
+ /* #endif */
+</style>
diff --git a/uni_modules/uv-notice-bar/package.json b/uni_modules/uv-notice-bar/package.json
new file mode 100644
index 0000000..f1bca29
--- /dev/null
+++ b/uni_modules/uv-notice-bar/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-notice-bar",
+ "displayName": "uv-notice-bar 滚动通知 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.7",
+ "description": "uv-notice-bar 该组件用于滚动通告场景,有多种模式可供选择。",
+ "keywords": [
+ "uv-notice-bar",
+ "uvui",
+ "uv-ui",
+ "notice",
+ "滚动公告"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-notice-bar/readme.md b/uni_modules/uv-notice-bar/readme.md
new file mode 100644
index 0000000..61eb131
--- /dev/null
+++ b/uni_modules/uv-notice-bar/readme.md
@@ -0,0 +1,19 @@
+## NoticeBar 滚动通知
+
+> **组件名:uv-notice-bar**
+
+该组件用于滚动通告场景,有多种模式可供选择。灵活配置,开箱即用。
+
+# <a href="https://www.uvui.cn/components/noticeBar.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small style="font-size:14px;font-weight:700;">(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-notify/changelog.md b/uni_modules/uv-notify/changelog.md
new file mode 100644
index 0000000..5d818ba
--- /dev/null
+++ b/uni_modules/uv-notify/changelog.md
@@ -0,0 +1,9 @@
+## 1.0.3(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.2(2023-07-02)
+uv-notify 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/notify.html
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-notify 消息提示
diff --git a/uni_modules/uv-notify/components/uv-notify/props.js b/uni_modules/uv-notify/components/uv-notify/props.js
new file mode 100644
index 0000000..5728276
--- /dev/null
+++ b/uni_modules/uv-notify/components/uv-notify/props.js
@@ -0,0 +1,45 @@
+export default {
+ props: {
+ // 到顶部的距离
+ top: {
+ type: [String, Number],
+ default: 0
+ },
+ // type主题,primary,success,warning,error
+ type: {
+ type: String,
+ default: 'primary'
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: '#ffffff'
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: ''
+ },
+ // 展示的文字内容
+ message: {
+ type: String,
+ default: ''
+ },
+ // 展示时长,为0时不消失,单位ms
+ duration: {
+ type: [String, Number],
+ default: 3000
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 15
+ },
+ // 是否留出顶部安全距离(状态栏高度)
+ safeAreaInsetTop: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.notify
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-notify/components/uv-notify/uv-notify.vue b/uni_modules/uv-notify/components/uv-notify/uv-notify.vue
new file mode 100644
index 0000000..96c1566
--- /dev/null
+++ b/uni_modules/uv-notify/components/uv-notify/uv-notify.vue
@@ -0,0 +1,220 @@
+<template>
+ <uv-transition
+ mode="slide-top"
+ :customStyle="containerStyle"
+ :show="open"
+ >
+ <view
+ class="uv-notify"
+ :class="[`uv-notify--${tmpConfig.type}`]"
+ :style="[backgroundColor, $uv.addStyle(customStyle)]"
+ >
+ <uv-status-bar v-if="tmpConfig.safeAreaInsetTop"></uv-status-bar>
+ <view class="uv-notify__warpper">
+ <slot name="icon">
+ <uv-icon
+ v-if="['success', 'warning', 'error'].includes(tmpConfig.type)"
+ :name="tmpConfig.icon"
+ :color="tmpConfig.color"
+ :size="1.3 * tmpConfig.fontSize"
+ :customStyle="{marginRight: '4px'}"
+ ></uv-icon>
+ </slot>
+ <text
+ class="uv-notify__warpper__text"
+ :style="{
+ fontSize: $uv.addUnit(tmpConfig.fontSize),
+ color: tmpConfig.color
+ }"
+ >{{ tmpConfig.message }}</text>
+ </view>
+ </view>
+ </uv-transition>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * notify 顶部提示
+ * @description 该组件一般用于页面顶部向下滑出一个提示,尔后自动收起的场景
+ * @tutorial
+ * @property {String | Number} top 到顶部的距离 ( 默认 0 )
+ * @property {String} type 主题,primary,success,warning,error ( 默认 'primary' )
+ * @property {String} color 字体颜色 ( 默认 '#ffffff' )
+ * @property {String} bgColor 背景颜色
+ * @property {String} message 展示的文字内容
+ * @property {String | Number} duration 展示时长,为0时不消失,单位ms ( 默认 3000 )
+ * @property {String | Number} fontSize 字体大小 ( 默认 15 )
+ * @property {Boolean} safeAreaInsetTop 是否留出顶部安全距离(状态栏高度) ( 默认 false )
+ * @property {Object} customStyle 组件的样式,对象形式
+ * @event {Function} open 开启组件时调用的函数
+ * @event {Function} close 关闭组件式调用的函数
+ * @example <uv-notify message="Hi uvui"></uv-notify>
+ */
+ export default {
+ name: 'uv-notify',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 是否展示组件
+ open: false,
+ timer: null,
+ config: {
+ // 到顶部的距离
+ top: this.top,
+ // type主题,primary,success,warning,error
+ type: this.type,
+ // 字体颜色
+ color: this.color,
+ // 背景颜色
+ bgColor: this.bgColor,
+ // 展示的文字内容
+ message: this.message,
+ // 展示时长,为0时不消失,单位ms
+ duration: this.duration,
+ // 字体大小
+ fontSize: this.fontSize,
+ // 是否留出顶部安全距离(状态栏高度)
+ safeAreaInsetTop: this.safeAreaInsetTop
+ },
+ // 合并后的配置,避免多次调用组件后,可能会复用之前使用的配置参数
+ tmpConfig: {}
+ }
+ },
+ computed: {
+ containerStyle() {
+ let top = 0
+ if (this.tmpConfig.top === 0) {
+ // #ifdef H5
+ // H5端,导航栏为普通元素,需要将组件移动到导航栏的下边沿
+ // H5的导航栏高度为44px
+ top = 44
+ // #endif
+ }
+ const style = {
+ top: this.$uv.addUnit(this.tmpConfig.top === 0 ? top : this.tmpConfig.top),
+ // 因为组件底层为uv-transition组件,必须将其设置为fixed定位
+ // 让其出现在导航栏底部
+ position: 'fixed',
+ left: 0,
+ right: 0,
+ zIndex: 10076
+ }
+ return style
+ },
+ // 组件背景颜色
+ backgroundColor() {
+ const style = {}
+ if (this.tmpConfig.bgColor) {
+ style.backgroundColor = this.tmpConfig.bgColor
+ }
+ return style
+ },
+ // 默认主题下的图标
+ icon() {
+ let icon
+ if (this.tmpConfig.type === 'success') {
+ icon = 'checkmark-circle'
+ } else if (this.tmpConfig.type === 'error') {
+ icon = 'close-circle'
+ } else if (this.tmpConfig.type === 'warning') {
+ icon = 'error-circle'
+ }
+ return icon
+ }
+ },
+ created() {
+ // 通过主题的形式调用toast,批量生成方法函数
+ ['primary', 'success', 'error', 'warning'].map(item => {
+ this[item] = message => this.show({
+ type: item,
+ message
+ })
+ })
+ },
+ methods: {
+ show(options) {
+ // 不将结果合并到this.config变量,避免多次调用uv-toast,前后的配置造成混乱
+ this.tmpConfig = this.$uv.deepMerge(this.config, options)
+ // 任何定时器初始化之前,都要执行清除操作,否则可能会造成混乱
+ this.clearTimer()
+ this.open = true
+ if (this.tmpConfig.duration > 0) {
+ this.timer = setTimeout(() => {
+ this.open = false
+ // 倒计时结束,清除定时器,隐藏toast组件
+ this.clearTimer()
+ // 判断是否存在callback方法,如果存在就执行
+ typeof(this.tmpConfig.complete) === 'function' && this.tmpConfig.complete()
+ }, this.tmpConfig.duration)
+ }
+ },
+ // 关闭notify
+ close() {
+ this.clearTimer()
+ },
+ clearTimer() {
+ this.open = false
+ // 清除定时器
+ clearTimeout(this.timer)
+ this.timer = null
+ }
+ },
+ // #ifdef VUE2
+ beforeDestroy() {
+ this.clearTimer()
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ this.clearTimer()
+ }
+ // #endif
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-notify-padding: 8px 10px !default;
+ $uv-notify-text-font-size: 15px !default;
+ $uv-notify-primary-bgColor: $uv-primary !default;
+ $uv-notify-success-bgColor: $uv-success !default;
+ $uv-notify-error-bgColor: $uv-error !default;
+ $uv-notify-warning-bgColor: $uv-warning !default;
+
+
+ .uv-notify {
+ padding: $uv-notify-padding;
+
+ &__warpper {
+ @include flex;
+ align-items: center;
+ text-align: center;
+ justify-content: center;
+
+ &__text {
+ font-size: $uv-notify-text-font-size;
+ text-align: center;
+ }
+ }
+
+ &--primary {
+ background-color: $uv-notify-primary-bgColor;
+ }
+
+ &--success {
+ background-color: $uv-notify-success-bgColor;
+ }
+
+ &--error {
+ background-color: $uv-notify-error-bgColor;
+ }
+
+ &--warning {
+ background-color: $uv-notify-warning-bgColor;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-notify/package.json b/uni_modules/uv-notify/package.json
new file mode 100644
index 0000000..921949b
--- /dev/null
+++ b/uni_modules/uv-notify/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-notify",
+ "displayName": "uv-notify 消息提示 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "uv-notify 该组件一般用于页面顶部向下滑出一个提示,后自动收起的场景。",
+ "keywords": [
+ "uv-notify",
+ "uvui",
+ "uv-ui",
+ "notify",
+ "消息提示"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-status-bar",
+ "uv-overlay",
+ "uv-transition"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-notify/readme.md b/uni_modules/uv-notify/readme.md
new file mode 100644
index 0000000..482344a
--- /dev/null
+++ b/uni_modules/uv-notify/readme.md
@@ -0,0 +1,11 @@
+## Notify 消息提示
+
+> **组件名:uv-notify**
+
+该组件一般用于页面顶部向下滑出一个提示,后自动收起的场景。
+
+### <a href="https://www.uvui.cn/components/notify.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-number-box/changelog.md b/uni_modules/uv-number-box/changelog.md
new file mode 100644
index 0000000..3f07995
--- /dev/null
+++ b/uni_modules/uv-number-box/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-07-13)
+1. 修复 uv-number-box设置value属性不生效的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-number-box 步进器
diff --git a/uni_modules/uv-number-box/components/uv-number-box/props.js b/uni_modules/uv-number-box/components/uv-number-box/props.js
new file mode 100644
index 0000000..64b633b
--- /dev/null
+++ b/uni_modules/uv-number-box/components/uv-number-box/props.js
@@ -0,0 +1,113 @@
+export default {
+ props: {
+ value: {
+ type: [String, Number],
+ default: 0
+ },
+ modelValue: {
+ type: [String, Number],
+ default: 0
+ },
+ // 步进器标识符,在change回调返回
+ name: {
+ type: [String, Number],
+ default: ''
+ },
+ // 最小值
+ min: {
+ type: [String, Number],
+ default: 1
+ },
+ // 最大值
+ max: {
+ type: [String, Number],
+ default: Number.MAX_SAFE_INTEGER
+ },
+ // 加减的步长,可为小数
+ step: {
+ type: [String, Number],
+ default: 1
+ },
+ // 是否只允许输入整数
+ integer: {
+ type: Boolean,
+ default: false
+ },
+ // 是否禁用,包括输入框,加减按钮
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否禁用输入框
+ disabledInput: {
+ type: Boolean,
+ default: false
+ },
+ // 是否开启异步变更,开启后需要手动控制输入值
+ asyncChange: {
+ type: Boolean,
+ default: false
+ },
+ // 输入框宽度,单位为px
+ inputWidth: {
+ type: [String, Number],
+ default: 35
+ },
+ // 是否显示减少按钮
+ showMinus: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示增加按钮
+ showPlus: {
+ type: Boolean,
+ default: true
+ },
+ // 显示的小数位数
+ decimalLength: {
+ type: [String, Number, null],
+ default: null
+ },
+ // 是否开启长按加减手势
+ longPress: {
+ type: Boolean,
+ default: true
+ },
+ // 输入框文字和加减按钮图标的颜色
+ color: {
+ type: String,
+ default: '#323233'
+ },
+ // 按钮大小,宽高等于此值,单位px,输入框高度和此值保持一致
+ buttonSize: {
+ type: [String, Number],
+ default: 30
+ },
+ // 输入框和按钮的背景颜色
+ bgColor: {
+ type: String,
+ default: '#EBECEE'
+ },
+ // 指定光标于键盘的距离,避免键盘遮挡输入框,单位px
+ cursorSpacing: {
+ type: [String, Number],
+ default: 100
+ },
+ // 是否禁用增加按钮
+ disablePlus: {
+ type: Boolean,
+ default: false
+ },
+ // 是否禁用减少按钮
+ disableMinus: {
+ type: Boolean,
+ default: false
+ },
+ // 加减按钮图标的样式
+ iconStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ ...uni.$uv?.props?.numberBox
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue b/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue
new file mode 100644
index 0000000..6937e89
--- /dev/null
+++ b/uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue
@@ -0,0 +1,395 @@
+<template>
+ <view class="uv-number-box">
+ <view
+ class="uv-number-box__slot"
+ @tap.stop="clickHandler('minus')"
+ @touchstart="onTouchStart('minus')"
+ @touchend.stop="clearTimeout"
+ v-if="showMinus && $slots.minus"
+ >
+ <slot name="minus" />
+ </view>
+ <view
+ v-else-if="showMinus"
+ class="uv-number-box__minus"
+ @tap.stop="clickHandler('minus')"
+ @touchstart="onTouchStart('minus')"
+ @touchend.stop="clearTimeout"
+ hover-class="uv-number-box__minus--hover"
+ hover-stay-time="150"
+ :class="{ 'uv-number-box__minus--disabled': isDisabled('minus') }"
+ :style="[buttonStyle('minus')]"
+ >
+ <uv-icon
+ name="minus"
+ :color="isDisabled('minus') ? '#c8c9cc' : '#323233'"
+ size="15"
+ bold
+ :customStyle="iconStyle"
+ ></uv-icon>
+ </view>
+ <slot name="input">
+ <input
+ :disabled="disabledInput || disabled"
+ :cursor-spacing="getCursorSpacing"
+ :class="{ 'uv-number-box__input--disabled': disabled || disabledInput }"
+ v-model="currentValue"
+ class="uv-number-box__input"
+ @blur="onBlur"
+ @focus="onFocus"
+ @input="onInput"
+ type="number"
+ :style="[inputStyle]"
+ />
+ </slot>
+ <view
+ class="uv-number-box__slot"
+ @tap.stop="clickHandler('plus')"
+ @touchstart="onTouchStart('plus')"
+ @touchend.stop="clearTimeout"
+ v-if="showPlus && $slots.plus"
+ >
+ <slot name="plus" />
+ </view>
+ <view
+ v-else-if="showPlus"
+ class="uv-number-box__plus"
+ @tap.stop="clickHandler('plus')"
+ @touchstart="onTouchStart('plus')"
+ @touchend.stop="clearTimeout"
+ hover-class="uv-number-box__plus--hover"
+ hover-stay-time="150"
+ :class="{ 'uv-number-box__minus--disabled': isDisabled('plus') }"
+ :style="[buttonStyle('plus')]"
+ >
+ <uv-icon
+ name="plus"
+ :color="isDisabled('plus') ? '#c8c9cc' : '#323233'"
+ size="15"
+ bold
+ :customStyle="iconStyle"
+ ></uv-icon>
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * numberBox 步进器
+ * @description 该组件一般用于商城购物选择物品数量的场景。
+ * @tutorial https://www.uvui.cn/components/numberBox.html
+ * @property {String | Number} value / v-model 用于双向绑定的值,初始化时设置设为默认min值(最小值) (默认 0 )
+ * @property {String | Number} name 步进器标识符,在change回调返回
+ * @property {String | Number} min 最小值 (默认 1 )
+ * @property {String | Number} max 最大值 (默认 Number.MAX_SAFE_INTEGER )
+ * @property {String | Number} step 加减的步长,可为小数 (默认 1 )
+ * @property {Boolean} integer 是否只允许输入整数 (默认 false )
+ * @property {Boolean} disabled 是否禁用,包括输入框,加减按钮 (默认 false )
+ * @property {Boolean} disabledInput 是否禁用输入框 (默认 false )
+ * @property {Boolean} asyncChange 是否开启异步变更,开启后需要手动控制输入值 (默认 false )
+ * @property {String | Number} inputWidth 输入框宽度,单位为px (默认 35 )
+ * @property {Boolean} showMinus 是否显示减少按钮 (默认 true )
+ * @property {Boolean} showPlus 是否显示增加按钮 (默认 true )
+ * @property {String | Number} decimalLength 显示的小数位数
+ * @property {Boolean} longPress 是否开启长按加减手势 (默认 true )
+ * @property {String} color 输入框文字和加减按钮图标的颜色 (默认 '#323233' )
+ * @property {String | Number} buttonSize 按钮大小,宽高等于此值,单位px,输入框高度和此值保持一致 (默认 30 )
+ * @property {String} bgColor 输入框和按钮的背景颜色 (默认 '#EBECEE' )
+ * @property {String | Number} cursorSpacing 指定光标于键盘的距离,避免键盘遮挡输入框,单位px (默认 100 )
+ * @property {Boolean} disablePlus 是否禁用增加按钮 (默认 false )
+ * @property {Boolean} disableMinus 是否禁用减少按钮 (默认 false )
+ * @property {Object | String} iconStyle 加减按钮图标的样式
+ * @event {Function} onFocus 输入框活动焦点
+ * @event {Function} onBlur 输入框失去焦点
+ * @event {Function} onInput 输入框值发生变化
+ * @event {Function} onChange
+ * @example <uv-number-box v-model="value" @change="valChange"></uv-number-box>
+ */
+ export default {
+ name: 'uv-number-box',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 输入框实际操作的值
+ currentValue: '',
+ // 定时器
+ longPressTimer: null
+ }
+ },
+ watch: {
+ // 多个值之间,只要一个值发生变化,都要重新检查check()函数
+ watchChange(n) {
+ this.check()
+ },
+ value(newVal) {
+ if (newVal !== this.currentValue) {
+ this.currentValue = this.format(this.value)
+ }
+ },
+ modelValue(newVal) {
+ if (newVal !== this.currentValue) {
+ this.currentValue = this.format(this.modelValue)
+ }
+ }
+ },
+ computed: {
+ getCursorSpacing() {
+ // 判断传入的单位,如果为px单位,需要转成px
+ return this.$uv.getPx(this.cursorSpacing)
+ },
+ // 按钮的样式
+ buttonStyle() {
+ return (type) => {
+ const style = {
+ backgroundColor: this.bgColor,
+ height: this.$uv.addUnit(this.buttonSize),
+ color: this.color
+ }
+ if (this.isDisabled(type)) {
+ style.backgroundColor = '#f7f8fa'
+ }
+ return style
+ }
+ },
+ // 输入框的样式
+ inputStyle() {
+ const disabled = this.disabled || this.disabledInput
+ const style = {
+ color: this.color,
+ backgroundColor: this.bgColor,
+ height: this.$uv.addUnit(this.buttonSize),
+ width: this.$uv.addUnit(this.inputWidth)
+ }
+ return style
+ },
+ // 用于监听多个值发生变化
+ watchChange() {
+ return [this.integer, this.decimalLength, this.min, this.max]
+ },
+ isDisabled() {
+ return (type) => {
+ if (type === 'plus') {
+ // 在点击增加按钮情况下,判断整体的disabled,是否单独禁用增加按钮,以及当前值是否大于最大的允许值
+ return (
+ this.disabled ||
+ this.disablePlus ||
+ this.currentValue >= this.max
+ )
+ }
+ // 点击减少按钮同理
+ return (
+ this.disabled ||
+ this.disableMinus ||
+ this.currentValue <= this.min
+ )
+ }
+ },
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ const value = this.value || this.modelValue;
+ this.currentValue = this.format(value)
+ },
+ // 格式化整理数据,限制范围
+ format(value) {
+ value = this.filter(value)
+ // 如果为空字符串,那么设置为0,同时将值转为Number类型
+ value = value === '' ? 0 : +value
+ // 对比最大最小值,取在min和max之间的值
+ value = Math.max(Math.min(this.max, value), this.min)
+ // 如果设定了最大的小数位数,使用toFixed去进行格式化
+ if (this.decimalLength !== null) {
+ value = value.toFixed(this.decimalLength)
+ }
+ return value
+ },
+ // 过滤非法的字符
+ filter(value) {
+ // 只允许0-9之间的数字,"."为小数点,"-"为负数时候使用
+ value = String(value).replace(/[^0-9.-]/g, '')
+ // 如果只允许输入整数,则过滤掉小数点后的部分
+ if (this.integer && value.indexOf('.') !== -1) {
+ value = value.split('.')[0]
+ }
+ return value;
+ },
+ check() {
+ // 格式化了之后,如果前后的值不相等,那么设置为格式化后的值
+ const val = this.format(this.currentValue);
+ if (val !== this.currentValue) {
+ this.currentValue = val
+ }
+ },
+ // 输入框活动焦点
+ onFocus(event) {
+ this.$emit('focus', {
+ ...event.detail,
+ name: this.name,
+ })
+ },
+ // 输入框失去焦点
+ onBlur(event) {
+ // 对输入值进行格式化
+ const value = this.format(event.detail.value)
+ // 发出blur事件
+ this.$emit(
+ 'blur', {
+ ...event.detail,
+ name: this.name,
+ }
+ )
+ },
+ // 输入框值发生变化
+ onInput(e) {
+ const {
+ value = ''
+ } = e.detail || {}
+ // 为空返回
+ if (value === '') return
+ let formatted = this.filter(value)
+ // 最大允许的小数长度
+ if (this.decimalLength !== null && formatted.indexOf('.') !== -1) {
+ const pair = formatted.split('.');
+ formatted = `${pair[0]}.${pair[1].slice(0, this.decimalLength)}`
+ }
+ formatted = this.format(formatted)
+ this.emitChange(formatted);
+ },
+ // 发出change事件
+ emitChange(value) {
+ // 如果开启了异步变更值,则不修改内部的值,需要用户手动在外部通过v-model变更
+ if (!this.asyncChange) {
+ this.$nextTick(() => {
+ this.$emit('input', value)
+ this.$emit('update:modelValue', value)
+ this.currentValue = value
+ this.$forceUpdate()
+ })
+ }
+ this.$emit('change', {
+ value,
+ name: this.name,
+ });
+ },
+ onChange() {
+ const {
+ type
+ } = this
+ if (this.isDisabled(type)) {
+ return this.$emit('overlimit', type)
+ }
+ const diff = type === 'minus' ? -this.step : +this.step
+ const value = this.format(this.add(+this.currentValue, diff))
+ this.emitChange(value)
+ this.$emit(type)
+ },
+ // 对值扩大后进行四舍五入,再除以扩大因子,避免出现浮点数操作的精度问题
+ add(num1, num2) {
+ const cardinal = Math.pow(10, 10);
+ return Math.round((num1 + num2) * cardinal) / cardinal
+ },
+ // 点击加减按钮
+ clickHandler(type) {
+ this.type = type
+ this.onChange()
+ },
+ longPressStep() {
+ // 每隔一段时间,重新调用longPressStep方法,实现长按加减
+ this.clearTimeout()
+ this.longPressTimer = setTimeout(() => {
+ this.onChange()
+ this.longPressStep()
+ }, 250);
+ },
+ onTouchStart(type) {
+ if (!this.longPress) return
+ this.clearTimeout()
+ this.type = type
+ // 一定时间后,默认达到长按状态
+ this.longPressTimer = setTimeout(() => {
+ this.onChange()
+ this.longPressStep()
+ }, 600)
+ },
+ // 触摸结束,清除定时器,停止长按加减
+ onTouchEnd() {
+ if (!this.longPress) return
+ this.clearTimeout()
+ },
+ // 清除定时器
+ clearTimeout() {
+ clearTimeout(this.longPressTimer)
+ this.longPressTimer = null
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-numberBox-hover-bgColor: #E6E6E6 !default;
+ $uv-numberBox-disabled-color: #c8c9cc !default;
+ $uv-numberBox-disabled-bgColor: #f7f8fa !default;
+ $uv-numberBox-plus-radius: 4px !default;
+ $uv-numberBox-minus-radius: 4px !default;
+ $uv-numberBox-input-text-align: center !default;
+ $uv-numberBox-input-font-size: 15px !default;
+ $uv-numberBox-input-padding: 0 !default;
+ $uv-numberBox-input-margin: 0 2px !default;
+ $uv-numberBox-input-disabled-color: #c8c9cc !default;
+ $uv-numberBox-input-disabled-bgColor: #f2f3f5 !default;
+ .uv-number-box {
+ @include flex(row);
+ align-items: center;
+ &__slot {
+ /* #ifndef APP-NVUE */
+ touch-action: none;
+ /* #endif */
+ }
+ &__plus,
+ &__minus {
+ width: 35px;
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ /* #ifndef APP-NVUE */
+ touch-action: none;
+ /* #endif */
+ &--hover {
+ background-color: $uv-numberBox-hover-bgColor !important;
+ }
+ &--disabled {
+ color: $uv-numberBox-disabled-color;
+ background-color: $uv-numberBox-disabled-bgColor;
+ }
+ }
+ &__plus {
+ border-top-right-radius: $uv-numberBox-plus-radius;
+ border-bottom-right-radius: $uv-numberBox-plus-radius;
+ }
+ &__minus {
+ border-top-left-radius: $uv-numberBox-minus-radius;
+ border-bottom-left-radius: $uv-numberBox-minus-radius;
+ }
+ &__input {
+ position: relative;
+ text-align: $uv-numberBox-input-text-align;
+ font-size: $uv-numberBox-input-font-size;
+ padding: $uv-numberBox-input-padding;
+ margin: $uv-numberBox-input-margin;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ &--disabled {
+ color: $uv-numberBox-input-disabled-color;
+ background-color: $uv-numberBox-input-disabled-bgColor;
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-number-box/package.json b/uni_modules/uv-number-box/package.json
new file mode 100644
index 0000000..771e6eb
--- /dev/null
+++ b/uni_modules/uv-number-box/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-number-box",
+ "displayName": "uv-number-box 步进器 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "uv-number-box 该组件一般用于商城购物选择物品数量的场景。",
+ "keywords": [
+ "uv-number-box",
+ "uvui",
+ "uv-ui",
+ "number",
+ "步进器"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-number-box/readme.md b/uni_modules/uv-number-box/readme.md
new file mode 100644
index 0000000..6b83da9
--- /dev/null
+++ b/uni_modules/uv-number-box/readme.md
@@ -0,0 +1,11 @@
+## NumberBox 步进器
+
+> **组件名:uv-number-box**
+
+该组件一般用于商城购物选择物品数量的场景。
+
+### <a href="https://www.uvui.cn/components/numberBox.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-parse/changelog.md b/uni_modules/uv-parse/changelog.md
new file mode 100644
index 0000000..4575e07
--- /dev/null
+++ b/uni_modules/uv-parse/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.4(2023-07-17)
+1. 优化文档
+2. 优化其他
+## 1.0.3(2023-06-19)
+1. 修复nvue模式下不显示的BUG
+## 1.0.2(2023-06-02)
+1. 修复可能存在的BUG
+2. 优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-parse 富文本解析器
diff --git a/uni_modules/uv-parse/components/uv-parse/node/node.vue b/uni_modules/uv-parse/components/uv-parse/node/node.vue
new file mode 100644
index 0000000..5857077
--- /dev/null
+++ b/uni_modules/uv-parse/components/uv-parse/node/node.vue
@@ -0,0 +1,576 @@
+<template>
+ <view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
+ <block v-for="(n, i) in childs" v-bind:key="i">
+ <!-- 图片 -->
+ <!-- 占位图 -->
+ <image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
+ <!-- 显示图片 -->
+ <!-- #ifdef H5 || (APP-PLUS && VUE2) -->
+ <img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+ <!-- #endif -->
+ <!-- #ifndef H5 || (APP-PLUS && VUE2) -->
+ <!-- 表格中的图片,使用 rich-text 防止大小不正确 -->
+ <rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="[{attrs:{style:n.attrs.style,src:n.attrs.src},name:'img'}]" :data-i="i" @tap.stop="imgTap" />
+ <!-- #endif -->
+ <!-- #ifndef H5 || APP-PLUS -->
+ <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+ <!-- #endif -->
+ <!-- #ifdef APP-PLUS && VUE3 -->
+ <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+ <!-- #endif -->
+ <!-- 文本 -->
+ <!-- #ifdef MP-WEIXIN -->
+ <text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
+ <!-- #endif -->
+ <!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
+ <text v-else-if="n.text" decode>{{n.text}}</text>
+ <!-- #endif -->
+ <text v-else-if="n.name==='br'">\n</text>
+ <!-- 链接 -->
+ <view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
+ <node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
+ </view>
+ <!-- 视频 -->
+ <!-- #ifdef APP-PLUS -->
+ <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
+ <!-- #endif -->
+ <!-- #ifndef APP-PLUS -->
+ <video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
+ <!-- #endif -->
+ <!-- #ifdef H5 || APP-PLUS -->
+ <iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
+ <embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
+ <!-- #endif -->
+ <!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
+ <!-- 音频 -->
+ <audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
+ <!-- #endif -->
+ <view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
+ <node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
+ <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
+ <node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
+ <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
+ <view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
+ <node :childs="tr.children" :opts="opts" />
+ </view>
+ <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
+ <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
+ <node :childs="td.children" :opts="opts" />
+ </view>
+ </view>
+ </block>
+ </view>
+ </view>
+
+ <!-- 富文本 -->
+ <!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
+ <rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
+ <!-- #endif -->
+ <!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
+ <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="'display:inline;'+n.f" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
+ <!-- #endif -->
+ <!-- 继续递归 -->
+ <view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
+ <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
+ </view>
+ <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
+ </block>
+ </view>
+</template>
+<script module="handler" lang="wxs">
+// 行内标签列表
+var inlineTags = {
+ abbr: true,
+ b: true,
+ big: true,
+ code: true,
+ del: true,
+ em: true,
+ i: true,
+ ins: true,
+ label: true,
+ q: true,
+ small: true,
+ span: true,
+ strong: true,
+ sub: true,
+ sup: true
+}
+/**
+ * @description 判断是否为行内标签
+ */
+module.exports = {
+ isInline: function (tagName, style) {
+ return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
+ }
+}
+</script>
+<script>
+
+import node from './node'
+export default {
+ name: 'node',
+ options: {
+ // #ifdef MP-WEIXIN
+ virtualHost: true,
+ // #endif
+ // #ifdef MP-TOUTIAO
+ addGlobalClass: false
+ // #endif
+ },
+ data () {
+ return {
+ ctrl: {},
+ // #ifdef MP-WEIXIN
+ isiOS: uni.getSystemInfoSync().system.includes('iOS')
+ // #endif
+ }
+ },
+ props: {
+ name: String,
+ attrs: {
+ type: Object,
+ default () {
+ return {}
+ }
+ },
+ childs: Array,
+ opts: Array
+ },
+ components: {
+
+ // #ifndef (H5 || APP-PLUS) && VUE3
+ node
+ // #endif
+ },
+ mounted () {
+ this.$nextTick(() => {
+ for (this.root = this.$parent; this.root.$options.name !== 'uv-parse'; this.root = this.root.$parent);
+ })
+ // #ifdef H5 || APP-PLUS
+ if (this.opts[0]) {
+ let i
+ for (i = this.childs.length; i--;) {
+ if (this.childs[i].name === 'img') break
+ }
+ if (i !== -1) {
+ this.observer = uni.createIntersectionObserver(this).relativeToViewport({
+ top: 500,
+ bottom: 500
+ })
+ this.observer.observe('._img', res => {
+ if (res.intersectionRatio) {
+ this.$set(this.ctrl, 'load', 1)
+ this.observer.disconnect()
+ }
+ })
+ }
+ }
+ // #endif
+ },
+ beforeDestroy () {
+ // #ifdef H5 || APP-PLUS
+ if (this.observer) {
+ this.observer.disconnect()
+ }
+ // #endif
+ },
+ methods:{
+ // #ifdef MP-WEIXIN
+ toJSON () { return this },
+ // #endif
+ /**
+ * @description 播放视频事件
+ * @param {Event} e
+ */
+ play (e) {
+ this.root.$emit('play')
+ // #ifndef APP-PLUS
+ if (this.root.pauseVideo) {
+ let flag = false
+ const id = e.target.id
+ for (let i = this.root._videos.length; i--;) {
+ if (this.root._videos[i].id === id) {
+ flag = true
+ } else {
+ this.root._videos[i].pause() // 自动暂停其他视频
+ }
+ }
+ // 将自己加入列表
+ if (!flag) {
+ const ctx = uni.createVideoContext(id
+ // #ifndef MP-BAIDU
+ , this
+ // #endif
+ )
+ ctx.id = id
+ if (this.root.playbackRate) {
+ ctx.playbackRate(this.root.playbackRate)
+ }
+ this.root._videos.push(ctx)
+ }
+ }
+ // #endif
+ },
+
+ /**
+ * @description 图片点击事件
+ * @param {Event} e
+ */
+ imgTap (e) {
+ const node = this.childs[e.currentTarget.dataset.i]
+ if (node.a) {
+ this.linkTap(node.a)
+ return
+ }
+ if (node.attrs.ignore) return
+ // #ifdef H5 || APP-PLUS
+ node.attrs.src = node.attrs.src || node.attrs['data-src']
+ // #endif
+ this.root.$emit('imgtap', node.attrs)
+ // 自动预览图片
+ if (this.root.previewImg) {
+ uni.previewImage({
+ // #ifdef MP-WEIXIN
+ showmenu: this.root.showImgMenu,
+ // #endif
+ // #ifdef MP-ALIPAY
+ enablesavephoto: this.root.showImgMenu,
+ enableShowPhotoDownload: this.root.showImgMenu,
+ // #endif
+ current: parseInt(node.attrs.i),
+ urls: this.root.imgList
+ })
+ }
+ },
+
+ /**
+ * @description 图片长按
+ */
+ imgLongTap (e) {
+ // #ifdef APP-PLUS
+ const attrs = this.childs[e.currentTarget.dataset.i].attrs
+ if (this.opts[3] && !attrs.ignore) {
+ uni.showActionSheet({
+ itemList: ['保存图片'],
+ success: () => {
+ const save = path => {
+ uni.saveImageToPhotosAlbum({
+ filePath: path,
+ success () {
+ uni.showToast({
+ title: '保存成功'
+ })
+ }
+ })
+ }
+ if (this.root.imgList[attrs.i].startsWith('http')) {
+ uni.downloadFile({
+ url: this.root.imgList[attrs.i],
+ success: res => save(res.tempFilePath)
+ })
+ } else {
+ save(this.root.imgList[attrs.i])
+ }
+ }
+ })
+ }
+ // #endif
+ },
+
+ /**
+ * @description 图片加载完成事件
+ * @param {Event} e
+ */
+ imgLoad (e) {
+ const i = e.currentTarget.dataset.i
+ /* #ifndef H5 || (APP-PLUS && VUE2) */
+ if (!this.childs[i].w) {
+ // 设置原宽度
+ this.$set(this.ctrl, i, e.detail.width)
+ } else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
+ // 加载完毕,取消加载中占位图
+ this.$set(this.ctrl, i, 1)
+ }
+ this.checkReady()
+ },
+
+ /**
+ * @description 检查是否所有图片加载完毕
+ */
+ checkReady () {
+ if (this.root && !this.root.lazyLoad) {
+ this.root._unloadimgs -= 1
+ if (!this.root._unloadimgs) {
+ setTimeout(() => {
+ this.root.getRect().then(rect => {
+ this.root.$emit('ready', rect)
+ }).catch(() => {
+ this.root.$emit('ready', {})
+ })
+ }, 350)
+ }
+ }
+ },
+
+ /**
+ * @description 链接点击事件
+ * @param {Event} e
+ */
+ linkTap (e) {
+ const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
+ const attrs = node.attrs || e
+ const href = attrs.href
+ this.root.$emit('linktap', Object.assign({
+ innerText: this.root.getText(node.children || []) // 链接内的文本内容
+ }, attrs))
+ if (href) {
+ if (href[0] === '#') {
+ // 跳转锚点
+ this.root.navigateTo(href.substring(1)).catch(() => { })
+ } else if (href.split('?')[0].includes('://')) {
+ // 复制外部链接
+ if (this.root.copyLink) {
+ // #ifdef H5
+ window.open(href)
+ // #endif
+ // #ifdef MP
+ uni.setClipboardData({
+ data: href,
+ success: () =>
+ uni.showToast({
+ title: '链接已复制'
+ })
+ })
+ // #endif
+ // #ifdef APP-PLUS
+ plus.runtime.openWeb(href)
+ // #endif
+ }
+ } else {
+ // 跳转页面
+ uni.navigateTo({
+ url: href,
+ fail () {
+ uni.switchTab({
+ url: href,
+ fail () { }
+ })
+ }
+ })
+ }
+ }
+ },
+
+ /**
+ * @description 错误事件
+ * @param {Event} e
+ */
+ mediaError (e) {
+ const i = e.currentTarget.dataset.i
+ const node = this.childs[i]
+ // 加载其他源
+ if (node.name === 'video' || node.name === 'audio') {
+ let index = (this.ctrl[i] || 0) + 1
+ if (index > node.src.length) {
+ index = 0
+ }
+ if (index < node.src.length) {
+ this.$set(this.ctrl, i, index)
+ return
+ }
+ } else if (node.name === 'img') {
+ // #ifdef H5 && VUE3
+ if (this.opts[0] && !this.ctrl.load) return
+ // #endif
+ // 显示错误占位图
+ if (this.opts[2]) {
+ this.$set(this.ctrl, i, -1)
+ }
+ this.checkReady()
+ }
+ if (this.root) {
+ this.root.$emit('error', {
+ source: node.name,
+ attrs: node.attrs,
+ // #ifndef H5 && VUE3
+ errMsg: e.detail.errMsg
+ // #endif
+ })
+ }
+ }
+ }
+}
+</script>
+<style>
+/* a 标签默认效果 */
+._a {
+ padding: 1.5px 0 1.5px 0;
+ color: #366092;
+ word-break: break-all;
+}
+
+/* a 标签点击态效果 */
+._hover {
+ text-decoration: underline;
+ opacity: 0.7;
+}
+
+/* 图片默认效果 */
+._img {
+ max-width: 100%;
+ -webkit-touch-callout: none;
+}
+
+/* 内部样式 */
+
+._block {
+ display: block;
+}
+
+._b,
+._strong {
+ font-weight: bold;
+}
+
+._code {
+ font-family: monospace;
+}
+
+._del {
+ text-decoration: line-through;
+}
+
+._em,
+._i {
+ font-style: italic;
+}
+
+._h1 {
+ font-size: 2em;
+}
+
+._h2 {
+ font-size: 1.5em;
+}
+
+._h3 {
+ font-size: 1.17em;
+}
+
+._h5 {
+ font-size: 0.83em;
+}
+
+._h6 {
+ font-size: 0.67em;
+}
+
+._h1,
+._h2,
+._h3,
+._h4,
+._h5,
+._h6 {
+ display: block;
+ font-weight: bold;
+}
+
+._image {
+ height: 1px;
+}
+
+._ins {
+ text-decoration: underline;
+}
+
+._li {
+ display: list-item;
+}
+
+._ol {
+ list-style-type: decimal;
+}
+
+._ol,
+._ul {
+ display: block;
+ padding-left: 40px;
+ margin: 1em 0;
+}
+
+._q::before {
+ content: '"';
+}
+
+._q::after {
+ content: '"';
+}
+
+._sub {
+ font-size: smaller;
+ vertical-align: sub;
+}
+
+._sup {
+ font-size: smaller;
+ vertical-align: super;
+}
+
+._thead,
+._tbody,
+._tfoot {
+ display: table-row-group;
+}
+
+._tr {
+ display: table-row;
+}
+
+._td,
+._th {
+ display: table-cell;
+ vertical-align: middle;
+}
+
+._th {
+ font-weight: bold;
+ text-align: center;
+}
+
+._ul {
+ list-style-type: disc;
+}
+
+._ul ._ul {
+ margin: 0;
+ list-style-type: circle;
+}
+
+._ul ._ul ._ul {
+ list-style-type: square;
+}
+
+._abbr,
+._b,
+._code,
+._del,
+._em,
+._i,
+._ins,
+._label,
+._q,
+._span,
+._strong,
+._sub,
+._sup {
+ display: inline;
+}
+
+/* #ifdef APP-PLUS */
+._video {
+ width: 300px;
+ height: 225px;
+}
+/* #endif */
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-parse/components/uv-parse/parser.js b/uni_modules/uv-parse/components/uv-parse/parser.js
new file mode 100644
index 0000000..e2e7a87
--- /dev/null
+++ b/uni_modules/uv-parse/components/uv-parse/parser.js
@@ -0,0 +1,1335 @@
+/**
+ * @fileoverview html 解析器
+ */
+
+// 配置
+const config = {
+ // 信任的标签(保持标签名不变)
+ trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+
+ // 块级标签(转为 div,其他的非信任标签转为 span)
+ blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ // 行内标签
+ inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
+ // #endif
+
+ // 要移除的标签
+ ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
+
+ // 自闭合的标签
+ voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+
+ // html 实体
+ entities: {
+ lt: '<',
+ gt: '>',
+ quot: '"',
+ apos: "'",
+ ensp: '\u2002',
+ emsp: '\u2003',
+ nbsp: '\xA0',
+ semi: ';',
+ ndash: '–',
+ mdash: '—',
+ middot: '·',
+ lsquo: '‘',
+ rsquo: '’',
+ ldquo: '“',
+ rdquo: '”',
+ bull: '•',
+ hellip: '…',
+ larr: '←',
+ uarr: '↑',
+ rarr: '→',
+ darr: '↓'
+ },
+
+ // 默认的标签样式
+ tagStyle: {
+ // #ifndef APP-PLUS-NVUE
+ address: 'font-style:italic',
+ big: 'display:inline;font-size:1.2em',
+ caption: 'display:table-caption;text-align:center',
+ center: 'text-align:center',
+ cite: 'font-style:italic',
+ dd: 'margin-left:40px',
+ mark: 'background-color:yellow',
+ pre: 'font-family:monospace;white-space:pre',
+ s: 'text-decoration:line-through',
+ small: 'display:inline;font-size:0.8em',
+ strike: 'text-decoration:line-through',
+ u: 'text-decoration:underline'
+ // #endif
+ },
+
+ // svg 大小写对照表
+ svgDict: {
+ animatetransform: 'animateTransform',
+ lineargradient: 'linearGradient',
+ viewbox: 'viewBox',
+ attributename: 'attributeName',
+ repeatcount: 'repeatCount',
+ repeatdur: 'repeatDur'
+ }
+}
+const tagSelector={}
+const {
+ windowWidth,
+ // #ifdef MP-WEIXIN
+ system
+ // #endif
+} = uni.getSystemInfoSync()
+const blankChar = makeMap(' ,\r,\n,\t,\f')
+let idIndex = 0
+
+// #ifdef H5 || APP-PLUS
+config.ignoreTags.iframe = undefined
+config.trustTags.iframe = true
+config.ignoreTags.embed = undefined
+config.trustTags.embed = true
+// #endif
+// #ifdef APP-PLUS-NVUE
+config.ignoreTags.source = undefined
+config.ignoreTags.style = undefined
+// #endif
+
+/**
+ * @description 创建 map
+ * @param {String} str 逗号分隔
+ */
+function makeMap (str) {
+ const map = Object.create(null)
+ const list = str.split(',')
+ for (let i = list.length; i--;) {
+ map[list[i]] = true
+ }
+ return map
+}
+
+/**
+ * @description 解码 html 实体
+ * @param {String} str 要解码的字符串
+ * @param {Boolean} amp 要不要解码 &
+ * @returns {String} 解码后的字符串
+ */
+function decodeEntity (str, amp) {
+ let i = str.indexOf('&')
+ while (i !== -1) {
+ const j = str.indexOf(';', i + 3)
+ let code
+ if (j === -1) break
+ if (str[i + 1] === '#') {
+ // { 形式的实体
+ code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
+ if (!isNaN(code)) {
+ str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
+ }
+ } else {
+ // 形式的实体
+ code = str.substring(i + 1, j)
+ if (config.entities[code] || (code === 'amp' && amp)) {
+ str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
+ }
+ }
+ i = str.indexOf('&', i + 1)
+ }
+ return str
+}
+
+/**
+ * @description 合并多个块级标签,加快长内容渲染
+ * @param {Array} nodes 要合并的标签数组
+ */
+function mergeNodes (nodes) {
+ let i = nodes.length - 1
+ for (let j = i; j >= -1; j--) {
+ if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
+ if (i - j >= 5) {
+ nodes.splice(j + 1, i - j, {
+ name: 'div',
+ attrs: {},
+ children: nodes.slice(j + 1, i + 1)
+ })
+ }
+ i = j - 1
+ }
+ }
+}
+
+/**
+ * @description html 解析器
+ * @param {Object} vm 组件实例
+ */
+function Parser (vm) {
+ this.options = vm || {}
+ this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
+ this.imgList = vm.imgList || []
+ this.imgList._unloadimgs = 0
+ this.plugins = vm.plugins || []
+ this.attrs = Object.create(null)
+ this.stack = []
+ this.nodes = []
+ this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Parser.prototype.parse = function (content) {
+ // 插件处理
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onUpdate) {
+ content = this.plugins[i].onUpdate(content, config) || content
+ }
+ }
+
+ new Lexer(this).parse(content)
+ // 出栈未闭合的标签
+ while (this.stack.length) {
+ this.popNode()
+ }
+ if (this.nodes.length > 50) {
+ mergeNodes(this.nodes)
+ }
+ return this.nodes
+}
+
+/**
+ * @description 将标签暴露出来(不被 rich-text 包含)
+ */
+Parser.prototype.expose = function () {
+ // #ifndef APP-PLUS-NVUE
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
+ item.c = 1
+ }
+ // #endif
+}
+
+/**
+ * @description 处理插件
+ * @param {Object} node 要处理的标签
+ * @returns {Boolean} 是否要移除此标签
+ */
+Parser.prototype.hook = function (node) {
+ for (let i = this.plugins.length; i--;) {
+ if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
+ return false
+ }
+ }
+ return true
+}
+
+/**
+ * @description 将链接拼接上主域名
+ * @param {String} url 需要拼接的链接
+ * @returns {String} 拼接后的链接
+ */
+Parser.prototype.getUrl = function (url) {
+ const domain = this.options.domain
+ if (url[0] === '/') {
+ if (url[1] === '/') {
+ // // 开头的补充协议名
+ url = (domain ? domain.split('://')[0] : 'http') + ':' + url
+ } else if (domain) {
+ // 否则补充整个域名
+ url = domain + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ } else if (!url.includes('data:') && !url.includes('://')) {
+ if (domain) {
+ url = domain + '/' + url
+ } /* #ifdef APP-PLUS */ else {
+ url = plus.io.convertLocalFileSystemURL(url)
+ } /* #endif */
+ }
+ return url
+}
+
+/**
+ * @description 解析样式表
+ * @param {Object} node 标签
+ * @returns {Object}
+ */
+Parser.prototype.parseStyle = function (node) {
+ const attrs = node.attrs
+ const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
+ const styleObj = {}
+ let tmp = ''
+
+ if (attrs.id && !this.xml) {
+ // 暴露锚点
+ if (this.options.useAnchor) {
+ this.expose()
+ } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
+ attrs.id = undefined
+ }
+ }
+
+ // 转换 width 和 height 属性
+ if (attrs.width) {
+ styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
+ attrs.width = undefined
+ }
+ if (attrs.height) {
+ styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
+ attrs.height = undefined
+ }
+
+ for (let i = 0, len = list.length; i < len; i++) {
+ const info = list[i].split(':')
+ if (info.length < 2) continue
+ const key = info.shift().trim().toLowerCase()
+ let value = info.join(':').trim()
+ if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
+ // 兼容性的 css 不压缩
+ tmp += `;${key}:${value}`
+ } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
+ // 重复的样式进行覆盖
+ if (value.includes('url')) {
+ // 填充链接
+ let j = value.indexOf('(') + 1
+ if (j) {
+ while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
+ j++
+ }
+ value = value.substr(0, j) + this.getUrl(value.substr(j))
+ }
+ } else if (value.includes('rpx')) {
+ // 转换 rpx(rich-text 内部不支持 rpx)
+ value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
+ }
+ styleObj[key] = value
+ }
+ }
+
+ node.attrs.style = tmp
+ return styleObj
+}
+
+/**
+ * @description 解析到标签名
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onTagName = function (name) {
+ this.tagName = this.xml ? name : name.toLowerCase()
+ if (this.tagName === 'svg') {
+ this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
+ config.ignoreTags.style = undefined // svg 标签内 style 可用
+ }
+}
+
+/**
+ * @description 解析到属性名
+ * @param {String} name 属性名
+ * @private
+ */
+Parser.prototype.onAttrName = function (name) {
+ name = this.xml ? name : name.toLowerCase()
+ if (name.substr(0, 5) === 'data-') {
+ if (name === 'data-src' && !this.attrs.src) {
+ // data-src 自动转为 src
+ this.attrName = 'src'
+ } else if (this.tagName === 'img' || this.tagName === 'a') {
+ // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
+ this.attrName = name
+ } else {
+ // 剩余的移除以减小大小
+ this.attrName = undefined
+ }
+ } else {
+ this.attrName = name
+ this.attrs[name] = 'T' // boolean 型属性缺省设置
+ }
+}
+
+/**
+ * @description 解析到属性值
+ * @param {String} val 属性值
+ * @private
+ */
+Parser.prototype.onAttrVal = function (val) {
+ const name = this.attrName || ''
+ if (name === 'style' || name === 'href') {
+ // 部分属性进行实体解码
+ this.attrs[name] = decodeEntity(val, true)
+ } else if (name.includes('src')) {
+ // 拼接主域名
+ this.attrs[name] = this.getUrl(decodeEntity(val, true))
+ } else if (name) {
+ this.attrs[name] = val
+ }
+}
+
+/**
+ * @description 解析到标签开始
+ * @param {Boolean} selfClose 是否有自闭合标识 />
+ * @private
+ */
+Parser.prototype.onOpenTag = function (selfClose) {
+ // 拼装 node
+ const node = Object.create(null)
+ node.name = this.tagName
+ node.attrs = this.attrs
+ // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
+ if (this.options.nodes.length) {
+ node.type = 'node'
+ }
+ this.attrs = Object.create(null)
+
+ const attrs = node.attrs
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+ const close = this.xml ? selfClose : config.voidTags[node.name]
+
+ // 替换标签名选择器
+ if (tagSelector[node.name]) {
+ attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
+ }
+
+ // 转换 embed 标签
+ if (node.name === 'embed') {
+ // #ifndef H5 || APP-PLUS
+ const src = attrs.src || ''
+ // 按照后缀名和 type 将 embed 转为 video 或 audio
+ if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
+ node.name = 'video'
+ } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
+ node.name = 'audio'
+ }
+ if (attrs.autostart) {
+ attrs.autoplay = 'T'
+ }
+ attrs.controls = 'T'
+ // #endif
+ // #ifdef H5 || APP-PLUS
+ this.expose()
+ // #endif
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 处理音视频
+ if (node.name === 'video' || node.name === 'audio') {
+ // 设置 id 以便获取 context
+ if (node.name === 'video' && !attrs.id) {
+ attrs.id = 'v' + idIndex++
+ }
+ // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
+ if (!attrs.controls && !attrs.autoplay) {
+ attrs.controls = 'T'
+ }
+ // 用数组存储所有可用的 source
+ node.src = []
+ if (attrs.src) {
+ node.src.push(attrs.src)
+ attrs.src = undefined
+ }
+ this.expose()
+ }
+ // #endif
+
+ // 处理自闭合标签
+ if (close) {
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 通过 base 标签设置主域名
+ if (node.name === 'base' && !this.options.domain) {
+ this.options.domain = attrs.href
+ } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
+ // 设置 source 标签(仅父节点为 video 或 audio 时有效)
+ parent.src.push(attrs.src)
+ } /* #endif */
+ return
+ }
+
+ // 解析 style
+ const styleObj = this.parseStyle(node)
+
+ // 处理图片
+ if (node.name === 'img') {
+ if (attrs.src) {
+ // 标记 webp
+ if (attrs.src.includes('webp')) {
+ node.webp = 'T'
+ }
+ // data url 图片如果没有设置 original-src 默认为不可预览的小图片
+ if (attrs.src.includes('data:') && !attrs['original-src']) {
+ attrs.ignore = 'T'
+ }
+ if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
+ for (let i = this.stack.length; i--;) {
+ const item = this.stack[i]
+ if (item.name === 'a') {
+ node.a = item.attrs
+ }
+ if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
+ if (!styleObj.display || styleObj.display.includes('inline')) {
+ node.t = 'inline-block'
+ } else {
+ node.t = styleObj.display
+ }
+ styleObj.display = undefined
+ }
+ // #ifndef H5 || APP-PLUS
+ const style = item.attrs.style || ''
+ if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
+ styleObj.width = '100% !important'
+ styleObj.height = ''
+ for (let j = i + 1; j < this.stack.length; j++) {
+ this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
+ }
+ } else if (style.includes('flex') && styleObj.width === '100%') {
+ for (let j = i + 1; j < this.stack.length; j++) {
+ const style = this.stack[j].attrs.style || ''
+ if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
+ styleObj.width = ''
+ break
+ }
+ }
+ } else if (style.includes('inline-block')) {
+ if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
+ item.attrs.style += ';max-width:' + styleObj.width
+ styleObj.width = ''
+ } else {
+ item.attrs.style += ';max-width:100%'
+ }
+ }
+ // #endif
+ item.c = 1
+ }
+ attrs.i = this.imgList.length.toString()
+ let src = attrs['original-src'] || attrs.src
+ // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
+ if (this.imgList.includes(src)) {
+ // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
+ let i = src.indexOf('://')
+ if (i !== -1) {
+ i += 3
+ let newSrc = src.substr(0, i)
+ for (; i < src.length; i++) {
+ if (src[i] === '/') break
+ newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
+ }
+ newSrc += src.substr(i)
+ src = newSrc
+ }
+ }
+ // #endif
+ this.imgList.push(src)
+ if (!node.t) {
+ this.imgList._unloadimgs += 1
+ }
+ // #ifdef H5 || APP-PLUS
+ if (this.options.lazyLoad) {
+ attrs['data-src'] = attrs.src
+ attrs.src = undefined
+ }
+ // #endif
+ }
+ }
+ if (styleObj.display === 'inline') {
+ styleObj.display = ''
+ }
+ // #ifndef APP-PLUS-NVUE
+ if (attrs.ignore) {
+ styleObj['max-width'] = styleObj['max-width'] || '100%'
+ attrs.style += ';-webkit-touch-callout:none'
+ }
+ // #endif
+ // 设置的宽度超出屏幕,为避免变形,高度转为自动
+ if (parseInt(styleObj.width) > windowWidth) {
+ styleObj.height = undefined
+ }
+ // 记录是否设置了宽高
+ if (!isNaN(parseInt(styleObj.width))) {
+ node.w = 'T'
+ }
+ if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
+ node.h = 'T'
+ }
+ } else if (node.name === 'svg') {
+ siblings.push(node)
+ this.stack.push(node)
+ this.popNode()
+ return
+ }
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ if (!attrs.style) {
+ delete attrs.style
+ }
+ // #endif
+ } else {
+ if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
+ this.pre = node.pre = 1
+ }
+ node.children = []
+ this.stack.push(node)
+ }
+
+ // 加入节点树
+ siblings.push(node)
+}
+
+/**
+ * @description 解析到标签结束
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onCloseTag = function (name) {
+ // 依次出栈到匹配为止
+ name = this.xml ? name : name.toLowerCase()
+ let i
+ for (i = this.stack.length; i--;) {
+ if (this.stack[i].name === name) break
+ }
+ if (i !== -1) {
+ while (this.stack.length > i) {
+ this.popNode()
+ }
+ } else if (name === 'p' || name === 'br') {
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push({
+ name,
+ attrs: {
+ class: tagSelector[name] || '',
+ style: this.tagStyle[name] || ''
+ }
+ })
+ }
+}
+
+/**
+ * @description 处理标签出栈
+ * @private
+ */
+Parser.prototype.popNode = function () {
+ const node = this.stack.pop()
+ let attrs = node.attrs
+ const children = node.children
+ const parent = this.stack[this.stack.length - 1]
+ const siblings = parent ? parent.children : this.nodes
+
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
+ // 获取标题
+ if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
+ uni.setNavigationBarTitle({
+ title: children[0].text
+ })
+ }
+ siblings.pop()
+ return
+ }
+
+ if (node.pre && this.pre !== 2) {
+ // 是否合并空白符标识
+ this.pre = node.pre = undefined
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].pre) {
+ this.pre = 1
+ }
+ }
+ }
+
+ const styleObj = {}
+
+ // 转换 svg
+ if (node.name === 'svg') {
+ if (this.xml > 1) {
+ // 多层 svg 嵌套
+ this.xml--
+ return
+ }
+ // #ifdef APP-PLUS-NVUE
+ (function traversal (node) {
+ if (node.name) {
+ // 调整 svg 的大小写
+ node.name = config.svgDict[node.name] || node.name
+ for (const item in node.attrs) {
+ if (config.svgDict[item]) {
+ node.attrs[config.svgDict[item]] = node.attrs[item]
+ node.attrs[item] = undefined
+ }
+ }
+ for (let i = 0; i < (node.children || []).length; i++) {
+ traversal(node.children[i])
+ }
+ }
+ })(node)
+ // #endif
+ // #ifndef APP-PLUS-NVUE
+ let src = ''
+ const style = attrs.style
+ attrs.style = ''
+ attrs.xmlns = 'http://www.w3.org/2000/svg';
+ (function traversal (node) {
+ if (node.type === 'text') {
+ src += node.text
+ return
+ }
+ const name = config.svgDict[node.name] || node.name
+ src += '<' + name
+ for (const item in node.attrs) {
+ const val = node.attrs[item]
+ if (val) {
+ src += ` ${config.svgDict[item] || item}="${val}"`
+ }
+ }
+ if (!node.children) {
+ src += '/>'
+ } else {
+ src += '>'
+ for (let i = 0; i < node.children.length; i++) {
+ traversal(node.children[i])
+ }
+ src += '</' + name + '>'
+ }
+ })(node)
+ node.name = 'img'
+ node.attrs = {
+ src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+ style,
+ ignore: 'T'
+ }
+ node.children = undefined
+ // #endif
+ this.xml = false
+ config.ignoreTags.style = true
+ return
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ // 转换 align 属性
+ if (attrs.align) {
+ if (node.name === 'table') {
+ if (attrs.align === 'center') {
+ styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
+ } else {
+ styleObj.float = attrs.align
+ }
+ } else {
+ styleObj['text-align'] = attrs.align
+ }
+ attrs.align = undefined
+ }
+
+ // 转换 dir 属性
+ if (attrs.dir) {
+ styleObj.direction = attrs.dir
+ attrs.dir = undefined
+ }
+
+ // 转换 font 标签的属性
+ if (node.name === 'font') {
+ if (attrs.color) {
+ styleObj.color = attrs.color
+ attrs.color = undefined
+ }
+ if (attrs.face) {
+ styleObj['font-family'] = attrs.face
+ attrs.face = undefined
+ }
+ if (attrs.size) {
+ let size = parseInt(attrs.size)
+ if (!isNaN(size)) {
+ if (size < 1) {
+ size = 1
+ } else if (size > 7) {
+ size = 7
+ }
+ styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
+ }
+ attrs.size = undefined
+ }
+ }
+ // #endif
+
+ // 一些编辑器的自带 class
+ if ((attrs.class || '').includes('align-center')) {
+ styleObj['text-align'] = 'center'
+ }
+
+ Object.assign(styleObj, this.parseStyle(node))
+
+ if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
+ styleObj['max-width'] = '100%'
+ styleObj['box-sizing'] = 'border-box'
+ }
+
+ // #ifndef APP-PLUS-NVUE
+ if (config.blockTags[node.name]) {
+ node.name = 'div'
+ } else if (!config.trustTags[node.name] && !this.xml) {
+ // 未知标签转为 span,避免无法显示
+ node.name = 'span'
+ }
+
+ if (node.name === 'a' || node.name === 'ad'
+ // #ifdef H5 || APP-PLUS
+ || node.name === 'iframe' // eslint-disable-line
+ // #endif
+ ) {
+ this.expose()
+ } else if (node.name === 'video') {
+ if ((styleObj.height || '').includes('auto')) {
+ styleObj.height = undefined
+ }
+ /* #ifdef APP-PLUS */
+ let str = '<video style="width:100%;height:100%"'
+ for (const item in attrs) {
+ if (attrs[item]) {
+ str += ' ' + item + '="' + attrs[item] + '"'
+ }
+ }
+ if (this.options.pauseVideo) {
+ str += ' onplay="this.dispatchEvent(new CustomEvent(\'vplay\',{bubbles:!0}));for(var e=document.getElementsByTagName(\'video\'),t=0;t<e.length;t++)e[t]!=this&&e[t].pause()"'
+ }
+ str += '>'
+ for (let i = 0; i < node.src.length; i++) {
+ str += '<source src="' + node.src[i] + '">'
+ }
+ str += '</video>'
+ node.html = str
+ /* #endif */
+ } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
+ // 列表处理
+ const types = {
+ a: 'lower-alpha',
+ A: 'upper-alpha',
+ i: 'lower-roman',
+ I: 'upper-roman'
+ }
+ if (types[attrs.type]) {
+ attrs.style += ';list-style-type:' + types[attrs.type]
+ attrs.type = undefined
+ }
+ for (let i = children.length; i--;) {
+ if (children[i].name === 'li') {
+ children[i].c = 1
+ }
+ }
+ } else if (node.name === 'table') {
+ // 表格处理
+ // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
+ let padding = parseFloat(attrs.cellpadding)
+ let spacing = parseFloat(attrs.cellspacing)
+ const border = parseFloat(attrs.border)
+ const bordercolor = styleObj['border-color']
+ const borderstyle = styleObj['border-style']
+ if (node.c) {
+ // padding 和 spacing 默认 2
+ if (isNaN(padding)) {
+ padding = 2
+ }
+ if (isNaN(spacing)) {
+ spacing = 2
+ }
+ }
+ if (border) {
+ attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
+ }
+ if (node.flag && node.c) {
+ // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
+ styleObj.display = 'grid'
+ if (spacing) {
+ styleObj['grid-gap'] = spacing + 'px'
+ styleObj.padding = spacing + 'px'
+ } else if (border) {
+ // 无间隔的情况下避免边框重叠
+ attrs.style += ';border-left:0;border-top:0'
+ }
+
+ const width = [] // 表格的列宽
+ const trList = [] // tr 列表
+ const cells = [] // 保存新的单元格
+ const map = {}; // 被合并单元格占用的格子
+
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ if (nodes[i].name === 'tr') {
+ trList.push(nodes[i])
+ } else {
+ traversal(nodes[i].children || [])
+ }
+ }
+ })(children)
+
+ for (let row = 1; row <= trList.length; row++) {
+ let col = 1
+ for (let j = 0; j < trList[row - 1].children.length; j++) {
+ const td = trList[row - 1].children[j]
+ if (td.name === 'td' || td.name === 'th') {
+ // 这个格子被上面的单元格占用,则列号++
+ while (map[row + '.' + col]) {
+ col++
+ }
+ let style = td.attrs.style || ''
+ let start = style.indexOf('width') ? style.indexOf(';width') : 0
+ // 提取出 td 的宽度
+ if (start !== -1) {
+ let end = style.indexOf(';', start + 6)
+ if (end === -1) {
+ end = style.length
+ }
+ if (!td.attrs.colspan) {
+ width[col] = style.substring(start ? start + 7 : 6, end)
+ }
+ style = style.substr(0, start) + style.substr(end)
+ }
+ // 设置竖直对齐
+ style += ';display:flex'
+ start = style.indexOf('vertical-align')
+ if (start !== -1) {
+ const val = style.substr(start + 15, 10)
+ if (val.includes('middle')) {
+ style += ';align-items:center'
+ } else if (val.includes('bottom')) {
+ style += ';align-items:flex-end'
+ }
+ } else {
+ style += ';align-items:center'
+ }
+ // 设置水平对齐
+ start = style.indexOf('text-align')
+ if (start !== -1) {
+ const val = style.substr(start + 11, 10)
+ if (val.includes('center')) {
+ style += ';justify-content: center'
+ } else if (val.includes('right')) {
+ style += ';justify-content: right'
+ }
+ }
+ style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
+ // 处理列合并
+ if (td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
+ if (!td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + 1}`
+ }
+ col += parseInt(td.attrs.colspan) - 1
+ }
+ // 处理行合并
+ if (td.attrs.rowspan) {
+ style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
+ if (!td.attrs.colspan) {
+ style += `;grid-column-start:${col};grid-column-end:${col + 1}`
+ }
+ // 记录下方单元格被占用
+ for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
+ for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
+ map[(row + rowspan) + '.' + (col - colspan)] = 1
+ }
+ }
+ }
+ if (style) {
+ td.attrs.style = style
+ }
+ cells.push(td)
+ col++
+ }
+ }
+ if (row === 1) {
+ let temp = ''
+ for (let i = 1; i < col; i++) {
+ temp += (width[i] ? width[i] : 'auto') + ' '
+ }
+ styleObj['grid-template-columns'] = temp
+ }
+ }
+ node.children = cells
+ } else {
+ // 没有使用合并单元格的表格通过 table 布局实现
+ if (node.c) {
+ styleObj.display = 'table'
+ }
+ if (!isNaN(spacing)) {
+ styleObj['border-spacing'] = spacing + 'px'
+ }
+ if (border || padding) {
+ // 遍历
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ const td = nodes[i]
+ if (td.name === 'th' || td.name === 'td') {
+ if (border) {
+ td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
+ }
+ if (padding) {
+ td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
+ }
+ } else if (td.children) {
+ traversal(td.children)
+ }
+ }
+ })(children)
+ }
+ }
+ // 给表格添加一个单独的横向滚动层
+ if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
+ const table = Object.assign({}, node)
+ node.name = 'div'
+ node.attrs = {
+ style: 'overflow:auto'
+ }
+ node.children = [table]
+ attrs = table.attrs
+ }
+ } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
+ for (let i = this.stack.length; i--;) {
+ if (this.stack[i].name === 'table') {
+ this.stack[i].flag = 1 // 指示含有合并单元格
+ break
+ }
+ }
+ } else if (node.name === 'ruby') {
+ // 转换 ruby
+ node.name = 'span'
+ for (let i = 0; i < children.length - 1; i++) {
+ if (children[i].type === 'text' && children[i + 1].name === 'rt') {
+ children[i] = {
+ name: 'div',
+ attrs: {
+ style: 'display:inline-block;text-align:center'
+ },
+ children: [{
+ name: 'div',
+ attrs: {
+ style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
+ },
+ children: children[i + 1].children
+ }, children[i]]
+ }
+ children.splice(i + 1, 1)
+ }
+ }
+ } else if (node.c) {
+ (function traversal (node) {
+ node.c = 2
+ for (let i = node.children.length; i--;) {
+ const child = node.children[i]
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+ if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
+ traversal(child)
+ }
+ // #endif
+ if (!child.c || child.name === 'table') {
+ node.c = 1
+ }
+ }
+ })(node)
+ }
+
+ if ((styleObj.display || '').includes('flex') && !node.c) {
+ for (let i = children.length; i--;) {
+ const item = children[i]
+ if (item.f) {
+ item.attrs.style = (item.attrs.style || '') + item.f
+ item.f = undefined
+ }
+ }
+ }
+ // flex 布局时部分样式需要提取到 rich-text 外层
+ const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
+ // #ifdef MP-WEIXIN
+ // 检查基础库版本 virtualHost 是否可用
+ && !(node.c && wx.getNFCAdapter) // eslint-disable-line
+ // #endif
+ // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
+ && !node.c // eslint-disable-line
+ // #endif
+ if (flex) {
+ node.f = ';max-width:100%'
+ }
+
+ if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
+ mergeNodes(children)
+ }
+ // #endif
+
+ for (const key in styleObj) {
+ if (styleObj[key]) {
+ const val = `;${key}:${styleObj[key].replace(' !important', '')}`
+ /* #ifndef APP-PLUS-NVUE */
+ if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
+ node.f += val
+ if (key === 'width') {
+ attrs.style += ';width:100%'
+ }
+ } else /* #endif */ {
+ attrs.style += val
+ }
+ }
+ }
+ attrs.style = attrs.style.substr(1) || undefined
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+ for (const key in attrs) {
+ if (!attrs[key]) {
+ delete attrs[key]
+ }
+ }
+ // #endif
+}
+
+/**
+ * @description 解析到文本
+ * @param {String} text 文本内容
+ */
+Parser.prototype.onText = function (text) {
+ if (!this.pre) {
+ // 合并空白符
+ let trim = ''
+ let flag
+ for (let i = 0, len = text.length; i < len; i++) {
+ if (!blankChar[text[i]]) {
+ trim += text[i]
+ } else {
+ if (trim[trim.length - 1] !== ' ') {
+ trim += ' '
+ }
+ if (text[i] === '\n' && !flag) {
+ flag = true
+ }
+ }
+ }
+ // 去除含有换行符的空串
+ if (trim === ' ') {
+ if (flag) return
+ // #ifdef VUE3
+ else {
+ const parent = this.stack[this.stack.length - 1]
+ if (parent && parent.name[0] === 't') return
+ }
+ // #endif
+ }
+ text = trim
+ }
+ const node = Object.create(null)
+ node.type = 'text'
+ // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
+ node.attrs = {}
+ // #endif
+ node.text = decodeEntity(text)
+ if (this.hook(node)) {
+ // #ifdef MP-WEIXIN
+ if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
+ this.expose()
+ }
+ // #endif
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+ siblings.push(node)
+ }
+}
+
+/**
+ * @description html 词法分析器
+ * @param {Object} handler 高层处理器
+ */
+function Lexer (handler) {
+ this.handler = handler
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Lexer.prototype.parse = function (content) {
+ this.content = content || ''
+ this.i = 0 // 标记解析位置
+ this.start = 0 // 标记一个单词的开始位置
+ this.state = this.text // 当前状态
+ for (let len = this.content.length; this.i !== -1 && this.i < len;) {
+ this.state()
+ }
+}
+
+/**
+ * @description 检查标签是否闭合
+ * @param {String} method 如果闭合要进行的操作
+ * @returns {Boolean} 是否闭合
+ * @private
+ */
+Lexer.prototype.checkClose = function (method) {
+ const selfClose = this.content[this.i] === '/'
+ if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
+ if (method) {
+ this.handler[method](this.content.substring(this.start, this.i))
+ }
+ this.i += selfClose ? 2 : 1
+ this.start = this.i
+ this.handler.onOpenTag(selfClose)
+ if (this.handler.tagName === 'script') {
+ this.i = this.content.indexOf('</', this.i)
+ if (this.i !== -1) {
+ this.i += 2
+ this.start = this.i
+ }
+ this.state = this.endTag
+ } else {
+ this.state = this.text
+ }
+ return true
+ }
+ return false
+}
+
+/**
+ * @description 文本状态
+ * @private
+ */
+Lexer.prototype.text = function () {
+ this.i = this.content.indexOf('<', this.i) // 查找最近的标签
+ if (this.i === -1) {
+ // 没有标签了
+ if (this.start < this.content.length) {
+ this.handler.onText(this.content.substring(this.start, this.content.length))
+ }
+ return
+ }
+ const c = this.content[this.i + 1]
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ // 标签开头
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ this.start = ++this.i
+ this.state = this.tagName
+ } else if (c === '/' || c === '!' || c === '?') {
+ if (this.start !== this.i) {
+ this.handler.onText(this.content.substring(this.start, this.i))
+ }
+ const next = this.content[this.i + 2]
+ if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+ // 标签结尾
+ this.i += 2
+ this.start = this.i
+ this.state = this.endTag
+ return
+ }
+ // 处理注释
+ let end = '-->'
+ if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
+ end = '>'
+ }
+ this.i = this.content.indexOf(end, this.i)
+ if (this.i !== -1) {
+ this.i += end.length
+ this.start = this.i
+ }
+ } else {
+ this.i++
+ }
+}
+
+/**
+ * @description 标签名状态
+ * @private
+ */
+Lexer.prototype.tagName = function () {
+ if (blankChar[this.content[this.i]]) {
+ // 解析到标签名
+ this.handler.onTagName(this.content.substring(this.start, this.i))
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < this.content.length && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+ } else if (!this.checkClose('onTagName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性名状态
+ * @private
+ */
+Lexer.prototype.attrName = function () {
+ let c = this.content[this.i]
+ if (blankChar[c] || c === '=') {
+ // 解析到属性名
+ this.handler.onAttrName(this.content.substring(this.start, this.i))
+ let needVal = c === '='
+ const len = this.content.length
+ while (++this.i < len) {
+ c = this.content[this.i]
+ if (!blankChar[c]) {
+ if (this.checkClose()) return
+ if (needVal) {
+ // 等号后遇到第一个非空字符
+ this.start = this.i
+ this.state = this.attrVal
+ return
+ }
+ if (this.content[this.i] === '=') {
+ needVal = true
+ } else {
+ this.start = this.i
+ this.state = this.attrName
+ return
+ }
+ }
+ }
+ } else if (!this.checkClose('onAttrName')) {
+ this.i++
+ }
+}
+
+/**
+ * @description 属性值状态
+ * @private
+ */
+Lexer.prototype.attrVal = function () {
+ const c = this.content[this.i]
+ const len = this.content.length
+ if (c === '"' || c === "'") {
+ // 有冒号的属性
+ this.start = ++this.i
+ this.i = this.content.indexOf(c, this.i)
+ if (this.i === -1) return
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ } else {
+ // 没有冒号的属性
+ for (; this.i < len; this.i++) {
+ if (blankChar[this.content[this.i]]) {
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
+ break
+ } else if (this.checkClose('onAttrVal')) return
+ }
+ }
+ while (blankChar[this.content[++this.i]]);
+ if (this.i < len && !this.checkClose()) {
+ this.start = this.i
+ this.state = this.attrName
+ }
+}
+
+/**
+ * @description 结束标签状态
+ * @returns {String} 结束的标签名
+ * @private
+ */
+Lexer.prototype.endTag = function () {
+ const c = this.content[this.i]
+ if (blankChar[c] || c === '>' || c === '/') {
+ this.handler.onCloseTag(this.content.substring(this.start, this.i))
+ if (c !== '>') {
+ this.i = this.content.indexOf('>', this.i)
+ if (this.i === -1) return
+ }
+ this.start = ++this.i
+ this.state = this.text
+ } else {
+ this.i++
+ }
+}
+
+export default Parser
diff --git a/uni_modules/uv-parse/components/uv-parse/uv-parse.vue b/uni_modules/uv-parse/components/uv-parse/uv-parse.vue
new file mode 100644
index 0000000..2aa9837
--- /dev/null
+++ b/uni_modules/uv-parse/components/uv-parse/uv-parse.vue
@@ -0,0 +1,498 @@
+<template>
+ <view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
+ <slot v-if="!nodes[0]" />
+ <!-- #ifndef APP-PLUS-NVUE -->
+ <node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]" name="span" />
+ <!-- #endif -->
+ <!-- #ifdef APP-PLUS-NVUE -->
+ <web-view ref="web" src="/uni_modules/uv-parse/static/app-plus/uv-parse/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
+ <!-- #endif -->
+ </view>
+</template>
+
+<script>
+/**
+ * uv-parse v1.0.3
+ * @description 富文本组件
+ * @tutorial https://www.uvui.cn/components/parse.html
+ * @property {String} container-style 容器的样式
+ * @property {String} content 用于渲染的 html 字符串
+ * @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
+ * @property {String} domain 主域名,用于拼接链接
+ * @property {String} error-img 图片出错时的占位图链接
+ * @property {Boolean} lazy-load 是否开启图片懒加载
+ * @property {string} loading-img 图片加载过程中的占位图链接
+ * @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
+ * @property {Boolean} preview-img 是否允许图片被点击时自动预览
+ * @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
+ * @property {Boolean | String} selectable 是否开启长按复制
+ * @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
+ * @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
+ * @property {Object} tag-style 标签的默认样式
+ * @property {Boolean | Number} use-anchor 是否使用锚点链接
+ * @event {Function} load dom 结构加载完毕时触发
+ * @event {Function} ready 所有图片加载完毕时触发
+ * @event {Function} imgtap 图片被点击时触发
+ * @event {Function} linktap 链接被点击时触发
+ * @event {Function} play 音视频播放时触发
+ * @event {Function} error 媒体加载出错时触发
+ */
+// #ifndef APP-PLUS-NVUE
+import node from './node/node'
+// #endif
+import Parser from './parser'
+const plugins=[]
+// #ifdef APP-PLUS-NVUE
+const dom = weex.requireModule('dom')
+// #endif
+export default {
+ name: 'uv-parse',
+ data () {
+ return {
+ nodes: [],
+ // #ifdef APP-PLUS-NVUE
+ height: 3
+ // #endif
+ }
+ },
+ props: {
+ containerStyle: {
+ type: String,
+ default: ''
+ },
+ content: {
+ type: String,
+ default: ''
+ },
+ copyLink: {
+ type: [Boolean, String],
+ default: true
+ },
+ domain: String,
+ errorImg: {
+ type: String,
+ default: ''
+ },
+ lazyLoad: {
+ type: [Boolean, String],
+ default: false
+ },
+ loadingImg: {
+ type: String,
+ default: ''
+ },
+ pauseVideo: {
+ type: [Boolean, String],
+ default: true
+ },
+ previewImg: {
+ type: [Boolean, String],
+ default: true
+ },
+ scrollTable: [Boolean, String],
+ selectable: [Boolean, String],
+ setTitle: {
+ type: [Boolean, String],
+ default: true
+ },
+ showImgMenu: {
+ type: [Boolean, String],
+ default: true
+ },
+ tagStyle: Object,
+ useAnchor: [Boolean, Number]
+ },
+ // #ifdef VUE3
+ emits: ['load', 'ready', 'imgtap', 'linktap', 'play', 'error'],
+ // #endif
+ // #ifndef APP-PLUS-NVUE
+ components: {
+ node
+ },
+ // #endif
+ watch: {
+ content (content) {
+ this.setContent(content)
+ }
+ },
+ created () {
+ this.plugins = []
+ for (let i = plugins.length; i--;) {
+ this.plugins.push(new plugins[i](this))
+ }
+ },
+ mounted () {
+ if (this.content && !this.nodes.length) {
+ this.setContent(this.content)
+ }
+ },
+ beforeDestroy () {
+ this._hook('onDetached')
+ },
+ methods: {
+ /**
+ * @description 将锚点跳转的范围限定在一个 scroll-view 内
+ * @param {Object} page scroll-view 所在页面的示例
+ * @param {String} selector scroll-view 的选择器
+ * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
+ */
+ in (page, selector, scrollTop) {
+ // #ifndef APP-PLUS-NVUE
+ if (page && selector && scrollTop) {
+ this._in = {
+ page,
+ selector,
+ scrollTop
+ }
+ }
+ // #endif
+ },
+
+ /**
+ * @description 锚点跳转
+ * @param {String} id 要跳转的锚点 id
+ * @param {Number} offset 跳转位置的偏移量
+ * @returns {Promise}
+ */
+ navigateTo (id, offset) {
+ return new Promise((resolve, reject) => {
+ if (!this.useAnchor) {
+ reject(Error('Anchor is disabled'))
+ return
+ }
+ offset = offset || parseInt(this.useAnchor) || 0
+ // #ifdef APP-PLUS-NVUE
+ if (!id) {
+ dom.scrollToElement(this.$refs.web, {
+ offset
+ })
+ resolve()
+ } else {
+ this._navigateTo = {
+ resolve,
+ reject,
+ offset
+ }
+ this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})')
+ }
+ // #endif
+ // #ifndef APP-PLUS-NVUE
+ let deep = ' '
+ // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
+ deep = '>>>'
+ // #endif
+ const selector = uni.createSelectorQuery()
+ // #ifndef MP-ALIPAY
+ .in(this._in ? this._in.page : this)
+ // #endif
+ .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect()
+ if (this._in) {
+ selector.select(this._in.selector).scrollOffset()
+ .select(this._in.selector).boundingClientRect()
+ } else {
+ // 获取 scroll-view 的位置和滚动距离
+ selector.selectViewport().scrollOffset() // 获取窗口的滚动距离
+ }
+ selector.exec(res => {
+ if (!res[0]) {
+ reject(Error('Label not found'))
+ return
+ }
+ const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
+ if (this._in) {
+ // scroll-view 跳转
+ this._in.page[this._in.scrollTop] = scrollTop
+ } else {
+ // 页面跳转
+ uni.pageScrollTo({
+ scrollTop,
+ duration: 300
+ })
+ }
+ resolve()
+ })
+ // #endif
+ })
+ },
+
+ /**
+ * @description 获取文本内容
+ * @return {String}
+ */
+ getText (nodes) {
+ let text = '';
+ (function traversal (nodes) {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i]
+ if (node.type === 'text') {
+ text += node.text.replace(/&/g, '&')
+ } else if (node.name === 'br') {
+ text += '\n'
+ } else {
+ // 块级标签前后加换行
+ const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
+ if (isBlock && text && text[text.length - 1] !== '\n') {
+ text += '\n'
+ }
+ // 递归获取子节点的文本
+ if (node.children) {
+ traversal(node.children)
+ }
+ if (isBlock && text[text.length - 1] !== '\n') {
+ text += '\n'
+ } else if (node.name === 'td' || node.name === 'th') {
+ text += '\t'
+ }
+ }
+ }
+ })(nodes || this.nodes)
+ return text
+ },
+
+ /**
+ * @description 获取内容大小和位置
+ * @return {Promise}
+ */
+ getRect () {
+ return new Promise((resolve, reject) => {
+ uni.createSelectorQuery()
+ // #ifndef MP-ALIPAY
+ .in(this)
+ // #endif
+ .select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found')))
+ })
+ },
+
+ /**
+ * @description 暂停播放媒体
+ */
+ pauseMedia () {
+ for (let i = (this._videos || []).length; i--;) {
+ this._videos[i].pause()
+ }
+ // #ifdef APP-PLUS
+ const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
+ // #ifndef APP-PLUS-NVUE
+ let page = this.$parent
+ while (!page.$scope) page = page.$parent
+ page.$scope.$getAppWebview().evalJS(command)
+ // #endif
+ // #ifdef APP-PLUS-NVUE
+ this.$refs.web.evalJs(command)
+ // #endif
+ // #endif
+ },
+
+ /**
+ * @description 设置媒体播放速率
+ * @param {Number} rate 播放速率
+ */
+ setPlaybackRate (rate) {
+ this.playbackRate = rate
+ for (let i = (this._videos || []).length; i--;) {
+ this._videos[i].playbackRate(rate)
+ }
+ // #ifdef APP-PLUS
+ const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
+ // #ifndef APP-PLUS-NVUE
+ let page = this.$parent
+ while (!page.$scope) page = page.$parent
+ page.$scope.$getAppWebview().evalJS(command)
+ // #endif
+ // #ifdef APP-PLUS-NVUE
+ this.$refs.web.evalJs(command)
+ // #endif
+ // #endif
+ },
+
+ /**
+ * @description 设置内容
+ * @param {String} content html 内容
+ * @param {Boolean} append 是否在尾部追加
+ */
+ setContent (content, append) {
+ if (!append || !this.imgList) {
+ this.imgList = []
+ }
+ const nodes = new Parser(this).parse(content)
+ // #ifdef APP-PLUS-NVUE
+ if (this._ready) {
+ this._set(nodes, append)
+ }
+ // #endif
+ this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
+
+ // #ifndef APP-PLUS-NVUE
+ this._videos = []
+ this.$nextTick(() => {
+ this._hook('onLoad')
+ this.$emit('load')
+ })
+
+ if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
+ // 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
+ let height = 0
+ const callback = rect => {
+ if (!rect || !rect.height) rect = {}
+ // 350ms 总高度无变化就触发 ready 事件
+ if (rect.height === height) {
+ this.$emit('ready', rect)
+ } else {
+ height = rect.height
+ setTimeout(() => {
+ this.getRect().then(callback).catch(callback)
+ }, 350)
+ }
+ }
+ this.getRect().then(callback).catch(callback)
+ } else {
+ // 未设置懒加载,等待所有图片加载完毕
+ if (!this.imgList._unloadimgs) {
+ this.getRect().then(rect => {
+ this.$emit('ready', rect)
+ }).catch(() => {
+ this.$emit('ready', {})
+ })
+ }
+ }
+ // #endif
+ },
+
+ /**
+ * @description 调用插件钩子函数
+ */
+ _hook (name) {
+ for (let i = plugins.length; i--;) {
+ if (this.plugins[i][name]) {
+ this.plugins[i][name]()
+ }
+ }
+ },
+
+ // #ifdef APP-PLUS-NVUE
+ /**
+ * @description 设置内容
+ */
+ _set (nodes, append) {
+ this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes).replace(/%22/g, '') + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
+ },
+
+ /**
+ * @description 接收到 web-view 消息
+ */
+ _onMessage (e) {
+ const message = e.detail.data[0]
+ switch (message.action) {
+ // web-view 初始化完毕
+ case 'onJSBridgeReady':
+ this._ready = true
+ if (this.nodes) {
+ this._set(this.nodes)
+ }
+ break
+ // 内容 dom 加载完毕
+ case 'onLoad':
+ this.height = message.height
+ this._hook('onLoad')
+ this.$emit('load')
+ break
+ // 所有图片加载完毕
+ case 'onReady':
+ this.getRect().then(res => {
+ this.$emit('ready', res)
+ }).catch(() => {
+ this.$emit('ready', {})
+ })
+ break
+ // 总高度发生变化
+ case 'onHeightChange':
+ this.height = message.height
+ break
+ // 图片点击
+ case 'onImgTap':
+ this.$emit('imgtap', message.attrs)
+ if (this.previewImg) {
+ uni.previewImage({
+ current: parseInt(message.attrs.i),
+ urls: this.imgList
+ })
+ }
+ break
+ // 链接点击
+ case 'onLinkTap': {
+ const href = message.attrs.href
+ this.$emit('linktap', message.attrs)
+ if (href) {
+ // 锚点跳转
+ if (href[0] === '#') {
+ if (this.useAnchor) {
+ dom.scrollToElement(this.$refs.web, {
+ offset: message.offset
+ })
+ }
+ } else if (href.includes('://')) {
+ // 打开外链
+ if (this.copyLink) {
+ plus.runtime.openWeb(href)
+ }
+ } else {
+ uni.navigateTo({
+ url: href,
+ fail () {
+ uni.switchTab({
+ url: href
+ })
+ }
+ })
+ }
+ }
+ break
+ }
+ case 'onPlay':
+ this.$emit('play')
+ break
+ // 获取到锚点的偏移量
+ case 'getOffset':
+ if (typeof message.offset === 'number') {
+ dom.scrollToElement(this.$refs.web, {
+ offset: message.offset + this._navigateTo.offset
+ })
+ this._navigateTo.resolve()
+ } else {
+ this._navigateTo.reject(Error('Label not found'))
+ }
+ break
+ // 点击
+ case 'onClick':
+ this.$emit('tap')
+ this.$emit('click')
+ break
+ // 出错
+ case 'onError':
+ this.$emit('error', {
+ source: message.source,
+ attrs: message.attrs
+ })
+ }
+ }
+ // #endif
+ }
+}
+</script>
+
+<style>
+/* #ifndef APP-PLUS-NVUE */
+/* 根节点样式 */
+._root {
+ padding: 1px 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+ -webkit-overflow-scrolling: touch;
+}
+
+/* 长按复制 */
+._select {
+ user-select: text;
+}
+/* #endif */
+</style>
diff --git a/uni_modules/uv-parse/package.json b/uni_modules/uv-parse/package.json
new file mode 100644
index 0000000..834499e
--- /dev/null
+++ b/uni_modules/uv-parse/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-parse",
+ "displayName": "uv-parse 富文本解析器 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.4",
+ "description": "uv-parse 该组件一般用于富文本解析场景,比如解析文章内容,商品详情,带原生HTML标签的各类字符串等,此组件和uni-app官方的rich-text组件功能有重合之处,但是也有不同的地方。",
+ "keywords": [
+ "uv-parse",
+ "uvui",
+ "uv-ui",
+ "parse",
+ "富文本"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-parse/readme.md b/uni_modules/uv-parse/readme.md
new file mode 100644
index 0000000..78737b1
--- /dev/null
+++ b/uni_modules/uv-parse/readme.md
@@ -0,0 +1,21 @@
+## Parse 富文本解析器
+
+> **组件名:uv-parse**
+
+该组件一般用于富文本解析场景,比如解析文章内容,商品详情,带原生`HTML`标签的各类字符串等,此组件和`uni-app`官方的`rich-text`组件功能有重合之处,但是也有不同的地方。
+
+该插件只提供富文本的解析,该功能已经足够丰富。如果需要富文本的编辑,可使用`uniapp`官方提供的组件。
+
+# <a href="https://www.uvui.cn/components/parse.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js b/uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js
new file mode 100644
index 0000000..9283832
--- /dev/null
+++ b/uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js
@@ -0,0 +1,224 @@
+'use strict'
+
+// 等待初始化完毕
+document.addEventListener('UniAppJSBridgeReady', () => {
+ document.body.onclick = function () {
+ return uni.postMessage({
+ data: {
+ action: 'onClick'
+ }
+ })
+ }
+
+ uni.postMessage({
+ data: {
+ action: 'onJSBridgeReady'
+ }
+ })
+})
+let options
+let medias = []
+/**
+ * @description 获取标签的所有属性
+ * @param {Element} ele
+ */
+
+function getAttrs(ele) {
+ const attrs = Object.create(null)
+
+ for (let i = ele.attributes.length; i--;) {
+ attrs[ele.attributes[i].name] = ele.attributes[i].value
+ }
+
+ return attrs
+}
+/**
+ * @description 图片加载出错
+ */
+
+function onImgError() {
+ if (options[1]) {
+ this.src = options[1]
+ this.onerror = null
+ } // 取消监听点击
+
+ this.onclick = null
+ this.ontouchstart = null
+ uni.postMessage({
+ data: {
+ action: 'onError',
+ source: 'img',
+ attrs: getAttrs(this)
+ }
+ })
+}
+/**
+ * @description 创建 dom 结构
+ * @param {object[]} nodes 节点数组
+ * @param {Element} parent 父节点
+ * @param {string} namespace 命名空间
+ */
+
+function createDom(nodes, parent, namespace) {
+ const _loop = function _loop(i) {
+ const node = nodes[i]
+ let ele = void 0
+
+ if (!node.type || node.type == 'node') {
+ let { name } = node // svg 需要设置 namespace
+
+ if (name == 'svg') namespace = 'http://www.w3.org/2000/svg'
+ if (name == 'html' || name == 'body') name = 'div' // 创建标签
+
+ if (!namespace) ele = document.createElement(name); else ele = document.createElementNS(namespace, name) // 设置属性
+
+ for (const item in node.attrs) {
+ ele.setAttribute(item, node.attrs[item])
+ } // 递归创建子节点
+
+ if (node.children) createDom(node.children, ele, namespace) // 处理图片
+
+ if (name == 'img') {
+ if (!ele.src && ele.getAttribute('data-src')) ele.src = ele.getAttribute('data-src')
+
+ if (!node.attrs.ignore) {
+ // 监听图片点击事件
+ ele.onclick = function (e) {
+ e.stopPropagation()
+ uni.postMessage({
+ data: {
+ action: 'onImgTap',
+ attrs: getAttrs(this)
+ }
+ })
+ }
+ }
+
+ if (options[2]) {
+ image = new Image()
+ image.src = ele.src
+ ele.src = options[2]
+
+ image.onload = function () {
+ ele.src = this.src
+ }
+
+ image.onerror = function () {
+ ele.onerror()
+ }
+ }
+
+ ele.onerror = onImgError
+ } // 处理链接
+ else if (name == 'a') {
+ ele.addEventListener('click', function (e) {
+ e.stopPropagation()
+ e.preventDefault() // 阻止默认跳转
+
+ const href = this.getAttribute('href')
+ let offset
+ if (href && href[0] == '#') offset = (document.getElementById(href.substr(1)) || {}).offsetTop
+ uni.postMessage({
+ data: {
+ action: 'onLinkTap',
+ attrs: getAttrs(this),
+ offset
+ }
+ })
+ }, true)
+ } // 处理音视频
+ else if (name == 'video' || name == 'audio') {
+ medias.push(ele)
+
+ if (!node.attrs.autoplay) {
+ if (!node.attrs.controls) ele.setAttribute('controls', 'true') // 空白图占位
+
+ if (!node.attrs.poster) ele.setAttribute('poster', "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>")
+ }
+
+ if (options[3]) {
+ ele.onplay = function () {
+ for (let _i = 0; _i < medias.length; _i++) {
+ if (medias[_i] != this) medias[_i].pause()
+ }
+ }
+ }
+
+ ele.onerror = function () {
+ uni.postMessage({
+ data: {
+ action: 'onError',
+ source: name,
+ attrs: getAttrs(this)
+ }
+ })
+ }
+ } // 处理表格
+ else if (name == 'table' && options[4] && !ele.style.cssText.includes('inline')) {
+ const div = document.createElement('div')
+ div.style.overflow = 'auto'
+ div.appendChild(ele)
+ ele = div
+ } else if (name == 'svg') namespace = void 0
+ } else ele = document.createTextNode(node.text.replace(/&/g, '&'))
+
+ parent.appendChild(ele)
+ }
+
+ for (let i = 0; i < nodes.length; i++) {
+ var image
+
+ _loop(i)
+ }
+} // 设置 html 内容
+
+window.setContent = function (nodes, opts, append) {
+ const ele = document.getElementById('content') // 背景颜色
+
+ if (opts[0]) document.body.bgColor = opts[0] // 长按复制
+
+ if (!opts[5]) ele.style.userSelect = 'none'
+
+ if (!append) {
+ ele.innerHTML = '' // 不追加则先清空
+
+ medias = []
+ }
+
+ options = opts
+ const fragment = document.createDocumentFragment()
+ createDom(nodes, fragment)
+ ele.appendChild(fragment) // 触发事件
+
+ let height = ele.scrollHeight
+ uni.postMessage({
+ data: {
+ action: 'onLoad',
+ height
+ }
+ })
+ clearInterval(window.timer)
+ let ready = false
+ window.timer = setInterval(() => {
+ if (ele.scrollHeight != height) {
+ height = ele.scrollHeight
+ uni.postMessage({
+ data: {
+ action: 'onHeightChange',
+ height
+ }
+ })
+ } else if (!ready) {
+ ready = true
+ uni.postMessage({
+ data: {
+ action: 'onReady'
+ }
+ })
+ }
+ }, 350)
+} // 回收计时器
+
+window.onunload = function () {
+ clearInterval(window.timer)
+}
diff --git a/uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js b/uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js
new file mode 100644
index 0000000..518332d
--- /dev/null
+++ b/uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js
@@ -0,0 +1,19 @@
+!(function (e, n) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = n() : typeof define === 'function' && define.amd ? define(n) : (e = e || self).uni = n() }(this, (() => {
+ 'use strict'
+
+ try { const e = {}; Object.defineProperty(e, 'passive', { get() { !0 } }), window.addEventListener('test-passive', null, e) } catch (e) {} const n = Object.prototype.hasOwnProperty; function t(e, t) { return n.call(e, t) } const i = []; const a = function (e, n) { const t = { options: { timestamp: +new Date() }, name: e, arg: n }; if (window.__dcloud_weex_postMessage || window.__dcloud_weex_) { if (e === 'postMessage') { const a = { data: [n] }; return window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessage(a) : window.__dcloud_weex_.postMessage(JSON.stringify(a)) } const o = { type: 'WEB_INVOKE_APPSERVICE', args: { data: t, webviewIds: i } }; window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessageToService(o) : window.__dcloud_weex_.postMessageToService(JSON.stringify(o)) } if (!window.plus) return window.parent.postMessage({ type: 'WEB_INVOKE_APPSERVICE', data: t, pageId: '' }, '*'); if (i.length === 0) { const r = plus.webview.currentWebview(); if (!r) throw new Error('plus.webview.currentWebview() is undefined'); const d = r.parent(); let s = ''; s = d ? d.id : r.id, i.push(s) } if (plus.webview.getWebviewById('__uniapp__service'))plus.webview.postMessageToUniNView({ type: 'WEB_INVOKE_APPSERVICE', args: { data: t, webviewIds: i } }, '__uniapp__service'); else { const w = JSON.stringify(t); plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat('WEB_INVOKE_APPSERVICE', '",').concat(w, ',').concat(JSON.stringify(i), ');')) } }; const o = {
+ navigateTo() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('navigateTo', { url: encodeURI(n) }) }, navigateBack() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.delta; a('navigateBack', { delta: parseInt(n) || 1 }) }, switchTab() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('switchTab', { url: encodeURI(n) }) }, reLaunch() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('reLaunch', { url: encodeURI(n) }) }, redirectTo() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('redirectTo', { url: encodeURI(n) }) }, getEnv(e) { window.plus ? e({ plus: !0 }) : e({ h5: !0 }) }, postMessage() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; a('postMessage', e.data || {}) }
+ }; const r = /uni-app/i.test(navigator.userAgent); const d = /Html5Plus/i.test(navigator.userAgent); const s = /complete|loaded|interactive/; const w = window.my && navigator.userAgent.indexOf('AlipayClient') > -1; const u = window.swan && window.swan.webView && /swan/i.test(navigator.userAgent); const c = window.qq && window.qq.miniProgram && /QQ/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent); const g = window.tt && window.tt.miniProgram && /toutiaomicroapp/i.test(navigator.userAgent); const v = window.wx && window.wx.miniProgram && /micromessenger/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent); const p = window.qa && /quickapp/i.test(navigator.userAgent); for (var l, _ = function () { window.UniAppJSBridge = !0, document.dispatchEvent(new CustomEvent('UniAppJSBridgeReady', { bubbles: !0, cancelable: !0 })) }, f = [function (e) { if (r || d) return window.__dcloud_weex_postMessage || window.__dcloud_weex_ ? document.addEventListener('DOMContentLoaded', e) : window.plus && s.test(document.readyState) ? setTimeout(e, 0) : document.addEventListener('plusready', e), o }, function (e) { if (v) return window.WeixinJSBridge && window.WeixinJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('WeixinJSBridgeReady', e), window.wx.miniProgram }, function (e) { if (c) return window.QQJSBridge && window.QQJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('QQJSBridgeReady', e), window.qq.miniProgram }, function (e) {
+ if (w) {
+ document.addEventListener('DOMContentLoaded', e); const n = window.my; return {
+ navigateTo: n.navigateTo, navigateBack: n.navigateBack, switchTab: n.switchTab, reLaunch: n.reLaunch, redirectTo: n.redirectTo, postMessage: n.postMessage, getEnv: n.getEnv
+ }
+ }
+ }, function (e) { if (u) return document.addEventListener('DOMContentLoaded', e), window.swan.webView }, function (e) { if (g) return document.addEventListener('DOMContentLoaded', e), window.tt.miniProgram }, function (e) {
+ if (p) {
+ window.QaJSBridge && window.QaJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('QaJSBridgeReady', e); const n = window.qa; return {
+ navigateTo: n.navigateTo, navigateBack: n.navigateBack, switchTab: n.switchTab, reLaunch: n.reLaunch, redirectTo: n.redirectTo, postMessage: n.postMessage, getEnv: n.getEnv
+ }
+ }
+ }, function (e) { return document.addEventListener('DOMContentLoaded', e), o }], m = 0; m < f.length && !(l = f[m](_)); m++);l || (l = {}); const E = typeof uni !== 'undefined' ? uni : {}; if (!E.navigateTo) for (const b in l)t(l, b) && (E[b] = l[b]); return E.webView = l, E
+})))
diff --git a/uni_modules/uv-parse/static/app-plus/uv-parse/local.html b/uni_modules/uv-parse/static/app-plus/uv-parse/local.html
new file mode 100644
index 0000000..f78d703
--- /dev/null
+++ b/uni_modules/uv-parse/static/app-plus/uv-parse/local.html
@@ -0,0 +1 @@
+<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>body,html{width:100%;height:100%;overflow:hidden}body{margin:0}video{width:300px;height:225px}img{max-width:100%;-webkit-touch-callout:none}@keyframes show{0%{opacity:0}100%{opacity:1}}</style></head><body><div id="content"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>
\ No newline at end of file
diff --git a/uni_modules/uv-pick-color/changelog.md b/uni_modules/uv-pick-color/changelog.md
new file mode 100644
index 0000000..7788c12
--- /dev/null
+++ b/uni_modules/uv-pick-color/changelog.md
@@ -0,0 +1,18 @@
+## 1.0.8(2023-08-14)
+1. 取消scrollTop参数,使用clientY
+## 1.0.7(2023-08-13)
+1. 增加scrollTop参数,设置滚动条的位置。不设置如果页面出现滚动就需要传该值,会出现颜色面板无法进行选颜色的情况。
+## 1.0.6(2023-08-04)
+1. 修复颜色值错误的BUG
+## 1.0.5(2023-08-02)
+1. 更新依赖
+## 1.0.4(2023-07-02)
+uv-pick-color 由于弹出层uv-popup的修改,打开和关闭方法更改,详情参考文档:https://www.uvui.cn/components/pickColor.html
+## 1.0.3(2023-06-12)
+1. 修复在选择颜色时候百度小程序报错的BUG
+## 1.0.2(2023-05-27)
+1. 兼容非移动h5端不能选择的BUG
+## 1.0.1(2023-05-24)
+1. 去掉template中存在的this.导致头条小程序编译警告
+## 1.0.0(2023-05-23)
+uv-pick-color 新增颜色选择器
diff --git a/uni_modules/uv-pick-color/components/uv-pick-color/colors.js b/uni_modules/uv-pick-color/components/uv-pick-color/colors.js
new file mode 100644
index 0000000..cf804b2
--- /dev/null
+++ b/uni_modules/uv-pick-color/components/uv-pick-color/colors.js
@@ -0,0 +1,217 @@
+/**
+ * hsb 转 rgb
+ * @param {Object} hsb 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度
+ */
+export function hsbToRgb(hsb) {
+ let rgb = {};
+ let h = hsb.h;
+ let s = hsb.s * 255 / 100;
+ let v = hsb.b * 255 / 100;
+ if (s == 0) {
+ rgb.r = rgb.g = rgb.b = v;
+ } else {
+ let t1 = v;
+ let t2 = ((255 - s) * v) / 255;
+ let t3 = ((t1 - t2) * (h % 60)) / 60;
+ if (h == 360) h = 0;
+ if (h < 60) {
+ rgb.r = t1;
+ rgb.b = t2;
+ rgb.g = t2 + t3;
+ } else if (h < 120) {
+ rgb.g = t1;
+ rgb.b = t2;
+ rgb.r = t1 - t3;
+ } else if (h < 180) {
+ rgb.g = t1;
+ rgb.r = t2;
+ rgb.b = t2 + t3;
+ } else if (h < 240) {
+ rgb.b = t1;
+ rgb.r = t2;
+ rgb.g = t1 - t3;
+ } else if (h < 300) {
+ rgb.b = t1;
+ rgb.g = t2;
+ rgb.r = t2 + t3;
+ } else if (h < 360) {
+ rgb.r = t1;
+ rgb.g = t2;
+ rgb.b = t1 - t3;
+ } else {
+ rgb.r = 0;
+ rgb.g = 0;
+ rgb.b = 0;
+ }
+ }
+ return {
+ r: Math.round(rgb.r),
+ g: Math.round(rgb.g),
+ b: Math.round(rgb.b)
+ };
+}
+/**
+ * rgb转hsb
+ * @param {Object} rgb 颜色rgb值
+ */
+export function rgbToHsb(rgb) {
+ let hsb = {
+ h: 0,
+ s: 0,
+ b: 0
+ };
+ let h = 0,
+ s = 0,
+ v = 0;
+ let r = rgb.r,
+ g = rgb.g,
+ b = rgb.b;
+ let min = Math.min(rgb.r, rgb.g, rgb.b);
+ let max = Math.max(rgb.r, rgb.g, rgb.b);
+ v = max / 255;
+ if (max === 0) {
+ s = 0;
+ } else {
+ s = 1 - (min / max);
+ }
+ if (max === min) {
+ h = 0; //事实上,max===min的时候,h无论为多少都无所谓
+ } else if (max === r && g >= b) {
+ h = 60 * ((g - b) / (max - min)) + 0;
+ } else if (max === r && g < b) {
+ h = 60 * ((g - b) / (max - min)) + 360
+ } else if (max === g) {
+ h = 60 * ((b - r) / (max - min)) + 120
+ } else if (max === b) {
+ h = 60 * ((r - g) / (max - min)) + 240
+ }
+ hsb.h = parseInt(h);
+ hsb.s = parseInt(s * 100);
+ hsb.b = parseInt(v * 100);
+ return hsb;
+}
+/**
+ * rgb 转 二进制 hex
+ * @param {Object} rgb
+ */
+export function rgbToHex(rgb) {
+ let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
+ hex.map(function(str, i) {
+ if (str.length == 1) {
+ hex[i] = '0' + str;
+ }
+ });
+ return hex.join('');
+}
+//预制颜色
+export const colorList = [{
+ r: 60,
+ g: 156,
+ b: 255,
+ a: 1
+}, {
+ r: 245,
+ g: 108,
+ b: 108,
+ a: 1
+}, {
+ r: 249,
+ g: 174,
+ b: 61,
+ a: 1
+}, {
+ r: 90,
+ g: 199,
+ b: 37,
+ a: 1
+}, {
+ r: 144,
+ g: 147,
+ b: 153,
+ a: 1
+}, {
+ r: 48,
+ g: 49,
+ b: 51,
+ a: 1
+}, {
+ r: 233,
+ g: 30,
+ b: 99,
+ a: 1
+}, {
+ r: 156,
+ g: 39,
+ b: 176,
+ a: 1
+}, {
+ r: 103,
+ g: 58,
+ b: 183,
+ a: 1
+}, {
+ r: 63,
+ g: 81,
+ b: 181,
+ a: 1
+}, {
+ r: 0,
+ g: 188,
+ b: 212,
+ a: 1
+}, {
+ r: 0,
+ g: 150,
+ b: 136,
+ a: 1
+}, {
+ r: 139,
+ g: 195,
+ b: 74,
+ a: 1
+}, {
+ r: 205,
+ g: 220,
+ b: 57,
+ a: 1
+}, {
+ r: 255,
+ g: 235,
+ b: 59,
+ a: 1
+}, {
+ r: 255,
+ g: 193,
+ b: 7,
+ a: 1
+}, {
+ r: 255,
+ g: 152,
+ b: 0,
+ a: 1
+}, {
+ r: 255,
+ g: 87,
+ b: 34,
+ a: 1
+}, {
+ r: 121,
+ g: 85,
+ b: 72,
+ a: 1
+}, {
+ r: 158,
+ g: 158,
+ b: 158,
+ a: 1
+}, {
+ r: 0,
+ g: 0,
+ b: 0,
+ a: 0.5
+}, {
+ r: 0,
+ g: 0,
+ b: 0,
+ a: 0
+}]
\ No newline at end of file
diff --git a/uni_modules/uv-pick-color/components/uv-pick-color/props.js b/uni_modules/uv-pick-color/components/uv-pick-color/props.js
new file mode 100644
index 0000000..a57f063
--- /dev/null
+++ b/uni_modules/uv-pick-color/components/uv-pick-color/props.js
@@ -0,0 +1,47 @@
+export default {
+ props: {
+ // 颜色选择器初始颜色
+ color: {
+ type: Object,
+ default: () => {
+ return { r: 0, g: 0, b: 0, a: 0 }
+ }
+ },
+ // 预制颜色
+ prefabColor: {
+ type: Array,
+ default: () => []
+ },
+ // 是否允许点击遮罩关闭
+ closeOnClickOverlay: {
+ type: Boolean,
+ default: true
+ },
+ // 顶部标题
+ title: {
+ type: String,
+ default: ''
+ },
+ // 取消按钮的文字
+ cancelText: {
+ type: String,
+ default: '取消'
+ },
+ // 确认按钮的文字
+ confirmText: {
+ type: String,
+ default: '确定'
+ },
+ // 取消按钮的颜色
+ cancelColor: {
+ type: String,
+ default: '#909193'
+ },
+ // 确认按钮的颜色
+ confirmColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ ...uni.$uv?.props?.pickColor
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue b/uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue
new file mode 100644
index 0000000..f283a92
--- /dev/null
+++ b/uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue
@@ -0,0 +1,551 @@
+<template>
+ <!-- #ifndef APP-NVUE -->
+ <uv-popup
+ ref="pickerColorPopup"
+ mode="bottom"
+ :close-on-click-overlay="closeOnClickOverlay"
+ @change="popupChange">
+ <view class="uv-pick-color">
+ <uv-toolbar
+ :show="showToolbar"
+ :cancelColor="cancelColor"
+ :confirmColor="confirmColor"
+ :cancelText="cancelText"
+ :confirmText="confirmText"
+ :title="title"
+ :show-border="true"
+ @cancel="cancelHandler"
+ @confirm="confirmHandler"></uv-toolbar>
+ <view
+ class="uv-pick-color__box"
+ :style="{
+ background:`rgb(${bgcolor.r},${bgcolor.g},${bgcolor.b})`
+ }"
+ >
+ <!-- #ifdef H5 -->
+ <view
+ class="uv-pick-color__box__bg drag-box"
+ @tap.stop.prevent="touchstart($event, 0)"
+ @touchstart.stop.prevent="touchstart($event, 0)"
+ @touchmove.stop.prevent="touchmove($event, 0)"
+ @touchend.stop.prevent="touchend($event, 0)"
+ >
+ <!-- #endif -->
+ <!-- #ifndef H5 -->
+ <view
+ class="uv-pick-color__box__bg drag-box"
+ @touchstart.stop.prevent="touchstart($event, 0)"
+ @touchmove.stop.prevent="touchmove($event, 0)"
+ @touchend.stop.prevent="touchend($event, 0)"
+ >
+ <!-- #endif -->
+ <view class="uv-pick-color__box__bg-mask"></view>
+ <view
+ class="uv-pick-color__box__bg-pointer"
+ :style="[pointerStyle]"
+ >
+ </view>
+ </view>
+ </view>
+ <view class="uv-pick-color__control">
+ <view class="uv-pick-color__control__alpha">
+ <view
+ class="uv-pick-color__control__alpha--color"
+ :style="{ background:`rgba(${rgba.r},${rgba.g},${rgba.b},${rgba.a})` }"
+ ></view>
+ </view>
+ <view class="uv-pick-color__control__item">
+ <!-- #ifdef H5 -->
+ <view
+ class="uv-pick-color__control__item__drag drag-box"
+ @tap.stop="touchstart($event, 1)"
+ @touchstart.stop="touchstart($event, 1)"
+ @touchmove.stop="touchmove($event, 1)"
+ @touchend.stop="touchend($event, 1)"
+ >
+ <!-- #endif -->
+ <!-- #ifndef H5 -->
+ <view
+ class="uv-pick-color__control__item__drag drag-box"
+ @touchstart.stop="touchstart($event, 1)"
+ @touchmove.stop="touchmove($event, 1)"
+ @touchend.stop="touchend($event, 1)"
+ >
+ <!-- #endif -->
+ <view class="uv-pick-color__control__item__drag--hue"></view>
+ <view
+ class="uv-pick-color__control__item__drag--circle"
+ :style="{
+ left: $uv.getPx(site[1].left - 10,true)
+ }"
+ ></view>
+ </view>
+ <!-- #ifdef H5 -->
+ <view
+ class="uv-pick-color__control__item__drag drag-box"
+ @tap.stop="touchstart($event, 2)"
+ @touchstart.stop="touchstart($event, 2)"
+ @touchmove.stop="touchmove($event, 2)"
+ @touchend.stop="touchend($event, 2)"
+ >
+ <!-- #endif -->
+ <!-- #ifndef H5 -->
+ <view
+ class="uv-pick-color__control__item__drag drag-box"
+ @touchstart.stop="touchstart($event, 2)"
+ @touchmove.stop="touchmove($event, 2)"
+ @touchend.stop="touchend($event, 2)"
+ >
+ <!-- #endif -->
+ <view class="uv-pick-color__control__item__drag--alpha"></view>
+ <view
+ class="uv-pick-color__control__item__drag--circle"
+ :style="{
+ left: $uv.getPx(site[2].left - 10,true)
+ }"
+ ></view>
+ </view>
+ </view>
+ </view>
+ <view class="uv-pick-color__result">
+ <view
+ class="uv-pick-color__result__select"
+ hover-class="uv-hover-class"
+ @click.stop="select"
+ >
+ <text class="text">切换</text>
+ <text class="text">模式</text>
+ </view>
+ <view class="uv-pick-color__result__item" v-if="mode">
+ <view class="uv-pick-color__result__item--value uv-border">
+ <text>{{hex}}</text>
+ </view>
+ <view class="uv-pick-color__result__item--hex">
+ <text>HEX</text>
+ </view>
+ </view>
+ <template v-else>
+ <view class="uv-pick-color__result__item">
+ <view class="uv-pick-color__result__item--value uv-border">
+ <text>{{rgba.r}}</text>
+ </view>
+ <view class="uv-pick-color__result__item--rgba">
+ <text>R</text>
+ </view>
+ </view>
+ <view class="uv-pick-color__result__gap"></view>
+ <view class="uv-pick-color__result__item">
+ <view class="uv-pick-color__result__item--value uv-border">
+ <text>{{rgba.g}}</text>
+ </view>
+ <view class="uv-pick-color__result__item--rgba">
+ <text>G</text>
+ </view>
+ </view>
+ <view class="uv-pick-color__result__gap"></view>
+ <view class="uv-pick-color__result__item">
+ <view class="uv-pick-color__result__item--value uv-border">
+ <text>{{rgba.b}}</text>
+ </view>
+ <view class="uv-pick-color__result__item--rgba">
+ <text>B</text>
+ </view>
+ </view>
+ <view class="uv-pick-color__result__gap"></view>
+ <view class="uv-pick-color__result__item">
+ <view class="uv-pick-color__result__item--value uv-border">
+ <text>{{rgba.a}}</text>
+ </view>
+ <view class="uv-pick-color__result__item--rgba">
+ <text>A</text>
+ </view>
+ </view>
+ </template>
+ </view>
+ <view class="uv-pick-color__prefab">
+ <view
+ class="uv-pick-color__prefab__item"
+ v-for="(item,index) in colorList"
+ :key="index"
+ >
+ <view
+ class="uv-pick-color__prefab__item--color"
+ :style="{ background:`rgba(${item.r},${item.g},${item.b},${item.a})` }"
+ @click.stop="setColorBySelect(item)"
+ ></view>
+ </view>
+ </view>
+ </view>
+ </uv-popup>
+ <!-- #endif -->
+ <!-- #ifdef APP-NVUE -->
+ <view>
+ <text>nvue暂不支持uv-pick-color组件</text>
+ </view>
+ <!-- #endif -->
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+ import { rgbToHsb, hsbToRgb, rgbToHex, colorList } from './colors.js';
+ import props from './props.js';
+ export default {
+ name: 'uv-pick-color',
+ emits: ['confirm', 'cancel', 'close', 'change'],
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ pointerStyle() {
+ const style = {};
+ style.top = this.$uv.addUnit(this.site[0].top - 8);
+ style.left = this.$uv.addUnit(this.site[0].left - 8);
+ return style;
+ }
+ },
+ data() {
+ return {
+ showToolbar: false,
+ // rgba 颜色
+ rgba: {
+ r: 0,
+ g: 0,
+ b: 0,
+ a: 1
+ },
+ // hsb 颜色
+ hsb: {
+ h: 0,
+ s: 0,
+ b: 0
+ },
+ site: [{
+ top: 0,
+ left: 0
+ }, {
+ left: 0
+ }, {
+ left: 0
+ }],
+ index: 0,
+ bgcolor: {
+ r: 255,
+ g: 0,
+ b: 0,
+ a: 1
+ },
+ hex: '#000000',
+ mode: true,
+ colorList: colorList
+ };
+ },
+ watch: {
+ prefabColor(newVal) {
+ this.colorList = newVal;
+ }
+ },
+ created() {
+ // #ifdef APP-NVUE
+ return this.$uv.error('nvue暂不支持uv-pick-color组件');
+ // #endif
+ this.rgba = this.color;
+ if (this.prefabColor.length) this.colorList = this.prefabColor;
+ },
+ methods: {
+ open() {
+ this.$refs.pickerColorPopup.open();
+ this.showToolbar = true;
+ this.$nextTick(async () => {
+ await this.$uv.sleep(350);
+ this.getSelectorQuery();
+ })
+ },
+ close() {
+ this.$refs.pickerColorPopup.close();
+ },
+ popupChange(e) {
+ if(!e.show) this.$emit('close');
+ },
+ // 点击工具栏的取消按钮
+ cancelHandler() {
+ this.$emit('cancel');
+ this.close();
+ },
+ // 点击工具栏的确定按钮
+ confirmHandler() {
+ this.$emit('confirm', {
+ rgba: this.rgba,
+ hex: this.hex
+ })
+ this.close();
+ },
+ // 初始化
+ init() {
+ // hsb 颜色
+ this.hsb = rgbToHsb(this.rgba);
+ this.setValue(this.rgba);
+ },
+ async getSelectorQuery() {
+ const data = await this.$uvGetRect('.drag-box',true);
+ this.position = data;
+ this.setColorBySelect(this.rgba);
+ },
+ // 选择模式
+ select() {
+ this.mode = !this.mode;
+ },
+ touchstart(e, index) {
+ const { clientX, clientY } = e.touches[0];
+ this.pageX = clientX;
+ this.pageY = clientY;
+ this.setPosition(clientX, clientY, index);
+ },
+ touchmove(e, index) {
+ const { clientX, clientY } = e.touches[0];
+ this.moveX = clientX;
+ this.moveY = clientY;
+ this.setPosition(clientX, clientY, index);
+ },
+ touchend(e, index) {},
+ /**
+ * 设置位置
+ */
+ setPosition(x, y, index) {
+ this.index = index;
+ const { top, left, width, height } = this.position[index];
+ // 设置最大最小值
+ this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width));
+ if (index === 0) {
+ this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height));
+ // 设置颜色
+ this.hsb.s = parseInt((100 * this.site[index].left) / width);
+ this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height);
+ this.setColor();
+ this.setValue(this.rgba);
+ } else {
+ this.setControl(index, this.site[index].left);
+ }
+ },
+ /**
+ * 设置 rgb 颜色
+ */
+ setColor() {
+ const rgb = hsbToRgb(this.hsb);
+ this.rgba.r = rgb.r;
+ this.rgba.g = rgb.g;
+ this.rgba.b = rgb.b;
+ },
+ /**
+ * 设置二进制颜色
+ * @param {Object} rgb
+ */
+ setValue(rgb) {
+ this.hex = `#${(rgbToHex(rgb))}`;
+ },
+ setControl(index, x) {
+ const {
+ top,
+ left,
+ width,
+ height
+ } = this.position[index];
+
+ if (index === 1) {
+ this.hsb.h = parseInt((360 * x) / width);
+ this.bgcolor = hsbToRgb({
+ h: this.hsb.h,
+ s: 100,
+ b: 100
+ });
+ this.setColor()
+ } else {
+ this.rgba.a = +(x / width).toFixed(1);
+ }
+ this.setValue(this.rgba);
+ },
+ setColorBySelect(getrgb) {
+ const { r, g, b, a } = getrgb;
+ let rgb = {};
+ rgb = {
+ r: r ? parseInt(r) : 0,
+ g: g ? parseInt(g) : 0,
+ b: b ? parseInt(b) : 0,
+ a: a ? a : 0
+ };
+ this.rgba = rgb;
+ this.hsb = rgbToHsb(rgb);
+ this.changeViewByHsb();
+ },
+ changeViewByHsb() {
+ const [a, b, c] = this.position;
+ this.site[0].left = parseInt(this.hsb.s * a.width / 100);
+ this.site[0].top = parseInt((100 - this.hsb.b) * a.height / 100);
+ this.setColor(this.hsb.h);
+ this.setValue(this.rgba);
+ this.bgcolor = hsbToRgb({
+ h: this.hsb.h,
+ s: 100,
+ b: 100
+ });
+ this.site[1].left = this.hsb.h / 360 * b.width;
+ this.site[2].left = this.rgba.a * c.width;
+ }
+ }
+ };
+</script>
+<style scoped lang="scss">
+ $show-border: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ $height: 400rpx;
+ /* #ifndef APP-NVUE */
+ .uv-pick-color {
+ &__box {
+ position: relative;
+ height: $height;
+ background: rgb(255, 0, 0);
+ margin: 20rpx;
+ &__bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
+ &-mask {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ height: $height;
+ background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
+ }
+ &-pointer {
+ position: absolute;
+ top: -8px;
+ left: -8px;
+ z-index: 2;
+ width: 16px;
+ height: 16px;
+ border: 1px #fff solid;
+ border-radius: 8px;
+ }
+ }
+ }
+ &__control {
+ @include flex;
+ padding: 10rpx 20rpx;
+ &__alpha {
+ width: 100rpx;
+ height: 100rpx;
+ border-radius: 50rpx;
+ background-color: #fff;
+ background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
+ background-size: 36rpx 36rpx;
+ background-position: 0 0, 18rpx 18rpx;
+ border: 1px #eee solid;
+ overflow: hidden;
+ &--color {
+ width: 100%;
+ height: 100%;
+ }
+ }
+ &__item {
+ flex: 1;
+ @include flex(column);
+ justify-content: space-between;
+ height: 100rpx;
+ padding: 6rpx 0 6rpx 30rpx;
+ }
+ &__item__drag {
+ position: relative;
+ height: 16px;
+ background-color: #fff;
+ background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
+ linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
+ background-size: 32rpx 32rpx;
+ background-position: 0 0, 16rpx 16rpx;
+ &--hue {
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
+ }
+ &--alpha {
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
+ }
+ &--circle {
+ position: absolute;
+ top: -2px;
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ border-radius: 10px;
+ background: #fff;
+ box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
+ }
+ }
+ }
+ &__result {
+ @include flex;
+ padding: 20rpx;
+ text-align: center;
+ &__select {
+ @include flex(column);
+ justify-content: center;
+ width: 100rpx;
+ height: 100rpx;
+ margin-right: 10px;
+ border-radius: 10rpx;
+ box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
+ font-size: 14px;
+ color: #999;
+ }
+ &__item {
+ flex: 1;
+ height: 100rpx;
+ &--value {
+ height: 50rpx;
+ line-height: 50rpx;
+ border-radius: 4rpx;
+ font-size: 28rpx;
+ color: #999;
+ }
+ &--hex,
+ &--rgba {
+ height: 50rpx;
+ line-height: 50rpx;
+ font-size: 30rpx;
+ }
+ }
+ &__gap {
+ width: 10px;
+ height: 10px;
+ }
+ }
+ &__prefab {
+ @include flex;
+ margin: 0 -8rpx;
+ padding: 0 20rpx 20rpx;
+ flex-wrap: wrap;
+ &__item {
+ width: 50rpx;
+ height: 50rpx;
+ margin: 8rpx;
+ border-radius: 6rpx;
+ background-color: #fff;
+ background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
+ linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
+ background-size: 36rpx 36rpx;
+ background-position: 0 0, 18rpx 18rpx;
+ border: 1px #eee solid;
+ &--color {
+ width: 100%;
+ height: 100%;
+ border-radius: 6rpx;
+ }
+ }
+ }
+ }
+ /* #endif */
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-pick-color/package.json b/uni_modules/uv-pick-color/package.json
new file mode 100644
index 0000000..21a6b9a
--- /dev/null
+++ b/uni_modules/uv-pick-color/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-pick-color",
+ "displayName": "uv-pick-color 颜色选择器 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.8",
+ "description": "该组件为颜色选择器,支持预制颜色,初始化颜色,简单配置,开箱即用",
+ "keywords": [
+ "uv-pick-color",
+ "uvui",
+ "uv-ui",
+ "颜色",
+ "color"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-popup",
+ "uv-toolbar"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "u"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-pick-color/readme.md b/uni_modules/uv-pick-color/readme.md
new file mode 100644
index 0000000..c4accf5
--- /dev/null
+++ b/uni_modules/uv-pick-color/readme.md
@@ -0,0 +1,19 @@
+## PickColor 颜色选择器
+
+> **组件名:uv-pick-color**
+
+该组件为颜色选择器,支持预制颜色,初始化颜色,简单配置,开箱即用。
+
+# <a href="https://www.uvui.cn/components/pickColor.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-picker/components/uv-picker/props.js b/uni_modules/uv-picker/components/uv-picker/props.js
index 46dbddf..19549a6 100644
--- a/uni_modules/uv-picker/components/uv-picker/props.js
+++ b/uni_modules/uv-picker/components/uv-picker/props.js
@@ -48,7 +48,7 @@
// 确认按钮的颜色
confirmColor: {
type: String,
- default: '#EE2626'
+ default: '#3c9cff'
},
// 文字颜色
color: {
diff --git a/uni_modules/uv-picker/components/uv-picker/uv-picker.vue b/uni_modules/uv-picker/components/uv-picker/uv-picker.vue
index 3f9e0a5..c5e7c8a 100644
--- a/uni_modules/uv-picker/components/uv-picker/uv-picker.vue
+++ b/uni_modules/uv-picker/components/uv-picker/uv-picker.vue
@@ -82,7 +82,7 @@
* @property {String} cancelText 取消按钮的文字(默认 '取消' )
* @property {String} confirmText 确认按钮的文字(默认 '确定' )
* @property {String} cancelColor 取消按钮的颜色(默认 '#909193' )
- * @property {String} confirmColor 确认按钮的颜色(默认 '#EE2626' )
+ * @property {String} confirmColor 确认按钮的颜色(默认 '#3c9cff' )
* @property {String} color 文字颜色(默认 '' )
* @property {String} activeColor 选中文字的颜色(默认 '' )
* @property {String | Number} visibleItemCount 每列中可见选项的数量(默认 5 )
diff --git a/uni_modules/uv-qrcode/changelog.md b/uni_modules/uv-qrcode/changelog.md
new file mode 100644
index 0000000..70d8ba1
--- /dev/null
+++ b/uni_modules/uv-qrcode/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.5(2023-12-19)
+1. 优化
+## 1.0.4(2023-07-17)
+1. 优化文档
+2. 优化其他
+## 1.0.3(2023-06-26)
+1. H5增加属性h5SaveTip 保存二维码时候是否显示提示
+## 1.0.2(2023-06-01)
+1. 修复点击触发两次事件的BUG
+## 1.0.1(2023-05-23)
+1. 修复在部分平台不显示加载效果的BUG
+## 1.0.0(2023-05-17)
+1. 新增uv-qrcode组件
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/cache.js b/uni_modules/uv-qrcode/components/uv-qrcode/cache.js
new file mode 100644
index 0000000..d897d26
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/cache.js
@@ -0,0 +1 @@
+export const cacheImageList = [];
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js
new file mode 100644
index 0000000..27086ec
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js
@@ -0,0 +1,241 @@
+const isWeex = typeof WXEnvironment !== 'undefined';
+const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform);
+const isWeexAndroid = isWeex && !isWeexIOS;
+
+import GLmethod from '../context-webgl/GLmethod';
+
+const GCanvasModule =
+ (typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) :
+ (typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {};
+
+let isDebugging = false;
+
+let isComboDisabled = false;
+
+const logCommand = (function () {
+ const methodQuery = [];
+ Object.keys(GLmethod).forEach(key => {
+ methodQuery[GLmethod[key]] = key;
+ })
+ const queryMethod = (id) => {
+ return methodQuery[parseInt(id)] || 'NotFoundMethod';
+ }
+ const logCommand = (id, cmds) => {
+ const mId = cmds.split(',')[0];
+ const mName = queryMethod(mId);
+ console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`);
+ }
+ return logCommand;
+})();
+
+function joinArray(arr, sep) {
+ let res = '';
+ for (let i = 0; i < arr.length; i++) {
+ if (i !== 0) {
+ res += sep;
+ }
+ res += arr[i];
+ }
+ return res;
+}
+
+const commandsCache = {}
+
+const GBridge = {
+
+ callEnable: (ref, configArray) => {
+
+ commandsCache[ref] = [];
+
+ return GCanvasModule.enable({
+ componentId: ref,
+ config: configArray
+ });
+ },
+
+ callEnableDebug: () => {
+ isDebugging = true;
+ },
+
+ callEnableDisableCombo: () => {
+ isComboDisabled = true;
+ },
+
+ callSetContextType: function (componentId, context_type) {
+ GCanvasModule.setContextType(context_type, componentId);
+ },
+
+ callReset: function(id){
+ GCanvasModule.resetComponent && canvasModule.resetComponent(componentId);
+ },
+
+ render: isWeexIOS ? function (componentId) {
+ return GCanvasModule.extendCallNative({
+ contextId: componentId,
+ type: 0x60000001
+ });
+ } : function (componentId) {
+ return callGCanvasLinkNative(componentId, 0x60000001, 'render');
+ },
+
+ render2d: isWeexIOS ? function (componentId, commands, callback) {
+
+ if (isDebugging) {
+ console.log('>>> >>> render2d ===');
+ console.log('>>> commands: ' + commands);
+ }
+
+ GCanvasModule.render([commands, callback?true:false], componentId, callback);
+
+ } : function (componentId, commands,callback) {
+
+ if (isDebugging) {
+ console.log('>>> >>> render2d ===');
+ console.log('>>> commands: ' + commands);
+ }
+
+ callGCanvasLinkNative(componentId, 0x20000001, commands);
+ if(callback){
+ callback();
+ }
+ },
+
+ callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) {
+
+ throw 'should not be here anymore ' + cmdArgs;
+
+ } : function (componentId, cmdArgs) {
+
+ throw 'should not be here anymore ' + cmdArgs;
+
+ },
+
+
+ flushNative: isWeexIOS ? function (componentId) {
+
+ const cmdArgs = joinArray(commandsCache[componentId], ';');
+ commandsCache[componentId] = [];
+
+ if (isDebugging) {
+ console.log('>>> >>> flush native ===');
+ console.log('>>> commands: ' + cmdArgs);
+ }
+
+ const result = GCanvasModule.extendCallNative({
+ "contextId": componentId,
+ "type": 0x60000000,
+ "args": cmdArgs
+ });
+
+ const res = result && result.result;
+
+ if (isDebugging) {
+ console.log('>>> result: ' + res);
+ }
+
+ return res;
+
+ } : function (componentId) {
+
+ const cmdArgs = joinArray(commandsCache[componentId], ';');
+ commandsCache[componentId] = [];
+
+ if (isDebugging) {
+ console.log('>>> >>> flush native ===');
+ console.log('>>> commands: ' + cmdArgs);
+ }
+
+ const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs);
+
+ if (isDebugging) {
+ console.log('>>> result: ' + result);
+ }
+
+ return result;
+ },
+
+ callNative: function (componentId, cmdArgs, cache) {
+
+ if (isDebugging) {
+ logCommand(componentId, cmdArgs);
+ }
+
+ commandsCache[componentId].push(cmdArgs);
+
+ if (!cache || isComboDisabled) {
+ return GBridge.flushNative(componentId);
+ } else {
+ return undefined;
+ }
+ },
+
+ texImage2D(componentId, ...args) {
+ if (isWeexIOS) {
+ if (args.length === 6) {
+ const [target, level, internalformat, format, type, image] = args;
+ GBridge.callNative(
+ componentId,
+ GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src
+ )
+ } else if (args.length === 9) {
+ const [target, level, internalformat, width, height, border, format, type, image] = args;
+ GBridge.callNative(
+ componentId,
+ GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' +
+ + format + ',' + type + ',' + (image ? image.src : 0)
+ )
+ }
+ } else if (isWeexAndroid) {
+ if (args.length === 6) {
+ const [target, level, internalformat, format, type, image] = args;
+ GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src);
+ } else if (args.length === 9) {
+ const [target, level, internalformat, width, height, border, format, type, image] = args;
+ GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0));
+ }
+ }
+ },
+
+ texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) {
+ if (isWeexIOS) {
+ if (arguments.length === 8) {
+ GBridge.callNative(
+ componentId,
+ GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src
+ )
+ }
+ } else if (isWeexAndroid) {
+ GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src);
+ }
+ },
+
+ bindImageTexture(componentId, src, imageId) {
+ GCanvasModule.bindImageTexture([src, imageId], componentId);
+ },
+
+ perloadImage([url, id], callback) {
+ GCanvasModule.preLoadImage([url, id], function (image) {
+ image.url = url;
+ image.id = id;
+ callback(image);
+ });
+ },
+
+ measureText(text, fontStyle, componentId) {
+ return GCanvasModule.measureText([text, fontStyle], componentId);
+ },
+
+ getImageData (componentId, x, y, w, h, callback) {
+ GCanvasModule.getImageData([x, y,w,h],componentId,callback);
+ },
+
+ putImageData (componentId, data, x, y, w, h, callback) {
+ GCanvasModule.putImageData([x, y,w,h,data],componentId,callback);
+ },
+
+ toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){
+ GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback);
+ }
+}
+
+export default GBridge;
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js
new file mode 100644
index 0000000..3e7f03a
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js
@@ -0,0 +1,18 @@
+class FillStyleLinearGradient {
+
+ constructor(x0, y0, x1, y1) {
+ this._start_pos = { _x: x0, _y: y0 };
+ this._end_pos = { _x: x1, _y: y1 };
+ this._stop_count = 0;
+ this._stops = [0, 0, 0, 0, 0];
+ }
+
+ addColorStop = function (pos, color) {
+ if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
+ this._stops[this._stop_count] = { _pos: pos, _color: color };
+ this._stop_count++;
+ }
+ }
+}
+
+export default FillStyleLinearGradient;
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js
new file mode 100644
index 0000000..6e4f646
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js
@@ -0,0 +1,8 @@
+class FillStylePattern {
+ constructor(img, pattern) {
+ this._style = pattern;
+ this._img = img;
+ }
+}
+
+export default FillStylePattern;
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js
new file mode 100644
index 0000000..7790596
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js
@@ -0,0 +1,17 @@
+class FillStyleRadialGradient {
+ constructor(x0, y0, r0, x1, y1, r1) {
+ this._start_pos = { _x: x0, _y: y0, _r: r0 };
+ this._end_pos = { _x: x1, _y: y1, _r: r1 };
+ this._stop_count = 0;
+ this._stops = [0, 0, 0, 0, 0];
+ }
+
+ addColorStop(pos, color) {
+ if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
+ this._stops[this._stop_count] = { _pos: pos, _color: color };
+ this._stop_count++;
+ }
+ }
+}
+
+export default FillStyleRadialGradient;
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js
new file mode 100644
index 0000000..e6b8f48
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js
@@ -0,0 +1,666 @@
+import FillStylePattern from './FillStylePattern';
+import FillStyleLinearGradient from './FillStyleLinearGradient';
+import FillStyleRadialGradient from './FillStyleRadialGradient';
+import GImage from '../env/image.js';
+import {
+ ArrayBufferToBase64,
+ Base64ToUint8ClampedArray
+} from '../env/tool.js';
+
+export default class CanvasRenderingContext2D {
+
+ _drawCommands = '';
+
+ _globalAlpha = 1.0;
+
+ _fillStyle = 'rgb(0,0,0)';
+ _strokeStyle = 'rgb(0,0,0)';
+
+ _lineWidth = 1;
+ _lineCap = 'butt';
+ _lineJoin = 'miter';
+
+ _miterLimit = 10;
+
+ _globalCompositeOperation = 'source-over';
+
+ _textAlign = 'start';
+ _textBaseline = 'alphabetic';
+
+ _font = '10px sans-serif';
+
+ _savedGlobalAlpha = [];
+
+ timer = null;
+ componentId = null;
+
+ _notCommitDrawImageCache = [];
+ _needRedrawImageCache = [];
+ _redrawCommands = '';
+ _autoSaveContext = true;
+ // _imageMap = new GHashMap();
+ // _textureMap = new GHashMap();
+
+ constructor() {
+ this.className = 'CanvasRenderingContext2D';
+ //this.save()
+ }
+
+ setFillStyle(value) {
+ this.fillStyle = value;
+ }
+
+ set fillStyle(value) {
+ this._fillStyle = value;
+
+ if (typeof(value) == 'string') {
+ this._drawCommands = this._drawCommands.concat("F" + value + ";");
+ } else if (value instanceof FillStylePattern) {
+ const image = value._img;
+ if (!image.complete) {
+ image.onload = () => {
+ var index = this._needRedrawImageCache.indexOf(image);
+ if (index > -1) {
+ this._needRedrawImageCache.splice(index, 1);
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._redrawflush(true);
+ }
+ }
+ this._notCommitDrawImageCache.push(image);
+ } else {
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ }
+
+ //CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
+ } else if (value instanceof FillStyleLinearGradient) {
+ var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
+ value._stop_count;
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ } else if (value instanceof FillStyleRadialGradient) {
+ var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
+ .toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," +
+ value._stop_count;
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ }
+ }
+
+ get fillStyle() {
+ return this._fillStyle;
+ }
+
+ get globalAlpha() {
+ return this._globalAlpha;
+ }
+
+ setGlobalAlpha(value) {
+ this.globalAlpha = value;
+ }
+
+ set globalAlpha(value) {
+ this._globalAlpha = value;
+ this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";");
+ }
+
+
+ get strokeStyle() {
+ return this._strokeStyle;
+ }
+
+ setStrokeStyle(value) {
+ this.strokeStyle = value;
+ }
+
+ set strokeStyle(value) {
+
+ this._strokeStyle = value;
+
+ if (typeof(value) == 'string') {
+ this._drawCommands = this._drawCommands.concat("S" + value + ";");
+ } else if (value instanceof FillStylePattern) {
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
+ } else if (value instanceof FillStyleLinearGradient) {
+ var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
+ value._stop_count;
+
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ } else if (value instanceof FillStyleRadialGradient) {
+ var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
+ .toFixed(2) + "," +
+ value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," +
+ value._stop_count;
+
+ for (var i = 0; i < value._stop_count; ++i) {
+ command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
+ }
+ this._drawCommands = this._drawCommands.concat(command + ";");
+ }
+ }
+
+ get lineWidth() {
+ return this._lineWidth;
+ }
+
+ setLineWidth(value) {
+ this.lineWidth = value;
+ }
+
+ set lineWidth(value) {
+ this._lineWidth = value;
+ this._drawCommands = this._drawCommands.concat("W" + value + ";");
+ }
+
+ get lineCap() {
+ return this._lineCap;
+ }
+
+ setLineCap(value) {
+ this.lineCap = value;
+ }
+
+ set lineCap(value) {
+ this._lineCap = value;
+ this._drawCommands = this._drawCommands.concat("C" + value + ";");
+ }
+
+ get lineJoin() {
+ return this._lineJoin;
+ }
+
+ setLineJoin(value) {
+ this.lineJoin = value
+ }
+
+ set lineJoin(value) {
+ this._lineJoin = value;
+ this._drawCommands = this._drawCommands.concat("J" + value + ";");
+ }
+
+ get miterLimit() {
+ return this._miterLimit;
+ }
+
+ setMiterLimit(value) {
+ this.miterLimit = value
+ }
+
+ set miterLimit(value) {
+ this._miterLimit = value;
+ this._drawCommands = this._drawCommands.concat("M" + value + ";");
+ }
+
+ get globalCompositeOperation() {
+ return this._globalCompositeOperation;
+ }
+
+ set globalCompositeOperation(value) {
+
+ this._globalCompositeOperation = value;
+ let mode = 0;
+ switch (value) {
+ case "source-over":
+ mode = 0;
+ break;
+ case "source-atop":
+ mode = 5;
+ break;
+ case "source-in":
+ mode = 0;
+ break;
+ case "source-out":
+ mode = 2;
+ break;
+ case "destination-over":
+ mode = 4;
+ break;
+ case "destination-atop":
+ mode = 4;
+ break;
+ case "destination-in":
+ mode = 4;
+ break;
+ case "destination-out":
+ mode = 3;
+ break;
+ case "lighter":
+ mode = 1;
+ break;
+ case "copy":
+ mode = 2;
+ break;
+ case "xor":
+ mode = 6;
+ break;
+ default:
+ mode = 0;
+ }
+
+ this._drawCommands = this._drawCommands.concat("B" + mode + ";");
+ }
+
+ get textAlign() {
+ return this._textAlign;
+ }
+
+ setTextAlign(value) {
+ this.textAlign = value
+ }
+
+ set textAlign(value) {
+
+ this._textAlign = value;
+ let Align = 0;
+ switch (value) {
+ case "start":
+ Align = 0;
+ break;
+ case "end":
+ Align = 1;
+ break;
+ case "left":
+ Align = 2;
+ break;
+ case "center":
+ Align = 3;
+ break;
+ case "right":
+ Align = 4;
+ break;
+ default:
+ Align = 0;
+ }
+
+ this._drawCommands = this._drawCommands.concat("A" + Align + ";");
+ }
+
+ get textBaseline() {
+ return this._textBaseline;
+ }
+
+ setTextBaseline(value) {
+ this.textBaseline = value
+ }
+
+ set textBaseline(value) {
+ this._textBaseline = value;
+ let baseline = 0;
+ switch (value) {
+ case "alphabetic":
+ baseline = 0;
+ break;
+ case "middle":
+ baseline = 1;
+ break;
+ case "top":
+ baseline = 2;
+ break;
+ case "hanging":
+ baseline = 3;
+ break;
+ case "bottom":
+ baseline = 4;
+ break;
+ case "ideographic":
+ baseline = 5;
+ break;
+ default:
+ baseline = 0;
+ break;
+ }
+
+ this._drawCommands = this._drawCommands.concat("E" + baseline + ";");
+ }
+
+ get font() {
+ return this._font;
+ }
+
+ setFontSize(size) {
+ var str = this._font;
+ var strs = str.trim().split(/\s+/);
+ for (var i = 0; i < strs.length; i++) {
+ var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold",
+ "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",
+ "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
+ "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
+ ];
+
+ if (-1 == values.indexOf(strs[i].trim())) {
+ if (typeof size === 'string') {
+ strs[i] = size;
+ } else if (typeof size === 'number') {
+ strs[i] = String(size) + 'px';
+ }
+ break;
+ }
+ }
+ this.font = strs.join(" ");
+ }
+
+ set font(value) {
+ this._font = value;
+ this._drawCommands = this._drawCommands.concat("j" + value + ";");
+ }
+
+ setTransform(a, b, c, d, tx, ty) {
+ this._drawCommands = this._drawCommands.concat("t" +
+ (a === 1 ? "1" : a.toFixed(2)) + "," +
+ (b === 0 ? "0" : b.toFixed(2)) + "," +
+ (c === 0 ? "0" : c.toFixed(2)) + "," +
+ (d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
+ }
+
+ transform(a, b, c, d, tx, ty) {
+ this._drawCommands = this._drawCommands.concat("f" +
+ (a === 1 ? "1" : a.toFixed(2)) + "," +
+ (b === 0 ? "0" : b.toFixed(2)) + "," +
+ (c === 0 ? "0" : c.toFixed(2)) + "," +
+ (d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";");
+ }
+
+ resetTransform() {
+ this._drawCommands = this._drawCommands.concat("m;");
+ }
+
+ scale(a, d) {
+ this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," +
+ d.toFixed(2) + ";");
+ }
+
+ rotate(angle) {
+ this._drawCommands = this._drawCommands
+ .concat("r" + angle.toFixed(6) + ";");
+ }
+
+ translate(tx, ty) {
+ this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
+ }
+
+ save() {
+ this._savedGlobalAlpha.push(this._globalAlpha);
+ this._drawCommands = this._drawCommands.concat("v;");
+ }
+
+ restore() {
+ this._drawCommands = this._drawCommands.concat("e;");
+ this._globalAlpha = this._savedGlobalAlpha.pop();
+ }
+
+ createPattern(img, pattern) {
+ if (typeof img === 'string') {
+ var imgObj = new GImage();
+ imgObj.src = img;
+ img = imgObj;
+ }
+ return new FillStylePattern(img, pattern);
+ }
+
+ createLinearGradient(x0, y0, x1, y1) {
+ return new FillStyleLinearGradient(x0, y0, x1, y1);
+ }
+
+ createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
+ return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
+ };
+
+ createCircularGradient = function(x0, y0, r0) {
+ return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0);
+ };
+
+ strokeRect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";");
+ }
+
+
+ clearRect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w +
+ "," + h + ";");
+ }
+
+ clip() {
+ this._drawCommands = this._drawCommands.concat("p;");
+ }
+
+ resetClip() {
+ this._drawCommands = this._drawCommands.concat("q;");
+ }
+
+ closePath() {
+ this._drawCommands = this._drawCommands.concat("o;");
+ }
+
+ moveTo(x, y) {
+ this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";");
+ }
+
+ lineTo(x, y) {
+ this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";");
+ }
+
+ quadraticCurveTo = function(cpx, cpy, x, y) {
+ this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";");
+ }
+
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
+ this._drawCommands = this._drawCommands.concat(
+ "z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," +
+ x.toFixed(2) + "," + y.toFixed(2) + ";");
+ }
+
+ arcTo(x1, y1, x2, y2, radius) {
+ this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";");
+ }
+
+ beginPath() {
+ this._drawCommands = this._drawCommands.concat("b;");
+ }
+
+
+ fillRect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w +
+ "," + h + ";");
+ }
+
+ rect(x, y, w, h) {
+ this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";");
+ }
+
+ fill() {
+ this._drawCommands = this._drawCommands.concat("L;");
+ }
+
+ stroke(path) {
+ this._drawCommands = this._drawCommands.concat("x;");
+ }
+
+ arc(x, y, radius, startAngle, endAngle, anticlockwise) {
+
+ let ianticlockwise = 0;
+ if (anticlockwise) {
+ ianticlockwise = 1;
+ }
+
+ this._drawCommands = this._drawCommands.concat(
+ "y" + x.toFixed(2) + "," + y.toFixed(2) + "," +
+ radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise +
+ ";"
+ );
+ }
+
+ fillText(text, x, y) {
+ let tmptext = text.replace(/!/g, "!!");
+ tmptext = tmptext.replace(/,/g, "!,");
+ tmptext = tmptext.replace(/;/g, "!;");
+ this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;");
+ }
+
+ strokeText = function(text, x, y) {
+ let tmptext = text.replace(/!/g, "!!");
+ tmptext = tmptext.replace(/,/g, "!,");
+ tmptext = tmptext.replace(/;/g, "!;");
+ this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;");
+ }
+
+ measureText(text) {
+ return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId);
+ }
+
+ isPointInPath = function(x, y) {
+ throw new Error('GCanvas not supported yet');
+ }
+
+ drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ if (typeof image === 'string') {
+ var imgObj = new GImage();
+ imgObj.src = image;
+ image = imgObj;
+ }
+ if (image instanceof GImage) {
+ if (!image.complete) {
+ imgObj.onload = () => {
+ var index = this._needRedrawImageCache.indexOf(image);
+ if (index > -1) {
+ this._needRedrawImageCache.splice(index, 1);
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ this._redrawflush(true);
+ }
+ }
+ this._notCommitDrawImageCache.push(image);
+ } else {
+ CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
+ }
+ var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh];
+ var args = [];
+ for (var arg in srcArgs) {
+ if (typeof(srcArgs[arg]) != 'undefined') {
+ args.push(srcArgs[arg]);
+ }
+ }
+ this.__drawImage.apply(this, args);
+ //this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
+ }
+ }
+
+ __drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
+ const numArgs = arguments.length;
+
+ function drawImageCommands() {
+
+ if (numArgs === 3) {
+ const x = parseFloat(sx) || 0.0;
+ const y = parseFloat(sy) || 0.0;
+
+ return ("d" + image._id + ",0,0," +
+ image.width + "," + image.height + "," +
+ x + "," + y + "," + image.width + "," + image.height + ";");
+ } else if (numArgs === 5) {
+ const x = parseFloat(sx) || 0.0;
+ const y = parseFloat(sy) || 0.0;
+ const width = parseInt(sw) || image.width;
+ const height = parseInt(sh) || image.height;
+
+ return ("d" + image._id + ",0,0," +
+ image.width + "," + image.height + "," +
+ x + "," + y + "," + width + "," + height + ";");
+ } else if (numArgs === 9) {
+ sx = parseFloat(sx) || 0.0;
+ sy = parseFloat(sy) || 0.0;
+ sw = parseInt(sw) || image.width;
+ sh = parseInt(sh) || image.height;
+ dx = parseFloat(dx) || 0.0;
+ dy = parseFloat(dy) || 0.0;
+ dw = parseInt(dw) || image.width;
+ dh = parseInt(dh) || image.height;
+
+ return ("d" + image._id + "," +
+ sx + "," + sy + "," + sw + "," + sh + "," +
+ dx + "," + dy + "," + dw + "," + dh + ";");
+ }
+ }
+ this._drawCommands += drawImageCommands();
+ }
+
+ _flush(reserve, callback) {
+ const commands = this._drawCommands;
+ this._drawCommands = '';
+ CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
+ this._needRender = false;
+ }
+
+ _redrawflush(reserve, callback) {
+ const commands = this._redrawCommands;
+ CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
+ if (this._needRedrawImageCache.length == 0) {
+ this._redrawCommands = '';
+ }
+ }
+
+ draw(reserve, callback) {
+ if (!reserve) {
+ this._globalAlpha = this._savedGlobalAlpha.pop();
+ this._savedGlobalAlpha.push(this._globalAlpha);
+ this._redrawCommands = this._drawCommands;
+ this._needRedrawImageCache = this._notCommitDrawImageCache;
+ if (this._autoSaveContext) {
+ this._drawCommands = ("v;" + this._drawCommands);
+ this._autoSaveContext = false;
+ } else {
+ this._drawCommands = ("e;X;v;" + this._drawCommands);
+ }
+ } else {
+ this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache);
+ this._redrawCommands += this._drawCommands;
+ if (this._autoSaveContext) {
+ this._drawCommands = ("v;" + this._drawCommands);
+ this._autoSaveContext = false;
+ }
+ }
+ this._notCommitDrawImageCache = [];
+ if (this._flush) {
+ this._flush(reserve, callback);
+ }
+ }
+
+ getImageData(x, y, w, h, callback) {
+ CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) {
+ res.data = Base64ToUint8ClampedArray(res.data);
+ if (typeof(callback) == 'function') {
+ callback(res);
+ }
+ });
+ }
+
+ putImageData(data, x, y, w, h, callback) {
+ if (data instanceof Uint8ClampedArray) {
+ data = ArrayBufferToBase64(data);
+ CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) {
+ if (typeof(callback) == 'function') {
+ callback(res);
+ }
+ });
+ }
+ }
+
+ toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) {
+ CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight,
+ fileType, quality,
+ function(res) {
+ if (typeof(callback) == 'function') {
+ callback(res);
+ }
+ });
+ }
+}
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js
new file mode 100644
index 0000000..b495129
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js
@@ -0,0 +1,11 @@
+export default class WebGLActiveInfo {
+ className = 'WebGLActiveInfo';
+
+ constructor({
+ type, name, size
+ }) {
+ this.type = type;
+ this.name = name;
+ this.size = size;
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js
new file mode 100644
index 0000000..4800f67
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLBuffer';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLBuffer {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js
new file mode 100644
index 0000000..28b46d3
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLFrameBuffer';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLFramebuffer {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js
new file mode 100644
index 0000000..ac5544d
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js
@@ -0,0 +1,298 @@
+export default {
+ "DEPTH_BUFFER_BIT": 256,
+ "STENCIL_BUFFER_BIT": 1024,
+ "COLOR_BUFFER_BIT": 16384,
+ "POINTS": 0,
+ "LINES": 1,
+ "LINE_LOOP": 2,
+ "LINE_STRIP": 3,
+ "TRIANGLES": 4,
+ "TRIANGLE_STRIP": 5,
+ "TRIANGLE_FAN": 6,
+ "ZERO": 0,
+ "ONE": 1,
+ "SRC_COLOR": 768,
+ "ONE_MINUS_SRC_COLOR": 769,
+ "SRC_ALPHA": 770,
+ "ONE_MINUS_SRC_ALPHA": 771,
+ "DST_ALPHA": 772,
+ "ONE_MINUS_DST_ALPHA": 773,
+ "DST_COLOR": 774,
+ "ONE_MINUS_DST_COLOR": 775,
+ "SRC_ALPHA_SATURATE": 776,
+ "FUNC_ADD": 32774,
+ "BLEND_EQUATION": 32777,
+ "BLEND_EQUATION_RGB": 32777,
+ "BLEND_EQUATION_ALPHA": 34877,
+ "FUNC_SUBTRACT": 32778,
+ "FUNC_REVERSE_SUBTRACT": 32779,
+ "BLEND_DST_RGB": 32968,
+ "BLEND_SRC_RGB": 32969,
+ "BLEND_DST_ALPHA": 32970,
+ "BLEND_SRC_ALPHA": 32971,
+ "CONSTANT_COLOR": 32769,
+ "ONE_MINUS_CONSTANT_COLOR": 32770,
+ "CONSTANT_ALPHA": 32771,
+ "ONE_MINUS_CONSTANT_ALPHA": 32772,
+ "BLEND_COLOR": 32773,
+ "ARRAY_BUFFER": 34962,
+ "ELEMENT_ARRAY_BUFFER": 34963,
+ "ARRAY_BUFFER_BINDING": 34964,
+ "ELEMENT_ARRAY_BUFFER_BINDING": 34965,
+ "STREAM_DRAW": 35040,
+ "STATIC_DRAW": 35044,
+ "DYNAMIC_DRAW": 35048,
+ "BUFFER_SIZE": 34660,
+ "BUFFER_USAGE": 34661,
+ "CURRENT_VERTEX_ATTRIB": 34342,
+ "FRONT": 1028,
+ "BACK": 1029,
+ "FRONT_AND_BACK": 1032,
+ "TEXTURE_2D": 3553,
+ "CULL_FACE": 2884,
+ "BLEND": 3042,
+ "DITHER": 3024,
+ "STENCIL_TEST": 2960,
+ "DEPTH_TEST": 2929,
+ "SCISSOR_TEST": 3089,
+ "POLYGON_OFFSET_FILL": 32823,
+ "SAMPLE_ALPHA_TO_COVERAGE": 32926,
+ "SAMPLE_COVERAGE": 32928,
+ "NO_ERROR": 0,
+ "INVALID_ENUM": 1280,
+ "INVALID_VALUE": 1281,
+ "INVALID_OPERATION": 1282,
+ "OUT_OF_MEMORY": 1285,
+ "CW": 2304,
+ "CCW": 2305,
+ "LINE_WIDTH": 2849,
+ "ALIASED_POINT_SIZE_RANGE": 33901,
+ "ALIASED_LINE_WIDTH_RANGE": 33902,
+ "CULL_FACE_MODE": 2885,
+ "FRONT_FACE": 2886,
+ "DEPTH_RANGE": 2928,
+ "DEPTH_WRITEMASK": 2930,
+ "DEPTH_CLEAR_VALUE": 2931,
+ "DEPTH_FUNC": 2932,
+ "STENCIL_CLEAR_VALUE": 2961,
+ "STENCIL_FUNC": 2962,
+ "STENCIL_FAIL": 2964,
+ "STENCIL_PASS_DEPTH_FAIL": 2965,
+ "STENCIL_PASS_DEPTH_PASS": 2966,
+ "STENCIL_REF": 2967,
+ "STENCIL_VALUE_MASK": 2963,
+ "STENCIL_WRITEMASK": 2968,
+ "STENCIL_BACK_FUNC": 34816,
+ "STENCIL_BACK_FAIL": 34817,
+ "STENCIL_BACK_PASS_DEPTH_FAIL": 34818,
+ "STENCIL_BACK_PASS_DEPTH_PASS": 34819,
+ "STENCIL_BACK_REF": 36003,
+ "STENCIL_BACK_VALUE_MASK": 36004,
+ "STENCIL_BACK_WRITEMASK": 36005,
+ "VIEWPORT": 2978,
+ "SCISSOR_BOX": 3088,
+ "COLOR_CLEAR_VALUE": 3106,
+ "COLOR_WRITEMASK": 3107,
+ "UNPACK_ALIGNMENT": 3317,
+ "PACK_ALIGNMENT": 3333,
+ "MAX_TEXTURE_SIZE": 3379,
+ "MAX_VIEWPORT_DIMS": 3386,
+ "SUBPIXEL_BITS": 3408,
+ "RED_BITS": 3410,
+ "GREEN_BITS": 3411,
+ "BLUE_BITS": 3412,
+ "ALPHA_BITS": 3413,
+ "DEPTH_BITS": 3414,
+ "STENCIL_BITS": 3415,
+ "POLYGON_OFFSET_UNITS": 10752,
+ "POLYGON_OFFSET_FACTOR": 32824,
+ "TEXTURE_BINDING_2D": 32873,
+ "SAMPLE_BUFFERS": 32936,
+ "SAMPLES": 32937,
+ "SAMPLE_COVERAGE_VALUE": 32938,
+ "SAMPLE_COVERAGE_INVERT": 32939,
+ "COMPRESSED_TEXTURE_FORMATS": 34467,
+ "DONT_CARE": 4352,
+ "FASTEST": 4353,
+ "NICEST": 4354,
+ "GENERATE_MIPMAP_HINT": 33170,
+ "BYTE": 5120,
+ "UNSIGNED_BYTE": 5121,
+ "SHORT": 5122,
+ "UNSIGNED_SHORT": 5123,
+ "INT": 5124,
+ "UNSIGNED_INT": 5125,
+ "FLOAT": 5126,
+ "DEPTH_COMPONENT": 6402,
+ "ALPHA": 6406,
+ "RGB": 6407,
+ "RGBA": 6408,
+ "LUMINANCE": 6409,
+ "LUMINANCE_ALPHA": 6410,
+ "UNSIGNED_SHORT_4_4_4_4": 32819,
+ "UNSIGNED_SHORT_5_5_5_1": 32820,
+ "UNSIGNED_SHORT_5_6_5": 33635,
+ "FRAGMENT_SHADER": 35632,
+ "VERTEX_SHADER": 35633,
+ "MAX_VERTEX_ATTRIBS": 34921,
+ "MAX_VERTEX_UNIFORM_VECTORS": 36347,
+ "MAX_VARYING_VECTORS": 36348,
+ "MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661,
+ "MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660,
+ "MAX_TEXTURE_IMAGE_UNITS": 34930,
+ "MAX_FRAGMENT_UNIFORM_VECTORS": 36349,
+ "SHADER_TYPE": 35663,
+ "DELETE_STATUS": 35712,
+ "LINK_STATUS": 35714,
+ "VALIDATE_STATUS": 35715,
+ "ATTACHED_SHADERS": 35717,
+ "ACTIVE_UNIFORMS": 35718,
+ "ACTIVE_ATTRIBUTES": 35721,
+ "SHADING_LANGUAGE_VERSION": 35724,
+ "CURRENT_PROGRAM": 35725,
+ "NEVER": 512,
+ "LESS": 513,
+ "EQUAL": 514,
+ "LEQUAL": 515,
+ "GREATER": 516,
+ "NOTEQUAL": 517,
+ "GEQUAL": 518,
+ "ALWAYS": 519,
+ "KEEP": 7680,
+ "REPLACE": 7681,
+ "INCR": 7682,
+ "DECR": 7683,
+ "INVERT": 5386,
+ "INCR_WRAP": 34055,
+ "DECR_WRAP": 34056,
+ "VENDOR": 7936,
+ "RENDERER": 7937,
+ "VERSION": 7938,
+ "NEAREST": 9728,
+ "LINEAR": 9729,
+ "NEAREST_MIPMAP_NEAREST": 9984,
+ "LINEAR_MIPMAP_NEAREST": 9985,
+ "NEAREST_MIPMAP_LINEAR": 9986,
+ "LINEAR_MIPMAP_LINEAR": 9987,
+ "TEXTURE_MAG_FILTER": 10240,
+ "TEXTURE_MIN_FILTER": 10241,
+ "TEXTURE_WRAP_S": 10242,
+ "TEXTURE_WRAP_T": 10243,
+ "TEXTURE": 5890,
+ "TEXTURE_CUBE_MAP": 34067,
+ "TEXTURE_BINDING_CUBE_MAP": 34068,
+ "TEXTURE_CUBE_MAP_POSITIVE_X": 34069,
+ "TEXTURE_CUBE_MAP_NEGATIVE_X": 34070,
+ "TEXTURE_CUBE_MAP_POSITIVE_Y": 34071,
+ "TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072,
+ "TEXTURE_CUBE_MAP_POSITIVE_Z": 34073,
+ "TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074,
+ "MAX_CUBE_MAP_TEXTURE_SIZE": 34076,
+ "TEXTURE0": 33984,
+ "TEXTURE1": 33985,
+ "TEXTURE2": 33986,
+ "TEXTURE3": 33987,
+ "TEXTURE4": 33988,
+ "TEXTURE5": 33989,
+ "TEXTURE6": 33990,
+ "TEXTURE7": 33991,
+ "TEXTURE8": 33992,
+ "TEXTURE9": 33993,
+ "TEXTURE10": 33994,
+ "TEXTURE11": 33995,
+ "TEXTURE12": 33996,
+ "TEXTURE13": 33997,
+ "TEXTURE14": 33998,
+ "TEXTURE15": 33999,
+ "TEXTURE16": 34000,
+ "TEXTURE17": 34001,
+ "TEXTURE18": 34002,
+ "TEXTURE19": 34003,
+ "TEXTURE20": 34004,
+ "TEXTURE21": 34005,
+ "TEXTURE22": 34006,
+ "TEXTURE23": 34007,
+ "TEXTURE24": 34008,
+ "TEXTURE25": 34009,
+ "TEXTURE26": 34010,
+ "TEXTURE27": 34011,
+ "TEXTURE28": 34012,
+ "TEXTURE29": 34013,
+ "TEXTURE30": 34014,
+ "TEXTURE31": 34015,
+ "ACTIVE_TEXTURE": 34016,
+ "REPEAT": 10497,
+ "CLAMP_TO_EDGE": 33071,
+ "MIRRORED_REPEAT": 33648,
+ "FLOAT_VEC2": 35664,
+ "FLOAT_VEC3": 35665,
+ "FLOAT_VEC4": 35666,
+ "INT_VEC2": 35667,
+ "INT_VEC3": 35668,
+ "INT_VEC4": 35669,
+ "BOOL": 35670,
+ "BOOL_VEC2": 35671,
+ "BOOL_VEC3": 35672,
+ "BOOL_VEC4": 35673,
+ "FLOAT_MAT2": 35674,
+ "FLOAT_MAT3": 35675,
+ "FLOAT_MAT4": 35676,
+ "SAMPLER_2D": 35678,
+ "SAMPLER_CUBE": 35680,
+ "VERTEX_ATTRIB_ARRAY_ENABLED": 34338,
+ "VERTEX_ATTRIB_ARRAY_SIZE": 34339,
+ "VERTEX_ATTRIB_ARRAY_STRIDE": 34340,
+ "VERTEX_ATTRIB_ARRAY_TYPE": 34341,
+ "VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922,
+ "VERTEX_ATTRIB_ARRAY_POINTER": 34373,
+ "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975,
+ "IMPLEMENTATION_COLOR_READ_TYPE": 35738,
+ "IMPLEMENTATION_COLOR_READ_FORMAT": 35739,
+ "COMPILE_STATUS": 35713,
+ "LOW_FLOAT": 36336,
+ "MEDIUM_FLOAT": 36337,
+ "HIGH_FLOAT": 36338,
+ "LOW_INT": 36339,
+ "MEDIUM_INT": 36340,
+ "HIGH_INT": 36341,
+ "FRAMEBUFFER": 36160,
+ "RENDERBUFFER": 36161,
+ "RGBA4": 32854,
+ "RGB5_A1": 32855,
+ "RGB565": 36194,
+ "DEPTH_COMPONENT16": 33189,
+ "STENCIL_INDEX8": 36168,
+ "DEPTH_STENCIL": 34041,
+ "RENDERBUFFER_WIDTH": 36162,
+ "RENDERBUFFER_HEIGHT": 36163,
+ "RENDERBUFFER_INTERNAL_FORMAT": 36164,
+ "RENDERBUFFER_RED_SIZE": 36176,
+ "RENDERBUFFER_GREEN_SIZE": 36177,
+ "RENDERBUFFER_BLUE_SIZE": 36178,
+ "RENDERBUFFER_ALPHA_SIZE": 36179,
+ "RENDERBUFFER_DEPTH_SIZE": 36180,
+ "RENDERBUFFER_STENCIL_SIZE": 36181,
+ "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048,
+ "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049,
+ "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050,
+ "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051,
+ "COLOR_ATTACHMENT0": 36064,
+ "DEPTH_ATTACHMENT": 36096,
+ "STENCIL_ATTACHMENT": 36128,
+ "DEPTH_STENCIL_ATTACHMENT": 33306,
+ "NONE": 0,
+ "FRAMEBUFFER_COMPLETE": 36053,
+ "FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054,
+ "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055,
+ "FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057,
+ "FRAMEBUFFER_UNSUPPORTED": 36061,
+ "FRAMEBUFFER_BINDING": 36006,
+ "RENDERBUFFER_BINDING": 36007,
+ "MAX_RENDERBUFFER_SIZE": 34024,
+ "INVALID_FRAMEBUFFER_OPERATION": 1286,
+ "UNPACK_FLIP_Y_WEBGL": 37440,
+ "UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441,
+ "CONTEXT_LOST_WEBGL": 37442,
+ "UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443,
+ "BROWSER_DEFAULT_WEBGL": 37444
+};
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js
new file mode 100644
index 0000000..f2659be
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js
@@ -0,0 +1,142 @@
+let i = 1;
+
+const GLmethod = {};
+
+GLmethod.activeTexture = i++; //1
+GLmethod.attachShader = i++;
+GLmethod.bindAttribLocation = i++;
+GLmethod.bindBuffer = i++;
+GLmethod.bindFramebuffer = i++;
+GLmethod.bindRenderbuffer = i++;
+GLmethod.bindTexture = i++;
+GLmethod.blendColor = i++;
+GLmethod.blendEquation = i++;
+GLmethod.blendEquationSeparate = i++; //10
+GLmethod.blendFunc = i++;
+GLmethod.blendFuncSeparate = i++;
+GLmethod.bufferData = i++;
+GLmethod.bufferSubData = i++;
+GLmethod.checkFramebufferStatus = i++;
+GLmethod.clear = i++;
+GLmethod.clearColor = i++;
+GLmethod.clearDepth = i++;
+GLmethod.clearStencil = i++;
+GLmethod.colorMask = i++; //20
+GLmethod.compileShader = i++;
+GLmethod.compressedTexImage2D = i++;
+GLmethod.compressedTexSubImage2D = i++;
+GLmethod.copyTexImage2D = i++;
+GLmethod.copyTexSubImage2D = i++;
+GLmethod.createBuffer = i++;
+GLmethod.createFramebuffer = i++;
+GLmethod.createProgram = i++;
+GLmethod.createRenderbuffer = i++;
+GLmethod.createShader = i++; //30
+GLmethod.createTexture = i++;
+GLmethod.cullFace = i++;
+GLmethod.deleteBuffer = i++;
+GLmethod.deleteFramebuffer = i++;
+GLmethod.deleteProgram = i++;
+GLmethod.deleteRenderbuffer = i++;
+GLmethod.deleteShader = i++;
+GLmethod.deleteTexture = i++;
+GLmethod.depthFunc = i++;
+GLmethod.depthMask = i++; //40
+GLmethod.depthRange = i++;
+GLmethod.detachShader = i++;
+GLmethod.disable = i++;
+GLmethod.disableVertexAttribArray = i++;
+GLmethod.drawArrays = i++;
+GLmethod.drawArraysInstancedANGLE = i++;
+GLmethod.drawElements = i++;
+GLmethod.drawElementsInstancedANGLE = i++;
+GLmethod.enable = i++;
+GLmethod.enableVertexAttribArray = i++; //50
+GLmethod.flush = i++;
+GLmethod.framebufferRenderbuffer = i++;
+GLmethod.framebufferTexture2D = i++;
+GLmethod.frontFace = i++;
+GLmethod.generateMipmap = i++;
+GLmethod.getActiveAttrib = i++;
+GLmethod.getActiveUniform = i++;
+GLmethod.getAttachedShaders = i++;
+GLmethod.getAttribLocation = i++;
+GLmethod.getBufferParameter = i++; //60
+GLmethod.getContextAttributes = i++;
+GLmethod.getError = i++;
+GLmethod.getExtension = i++;
+GLmethod.getFramebufferAttachmentParameter = i++;
+GLmethod.getParameter = i++;
+GLmethod.getProgramInfoLog = i++;
+GLmethod.getProgramParameter = i++;
+GLmethod.getRenderbufferParameter = i++;
+GLmethod.getShaderInfoLog = i++;
+GLmethod.getShaderParameter = i++; //70
+GLmethod.getShaderPrecisionFormat = i++;
+GLmethod.getShaderSource = i++;
+GLmethod.getSupportedExtensions = i++;
+GLmethod.getTexParameter = i++;
+GLmethod.getUniform = i++;
+GLmethod.getUniformLocation = i++;
+GLmethod.getVertexAttrib = i++;
+GLmethod.getVertexAttribOffset = i++;
+GLmethod.isBuffer = i++;
+GLmethod.isContextLost = i++; //80
+GLmethod.isEnabled = i++;
+GLmethod.isFramebuffer = i++;
+GLmethod.isProgram = i++;
+GLmethod.isRenderbuffer = i++;
+GLmethod.isShader = i++;
+GLmethod.isTexture = i++;
+GLmethod.lineWidth = i++;
+GLmethod.linkProgram = i++;
+GLmethod.pixelStorei = i++;
+GLmethod.polygonOffset = i++; //90
+GLmethod.readPixels = i++;
+GLmethod.renderbufferStorage = i++;
+GLmethod.sampleCoverage = i++;
+GLmethod.scissor = i++;
+GLmethod.shaderSource = i++;
+GLmethod.stencilFunc = i++;
+GLmethod.stencilFuncSeparate = i++;
+GLmethod.stencilMask = i++;
+GLmethod.stencilMaskSeparate = i++;
+GLmethod.stencilOp = i++; //100
+GLmethod.stencilOpSeparate = i++;
+GLmethod.texImage2D = i++;
+GLmethod.texParameterf = i++;
+GLmethod.texParameteri = i++;
+GLmethod.texSubImage2D = i++;
+GLmethod.uniform1f = i++;
+GLmethod.uniform1fv = i++;
+GLmethod.uniform1i = i++;
+GLmethod.uniform1iv = i++;
+GLmethod.uniform2f = i++; //110
+GLmethod.uniform2fv = i++;
+GLmethod.uniform2i = i++;
+GLmethod.uniform2iv = i++;
+GLmethod.uniform3f = i++;
+GLmethod.uniform3fv = i++;
+GLmethod.uniform3i = i++;
+GLmethod.uniform3iv = i++;
+GLmethod.uniform4f = i++;
+GLmethod.uniform4fv = i++;
+GLmethod.uniform4i = i++; //120
+GLmethod.uniform4iv = i++;
+GLmethod.uniformMatrix2fv = i++;
+GLmethod.uniformMatrix3fv = i++;
+GLmethod.uniformMatrix4fv = i++;
+GLmethod.useProgram = i++;
+GLmethod.validateProgram = i++;
+GLmethod.vertexAttrib1f = i++; //new
+GLmethod.vertexAttrib2f = i++; //new
+GLmethod.vertexAttrib3f = i++; //new
+GLmethod.vertexAttrib4f = i++; //new //130
+GLmethod.vertexAttrib1fv = i++; //new
+GLmethod.vertexAttrib2fv = i++; //new
+GLmethod.vertexAttrib3fv = i++; //new
+GLmethod.vertexAttrib4fv = i++; //new
+GLmethod.vertexAttribPointer = i++;
+GLmethod.viewport = i++;
+
+export default GLmethod;
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js
new file mode 100644
index 0000000..695abcb
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js
@@ -0,0 +1,23 @@
+const GLtype = {};
+
+[
+ "GLbitfield",
+ "GLboolean",
+ "GLbyte",
+ "GLclampf",
+ "GLenum",
+ "GLfloat",
+ "GLint",
+ "GLintptr",
+ "GLsizei",
+ "GLsizeiptr",
+ "GLshort",
+ "GLubyte",
+ "GLuint",
+ "GLushort"
+].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1));
+
+export default GLtype;
+
+
+
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js
new file mode 100644
index 0000000..6f5691c
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLProgram';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLProgram {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js
new file mode 100644
index 0000000..d3182ae
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js
@@ -0,0 +1,21 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLRenderBuffer';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLRenderbuffer {
+ className = name;
+
+ constructor(id) {
+ this.id = id;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js
new file mode 100644
index 0000000..5f9608f
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js
@@ -0,0 +1,1191 @@
+import GLenum from './GLenum';
+import ActiveInfo from './ActiveInfo';
+import Buffer from './Buffer';
+import Framebuffer from './Framebuffer';
+import Renderbuffer from './Renderbuffer';
+import Texture from './Texture';
+import Program from './Program';
+import Shader from './Shader';
+import ShaderPrecisionFormat from './ShaderPrecisionFormat';
+import UniformLocation from './UniformLocation';
+import GLmethod from './GLmethod';
+
+const processArray = (array, checkArrayType = false) => {
+
+ function joinArray(arr, sep) {
+ let res = '';
+ for (let i = 0; i < arr.length; i++) {
+ if (i !== 0) {
+ res += sep;
+ }
+ res += arr[i];
+ }
+ return res;
+ }
+
+ let type = 'Float32Array';
+ if (checkArrayType) {
+ if (array instanceof Uint8Array) {
+ type = 'Uint8Array'
+ } else if (array instanceof Uint16Array) {
+ type = 'Uint16Array';
+ } else if (array instanceof Uint32Array) {
+ type = 'Uint32Array';
+ } else if (array instanceof Float32Array) {
+ type = 'Float32Array';
+ } else {
+ throw new Error('Check array type failed. Array type is ' + typeof array);
+ }
+ }
+
+ const ArrayTypes = {
+ Uint8Array: 1,
+ Uint16Array: 2,
+ Uint32Array: 4,
+ Float32Array: 14
+ };
+ return ArrayTypes[type] + ',' + btoa(joinArray(array, ','))
+}
+
+export default class WebGLRenderingContext {
+
+ // static GBridge = null;
+
+ className = 'WebGLRenderingContext';
+
+ constructor(canvas, type, attrs) {
+ this._canvas = canvas;
+ this._type = type;
+ this._version = 'WebGL 1.0';
+ this._attrs = attrs;
+ this._map = new Map();
+
+ Object.keys(GLenum)
+ .forEach(name => Object.defineProperty(this, name, {
+ value: GLenum[name]
+ }));
+ }
+
+ get canvas() {
+ return this._canvas;
+ }
+
+ activeTexture = function (textureUnit) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.activeTexture + ',' + textureUnit,
+ true
+ );
+ }
+
+ attachShader = function (progarm, shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.attachShader + ',' + progarm.id + ',' + shader.id,
+ true
+ );
+ }
+
+ bindAttribLocation = function (program, index, name) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindAttribLocation + ',' + program.id + ',' + index + ',' + name,
+ true
+ )
+ }
+
+ bindBuffer = function (target, buffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindBuffer + ',' + target + ',' + (buffer ? buffer.id : 0),
+ true
+ );
+ }
+
+ bindFramebuffer = function (target, framebuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindFramebuffer + ',' + target + ',' + (framebuffer ? framebuffer.id : 0),
+ true
+ )
+ }
+
+ bindRenderbuffer = function (target, renderBuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindRenderbuffer + ',' + target + ',' + (renderBuffer ? renderBuffer.id : 0),
+ true
+ )
+ }
+
+ bindTexture = function (target, texture) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bindTexture + ',' + target + ',' + (texture ? texture.id : 0),
+ true
+ )
+ }
+
+ blendColor = function (r, g, b, a) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendColor + ',' + target + ',' + r + ',' + g + ',' + b + ',' + a,
+ true
+ )
+ }
+
+ blendEquation = function (mode) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendEquation + ',' + mode,
+ true
+ )
+ }
+
+ blendEquationSeparate = function (modeRGB, modeAlpha) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendEquationSeparate + ',' + modeRGB + ',' + modeAlpha,
+ true
+ )
+ }
+
+
+ blendFunc = function (sfactor, dfactor) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendFunc + ',' + sfactor + ',' + dfactor,
+ true
+ );
+ }
+
+ blendFuncSeparate = function (srcRGB, dstRGB, srcAlpha, dstAlpha) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.blendFuncSeparate + ',' + srcRGB + ',' + dstRGB + ',' + srcAlpha + ',' + dstAlpha,
+ true
+ );
+ }
+
+ bufferData = function (target, data, usage) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bufferData + ',' + target + ',' + processArray(data, true) + ',' + usage,
+ true
+ )
+ }
+
+ bufferSubData = function (target, offset, data) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.bufferSubData + ',' + target + ',' + offset + ',' + processArray(data, true),
+ true
+ )
+ }
+
+ checkFramebufferStatus = function (target) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.checkFramebufferStatus + ',' + target
+ );
+ return Number(result);
+ }
+
+ clear = function (mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clear + ',' + mask
+ );
+ this._canvas._needRender = true;
+ }
+
+ clearColor = function (r, g, b, a) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clearColor + ',' + r + ',' + g + ',' + b,
+ true
+ )
+ }
+
+ clearDepth = function (depth) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clearDepth + ',' + depth,
+ true
+ )
+ }
+
+ clearStencil = function (s) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.clearStencil + ',' + s
+ );
+ }
+
+ colorMask = function (r, g, b, a) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.colorMask + ',' + r + ',' + g + ',' + b + ',' + a
+ )
+ }
+
+ compileShader = function (shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.compileShader + ',' + shader.id,
+ true
+ )
+ }
+
+ compressedTexImage2D = function (target, level, internalformat, width, height, border, pixels) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.compressedTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' +
+ width + ',' + height + ',' + border + ',' + processArray(pixels),
+ true
+ )
+ }
+
+ compressedTexSubImage2D = function (target, level, xoffset, yoffset, width, height, format, pixels) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.compressedTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' +
+ width + ',' + height + ',' + format + ',' + processArray(pixels),
+ true
+ )
+ }
+
+
+ copyTexImage2D = function (target, level, internalformat, x, y, width, height, border) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.copyTexImage2D + ',' + target + ',' + level + ',' + internalformat + ',' + x + ',' + y + ',' +
+ width + ',' + height + ',' + border,
+ true
+ );
+ }
+
+ copyTexSubImage2D = function (target, level, xoffset, yoffset, x, y, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.copyTexSubImage2D + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset + ',' + x + ',' + y + ',' +
+ width + ',' + height
+ );
+ }
+
+ createBuffer = function () {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createBuffer + ''
+ );
+ const buffer = new Buffer(result);
+ this._map.set(buffer.uuid(), buffer);
+ return buffer;
+ }
+
+ createFramebuffer = function () {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createFramebuffer + ''
+ );
+ const framebuffer = new Framebuffer(result);
+ this._map.set(framebuffer.uuid(), framebuffer);
+ return framebuffer;
+ }
+
+
+ createProgram = function () {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createProgram + ''
+ );
+ const program = new Program(id);
+ this._map.set(program.uuid(), program);
+ return program;
+ }
+
+ createRenderbuffer = function () {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createRenderbuffer + ''
+ )
+ const renderBuffer = new Renderbuffer(id);
+ this._map.set(renderBuffer.uuid(), renderBuffer);
+ return renderBuffer;
+ }
+
+ createShader = function (type) {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createShader + ',' + type
+ )
+ const shader = new Shader(id, type);
+ this._map.set(shader.uuid(), shader);
+ return shader;
+ }
+
+ createTexture = function () {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.createTexture + ''
+ );
+ const texture = new Texture(id);
+ this._map.set(texture.uuid(), texture);
+ return texture;
+ }
+
+ cullFace = function (mode) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.cullFace + ',' + mode,
+ true
+ )
+ }
+
+
+ deleteBuffer = function (buffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteBuffer + ',' + buffer.id,
+ true
+ )
+ }
+
+ deleteFramebuffer = function (framebuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteFramebuffer + ',' + framebuffer.id,
+ true
+ )
+ }
+
+ deleteProgram = function (program) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteProgram + ',' + program.id,
+ true
+ )
+ }
+
+ deleteRenderbuffer = function (renderbuffer) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteRenderbuffer + ',' + renderbuffer.id,
+ true
+ )
+ }
+
+ deleteShader = function (shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteShader + ',' + shader.id,
+ true
+ )
+ }
+
+ deleteTexture = function (texture) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.deleteTexture + ',' + texture.id,
+ true
+ )
+ }
+
+ depthFunc = function (func) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.depthFunc + ',' + func
+ )
+ }
+
+ depthMask = function (flag) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.depthMask + ',' + Number(flag),
+ true
+ )
+ }
+
+ depthRange = function (zNear, zFar) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.depthRange + ',' + zNear + ',' + zFar,
+ true
+ )
+ }
+
+ detachShader = function (program, shader) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.detachShader + ',' + program.id + ',' + shader.id,
+ true
+ )
+ }
+
+ disable = function (cap) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.disable + ',' + cap,
+ true
+ )
+ }
+
+ disableVertexAttribArray = function (index) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.disableVertexAttribArray + ',' + index,
+ true
+ );
+ }
+
+ drawArrays = function (mode, first, count) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.drawArrays + ',' + mode + ',' + first + ',' + count
+ )
+ this._canvas._needRender = true;
+ }
+
+ drawElements = function (mode, count, type, offset) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.drawElements + ',' + mode + ',' + count + ',' + type + ',' + offset + ';'
+ );
+ this._canvas._needRender = true;
+ }
+
+ enable = function (cap) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.enable + ',' + cap,
+ true
+ );
+ }
+
+ enableVertexAttribArray = function (index) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.enableVertexAttribArray + ',' + index,
+ true
+ )
+ }
+
+
+ flush = function () {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.flush + ''
+ )
+ }
+
+ framebufferRenderbuffer = function (target, attachment, textarget, texture, level) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.framebufferRenderbuffer + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level,
+ true
+ )
+ }
+
+ framebufferTexture2D = function (target, attachment, textarget, texture, level) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.framebufferTexture2D + ',' + target + ',' + attachment + ',' + textarget + ',' + (texture ? texture.id : 0) + ',' + level,
+ true
+ )
+ }
+
+ frontFace = function (mode) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.frontFace + ',' + mode,
+ true
+ )
+ }
+
+ generateMipmap = function (target) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.generateMipmap + ',' + target,
+ true
+ )
+ }
+
+ getActiveAttrib = function (progarm, index) {
+ const resultString = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getActiveAttrib + ',' + progarm.id + ',' + index
+ )
+ const [type, size, name] = resultString.split(',');
+ return new ActiveInfo({
+ type: Number(type),
+ size: Number(size),
+ name
+ });
+ }
+
+ getActiveUniform = function (progarm, index) {
+ const resultString = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getActiveUniform + ',' + progarm.id + ',' + index
+ );
+ const [type, size, name] = resultString.split(',');
+ return new ActiveInfo({
+ type: Number(type),
+ size: Number(size),
+ name
+ })
+ }
+
+ getAttachedShaders = function (progarm) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getAttachedShaders + ',' + progarm.id
+ );
+ const [type, ...ids] = result;
+ return ids.map(id => this._map.get(Shader.uuid(id)));
+ }
+
+ getAttribLocation = function (progarm, name) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getAttribLocation + ',' + progarm.id + ',' + name
+ )
+ }
+
+ getBufferParameter = function (target, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getBufferParameter + ',' + target + ',' + pname
+ );
+ const [type, res] = getBufferParameter;
+ return res;
+ }
+
+ getError = function () {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getError + ''
+ )
+ return result;
+ }
+
+ getExtension = function (name) {
+ return null;
+ }
+
+ getFramebufferAttachmentParameter = function (target, attachment, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getFramebufferAttachmentParameter + ',' + target + ',' + attachment + ',' + pname
+ )
+ switch (pname) {
+ case GLenum.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
+ return this._map.get(Renderbuffer.uuid(result)) || this._map.get(Texture.uuid(result)) || null;
+ default:
+ return result;
+ }
+ }
+
+ getParameter = function (pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getParameter + ',' + pname
+ )
+ switch (pname) {
+ case GLenum.VERSION:
+ return this._version;
+ case GLenum.ARRAY_BUFFER_BINDING: // buffer
+ case GLenum.ELEMENT_ARRAY_BUFFER_BINDING: // buffer
+ return this._map.get(Buffer.uuid(result)) || null;
+ case GLenum.CURRENT_PROGRAM: // program
+ return this._map.get(Program.uuid(result)) || null;
+ case GLenum.FRAMEBUFFER_BINDING: // framebuffer
+ return this._map.get(Framebuffer.uuid(result)) || null;
+ case GLenum.RENDERBUFFER_BINDING: // renderbuffer
+ return this._map.get(Renderbuffer.uuid(result)) || null;
+ case GLenum.TEXTURE_BINDING_2D: // texture
+ case GLenum.TEXTURE_BINDING_CUBE_MAP: // texture
+ return this._map.get(Texture.uuid(result)) || null;
+ case GLenum.ALIASED_LINE_WIDTH_RANGE: // Float32Array
+ case GLenum.ALIASED_POINT_SIZE_RANGE: // Float32Array
+ case GLenum.BLEND_COLOR: // Float32Array
+ case GLenum.COLOR_CLEAR_VALUE: // Float32Array
+ case GLenum.DEPTH_RANGE: // Float32Array
+ case GLenum.MAX_VIEWPORT_DIMS: // Int32Array
+ case GLenum.SCISSOR_BOX: // Int32Array
+ case GLenum.VIEWPORT: // Int32Array
+ case GLenum.COMPRESSED_TEXTURE_FORMATS: // Uint32Array
+ default:
+ const [type, ...res] = result.split(',');
+ if (res.length === 1) {
+ return Number(res[0]);
+ } else {
+ return res.map(Number);
+ }
+ }
+ }
+
+ getProgramInfoLog = function (progarm) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getProgramInfoLog + ',' + progarm.id
+ )
+ }
+
+ getProgramParameter = function (program, pname) {
+ const res = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getProgramParameter + ',' + program.id + ',' + pname
+ );
+
+ const [type, result] = res.split(',').map(i => parseInt(i));
+
+ if (type === 1) {
+ return Boolean(result);
+ } else if (type === 2) {
+ return result;
+ } else {
+ throw new Error('Unrecongized program paramater ' + res + ', type: ' + typeof res);
+ }
+ }
+
+
+ getRenderbufferParameter = function (target, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getRenderbufferParameter + ',' + target + ',' + pname
+ )
+ return result;
+ }
+
+
+ getShaderInfoLog = function (shader) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderInfoLog + ',' + shader.id
+ );
+ }
+
+ getShaderParameter = function (shader, pname) {
+ return WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderParameter + ',' + shader.id + ',' + pname
+ )
+ }
+
+ getShaderPrecisionFormat = function (shaderType, precisionType) {
+ const [rangeMin, rangeMax, precision] = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderPrecisionFormat + ',' + shaderType + ',' + precisionType
+ );
+ const shaderPrecisionFormat = new ShaderPrecisionFormat({
+ rangeMin: Number(rangeMin),
+ rangeMax: Number(rangeMax),
+ precision: Number(precision)
+ });
+ return shaderPrecisionFormat;
+ }
+
+ getShaderSource = function (shader) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getShaderSource + ',' + shader.id
+ );
+ return result;
+ }
+
+ getSupportedExtensions = function () {
+ return Object.keys({});
+ }
+
+ getTexParameter = function (target, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getTexParameter + ',' + target + ',' + pname
+ )
+ return result;
+ }
+
+ getUniformLocation = function (program, name) {
+ const id = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getUniformLocation + ',' + program.id + ',' + name
+ );
+ if (id === -1) {
+ return null;
+ } else {
+ return new UniformLocation(Number(id));
+ }
+ }
+
+ getVertexAttrib = function (index, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getVertexAttrib + ',' + index + ',' + pname
+ );
+ switch (pname) {
+ case GLenum.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+ return this._map.get(Buffer.uuid(result)) || null;
+ case GLenum.CURRENT_VERTEX_ATTRIB: // Float32Array
+ default:
+ return result;
+ }
+ }
+
+ getVertexAttribOffset = function (index, pname) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.getVertexAttribOffset + ',' + index + ',' + pname
+ )
+ return Number(result);
+ }
+
+ isBuffer = function (buffer) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isBuffer + ',' + buffer.id
+ )
+ return Boolean(result);
+ }
+
+ isContextLost = function () {
+ return false;
+ }
+
+ isEnabled = function (cap) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isEnabled + ',' + cap
+ )
+ return Boolean(result);
+ }
+
+ isFramebuffer = function (framebuffer) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isFramebuffer + ',' + framebuffer.id
+ )
+ return Boolean(result);
+ }
+
+ isProgram = function (program) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isProgram + ',' + program.id
+ )
+ return Boolean(result);
+ }
+
+ isRenderbuffer = function (renderBuffer) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isRenderbuffer + ',' + renderbuffer.id
+ )
+ return Boolean(result);
+ }
+
+ isShader = function (shader) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isShader + ',' + shader.id
+ )
+ return Boolean(result);
+ }
+
+ isTexture = function (texture) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.isTexture + ',' + texture.id
+ );
+ return Boolean(result);
+ }
+
+ lineWidth = function (width) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.lineWidth + ',' + width,
+ true
+ )
+ }
+
+ linkProgram = function (program) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.linkProgram + ',' + program.id,
+ true
+ );
+ }
+
+
+ pixelStorei = function (pname, param) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.pixelStorei + ',' + pname + ',' + Number(param)
+ )
+ }
+
+ polygonOffset = function (factor, units) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.polygonOffset + ',' + factor + ',' + units
+ )
+ }
+
+ readPixels = function (x, y, width, height, format, type, pixels) {
+ const result = WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.readPixels + ',' + x + ',' + y + ',' + width + ',' + height + ',' + format + ',' + type
+ )
+ return result;
+ }
+
+ renderbufferStorage = function (target, internalFormat, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.renderbufferStorage + ',' + target + ',' + internalFormat + ',' + width + ',' + height,
+ true
+ )
+ }
+
+ sampleCoverage = function (value, invert) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.sampleCoverage + ',' + value + ',' + Number(invert),
+ true
+ )
+ }
+
+ scissor = function (x, y, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.scissor + ',' + x + ',' + y + ',' + width + ',' + height,
+ true
+ )
+ }
+
+ shaderSource = function (shader, source) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.shaderSource + ',' + shader.id + ',' + source
+ )
+ }
+
+ stencilFunc = function (func, ref, mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilFunc + ',' + func + ',' + ref + ',' + mask,
+ true
+ )
+ }
+
+ stencilFuncSeparate = function (face, func, ref, mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilFuncSeparate + ',' + face + ',' + func + ',' + ref + ',' + mask,
+ true
+ )
+ }
+
+ stencilMask = function (mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilMask + ',' + mask,
+ true
+ )
+ }
+
+ stencilMaskSeparate = function (face, mask) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilMaskSeparate + ',' + face + ',' + mask,
+ true
+ )
+ }
+
+ stencilOp = function (fail, zfail, zpass) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilOp + ',' + fail + ',' + zfail + ',' + zpass
+ )
+ }
+
+ stencilOpSeparate = function (face, fail, zfail, zpass) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.stencilOp + ',' + face + ',' + fail + ',' + zfail + ',' + zpass,
+ true
+ )
+ }
+
+ texImage2D = function (...args) {
+ WebGLRenderingContext.GBridge.texImage2D(this._canvas.id, ...args);
+ }
+
+
+ texParameterf = function (target, pname, param) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.texParameterf + ',' + target + ',' + pname + ',' + param,
+ true
+ )
+ }
+
+ texParameteri = function (target, pname, param) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.texParameteri + ',' + target + ',' + pname + ',' + param
+ )
+ }
+
+ texSubImage2D = function (...args) {
+ WebGLRenderingContext.GBridge.texSubImage2D(this._canvas.id, ...args);
+ }
+
+ uniform1f = function (location, v0) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1f + ',' + location.id + ',' + v0
+ )
+ }
+
+ uniform1fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform1i = function (location, v0) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1i + ',' + location.id + ',' + v0,
+ // true
+ )
+ }
+
+ uniform1iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform1iv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform2f = function (location, v0, v1) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2f + ',' + location.id + ',' + v0 + ',' + v1,
+ true
+ )
+ }
+
+ uniform2fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform2i = function (location, v0, v1) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2i + ',' + location.id + ',' + v0 + ',' + v1,
+ true
+ )
+ }
+
+ uniform2iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform2iv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform3f = function (location, v0, v1, v2) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2,
+ true
+ )
+ }
+
+ uniform3fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform3i = function (location, v0, v1, v2) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2,
+ true
+ )
+ }
+
+ uniform3iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform3iv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform4f = function (location, v0, v1, v2, v3) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4f + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3,
+ true
+ )
+ }
+
+ uniform4fv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4fv + ',' + location.id + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniform4i = function (location, v0, v1, v2, v3) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4i + ',' + location.id + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3,
+ true
+ )
+ }
+
+ uniform4iv = function (location, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniform4iv + ',' + location.id + ',' + processArray(value, true),
+ true
+ )
+ }
+
+ uniformMatrix2fv = function (location, transpose, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniformMatrix2fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniformMatrix3fv = function (location, transpose, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniformMatrix3fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value),
+ true
+ )
+ }
+
+ uniformMatrix4fv = function (location, transpose, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.uniformMatrix4fv + ',' + location.id + ',' + Number(transpose) + ',' + processArray(value),
+ true
+ );
+ }
+
+ useProgram = function (progarm) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.useProgram + ',' + progarm.id + '',
+ true
+ )
+ }
+
+
+ validateProgram = function (program) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.validateProgram + ',' + program.id,
+ true
+ )
+ }
+
+ vertexAttrib1f = function (index, v0) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib1f + ',' + index + ',' + v0,
+ true
+ )
+ }
+
+ vertexAttrib2f = function (index, v0, v1) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib2f + ',' + index + ',' + v0 + ',' + v1,
+ true
+ )
+ }
+
+ vertexAttrib3f = function (index, v0, v1, v2) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib3f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2,
+ true
+ )
+ }
+
+ vertexAttrib4f = function (index, v0, v1, v2, v3) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib4f + ',' + index + ',' + v0 + ',' + v1 + ',' + v2 + ',' + v3,
+ true
+ )
+ }
+
+ vertexAttrib1fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib1fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttrib2fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib2fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttrib3fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib3fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttrib4fv = function (index, value) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttrib4fv + ',' + index + ',' + processArray(value),
+ true
+ )
+ }
+
+ vertexAttribPointer = function (index, size, type, normalized, stride, offset) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.vertexAttribPointer + ',' + index + ',' + size + ',' + type + ',' + Number(normalized) + ',' + stride + ',' + offset,
+ true
+ )
+ }
+
+ viewport = function (x, y, width, height) {
+ WebGLRenderingContext.GBridge.callNative(
+ this._canvas.id,
+ GLmethod.viewport + ',' + x + ',' + y + ',' + width + ',' + height,
+ true
+ )
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js
new file mode 100644
index 0000000..a763886
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js
@@ -0,0 +1,22 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLShader';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLShader {
+ className = name;
+
+ constructor(id, type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js
new file mode 100644
index 0000000..208d6c1
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js
@@ -0,0 +1,11 @@
+export default class WebGLShaderPrecisionFormat {
+ className = 'WebGLShaderPrecisionFormat';
+
+ constructor({
+ rangeMin, rangeMax, precision
+ }) {
+ this.rangeMin = rangeMin;
+ this.rangeMax = rangeMax;
+ this.precision = precision;
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js
new file mode 100644
index 0000000..de4d806
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js
@@ -0,0 +1,22 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLTexture';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLTexture {
+ className = name;
+
+ constructor(id, type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js
new file mode 100644
index 0000000..f5e99dc
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js
@@ -0,0 +1,22 @@
+import {getTransferedObjectUUID} from './classUtils';
+
+const name = 'WebGLUniformLocation';
+
+function uuid(id) {
+ return getTransferedObjectUUID(name, id);
+}
+
+export default class WebGLUniformLocation {
+ className = name;
+
+ constructor(id, type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ static uuid = uuid;
+
+ uuid() {
+ return uuid(this.id);
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js
new file mode 100644
index 0000000..88716be
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js
@@ -0,0 +1,3 @@
+export function getTransferedObjectUUID(name, id) {
+ return `${name.toLowerCase()}-${id}`;
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js
new file mode 100644
index 0000000..a8d9bb9
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js
@@ -0,0 +1,74 @@
+import GContext2D from '../context-2d/RenderingContext';
+import GContextWebGL from '../context-webgl/RenderingContext';
+
+export default class GCanvas {
+
+ // static GBridge = null;
+
+ id = null;
+
+ _needRender = true;
+
+ constructor(id, { disableAutoSwap }) {
+ this.id = id;
+
+ this._disableAutoSwap = disableAutoSwap;
+ if (disableAutoSwap) {
+ this._swapBuffers = () => {
+ GCanvas.GBridge.render(this.id);
+ }
+ }
+ }
+
+ getContext(type) {
+
+ let context = null;
+
+ if (type.match(/webgl/i)) {
+ context = new GContextWebGL(this);
+
+ context.componentId = this.id;
+
+ if (!this._disableAutoSwap) {
+ const render = () => {
+ if (this._needRender) {
+ GCanvas.GBridge.render(this.id);
+ this._needRender = false;
+ }
+ }
+ setInterval(render, 16);
+ }
+
+ GCanvas.GBridge.callSetContextType(this.id, 1); // 0 for 2d; 1 for webgl
+ } else if (type.match(/2d/i)) {
+ context = new GContext2D(this);
+
+ context.componentId = this.id;
+
+// const render = ( callback ) => {
+//
+// const commands = context._drawCommands;
+// context._drawCommands = '';
+//
+// GCanvas.GBridge.render2d(this.id, commands, callback);
+// this._needRender = false;
+// }
+// //draw方法触发
+// context._flush = render;
+// //setInterval(render, 16);
+
+ GCanvas.GBridge.callSetContextType(this.id, 0);
+ } else {
+ throw new Error('not supported context ' + type);
+ }
+
+ return context;
+
+ }
+
+ reset() {
+ GCanvas.GBridge.callReset(this.id);
+ }
+
+
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js
new file mode 100644
index 0000000..9499a51
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js
@@ -0,0 +1,96 @@
+let incId = 1;
+
+const noop = function () { };
+
+class GImage {
+
+ static GBridge = null;
+
+ constructor() {
+ this._id = incId++;
+ this._width = 0;
+ this._height = 0;
+ this._src = undefined;
+ this._onload = noop;
+ this._onerror = noop;
+ this.complete = false;
+ }
+
+ get width() {
+ return this._width;
+ }
+ set width(v) {
+ this._width = v;
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(v) {
+ this._height = v;
+ }
+
+ get src() {
+ return this._src;
+ }
+
+ set src(v) {
+
+ if (v.startsWith('//')) {
+ v = 'http:' + v;
+ }
+
+ this._src = v;
+
+ GImage.GBridge.perloadImage([this._src, this._id], (data) => {
+ if (typeof data === 'string') {
+ data = JSON.parse(data);
+ }
+ if (data.error) {
+ var evt = { type: 'error', target: this };
+ this.onerror(evt);
+ } else {
+ this.complete = true;
+ this.width = typeof data.width === 'number' ? data.width : 0;
+ this.height = typeof data.height === 'number' ? data.height : 0;
+ var evt = { type: 'load', target: this };
+ this.onload(evt);
+ }
+ });
+ }
+
+ addEventListener(name, listener) {
+ if (name === 'load') {
+ this.onload = listener;
+ } else if (name === 'error') {
+ this.onerror = listener;
+ }
+ }
+
+ removeEventListener(name, listener) {
+ if (name === 'load') {
+ this.onload = noop;
+ } else if (name === 'error') {
+ this.onerror = noop;
+ }
+ }
+
+ get onload() {
+ return this._onload;
+ }
+
+ set onload(v) {
+ this._onload = v;
+ }
+
+ get onerror() {
+ return this._onerror;
+ }
+
+ set onerror(v) {
+ this._onerror = v;
+ }
+}
+
+export default GImage;
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js
new file mode 100644
index 0000000..d3fb398
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js
@@ -0,0 +1,24 @@
+
+export function ArrayBufferToBase64 (buffer) {
+ var binary = '';
+ var bytes = new Uint8ClampedArray(buffer);
+ for (var len = bytes.byteLength, i = 0; i < len; i++) {
+ binary += String.fromCharCode(bytes[i]);
+ }
+ return btoa(binary);
+}
+
+export function Base64ToUint8ClampedArray(base64String) {
+ const padding = '='.repeat((4 - base64String.length % 4) % 4);
+ const base64 = (base64String + padding)
+ .replace(/\-/g, '+')
+ .replace(/_/g, '/');
+
+ const rawData = atob(base64);
+ const outputArray = new Uint8ClampedArray(rawData.length);
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i);
+ }
+ return outputArray;
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js
new file mode 100644
index 0000000..a34ad58
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js
@@ -0,0 +1,39 @@
+import GCanvas from './env/canvas';
+import GImage from './env/image';
+
+import GWebGLRenderingContext from './context-webgl/RenderingContext';
+import GContext2D from './context-2d/RenderingContext';
+
+import GBridgeWeex from './bridge/bridge-weex';
+
+export let Image = GImage;
+
+export let WeexBridge = GBridgeWeex;
+
+export function enable(el, { bridge, debug, disableAutoSwap, disableComboCommands } = {}) {
+
+ const GBridge = GImage.GBridge = GCanvas.GBridge = GWebGLRenderingContext.GBridge = GContext2D.GBridge = bridge;
+
+ GBridge.callEnable(el.ref, [
+ 0, // renderMode: 0--RENDERMODE_WHEN_DIRTY, 1--RENDERMODE_CONTINUOUSLY
+ -1, // hybridLayerType: 0--LAYER_TYPE_NONE 1--LAYER_TYPE_SOFTWARE 2--LAYER_TYPE_HARDWARE
+ false, // supportScroll
+ false, // newCanvasMode
+ 1, // compatible
+ 'white',// clearColor
+ false // sameLevel: newCanvasMode = true && true => GCanvasView and Webview is same level
+ ]);
+
+ if (debug === true) {
+ GBridge.callEnableDebug();
+ }
+ if (disableComboCommands) {
+ GBridge.callEnableDisableCombo();
+ }
+
+ var canvas = new GCanvas(el.ref, { disableAutoSwap });
+ canvas.width = el.style.width;
+ canvas.height = el.style.height;
+
+ return canvas;
+};
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/props.js b/uni_modules/uv-qrcode/components/uv-qrcode/props.js
new file mode 100644
index 0000000..55df5bb
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/props.js
@@ -0,0 +1,85 @@
+export default {
+ props: {
+ //二维码内容
+ value: {
+ type: [String, Number]
+ },
+ //选项
+ options: {
+ type: Object,
+ default: () => {
+ return {};
+ }
+ },
+ //二维码大小
+ size: {
+ type: [String, Number],
+ default: 200
+ },
+ //导出的文件类型
+ fileType: {
+ type: String,
+ default: 'png'
+ },
+ //是否初始化组件后就开始生成
+ start: {
+ type: Boolean,
+ default: true
+ },
+ //是否数据发生改变自动重绘
+ auto: {
+ type: Boolean,
+ default: true
+ },
+ //隐藏组件
+ hide: {
+ type: Boolean,
+ default: false
+ },
+ /**
+ * canvas 类型,微信小程序默认使用2d,非2d微信官方已放弃维护,问题比较多
+ * 注意:微信小程序type2d手机上正常,PC上微信内打开小程序toDataURL报错,看后期微信官方团队会不会做兼容,不兼容的话只能在自行判断在PC使用非2d,或者直接提示用户请在手机上操作,微信团队的海报中心小程序就是这么做的
+ */
+ type: {
+ type: String,
+ default: () => {
+ // #ifdef MP-WEIXIN
+ return '2d';
+ // #endif
+ // #ifndef MP-WEIXIN
+ return 'normal';
+ // #endif
+ }
+ },
+ //队列绘制,主要针对NVue端
+ queue: {
+ type: Boolean,
+ default: false
+ },
+ //是否队列加载图片,可减少canvas发起的网络资源请求,节省服务器资源
+ isQueueLoadImage: {
+ type: Boolean,
+ default: false
+ },
+ //loading态
+ loading: {
+ type: Boolean,
+ default: undefined
+ },
+ //H5保存即自动下载(在支持的环境下),默认false为仅弹层提示用户需要长按图片保存,不会自动下载
+ h5SaveIsDownload: {
+ type: Boolean,
+ default: false
+ },
+ //H5下载名称
+ h5DownloadName: {
+ type: String,
+ default: 'uvQRCode'
+ },
+ // H5保存二维码时候是否显示提示
+ h5SaveTip: {
+ type: Boolean,
+ default: true
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js b/uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js
new file mode 100644
index 0000000..2290ab3
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js
@@ -0,0 +1,34 @@
+//---------------------------------------------------------------------
+// uQRCode二维码生成插件 v4.0.6
+//
+// uQRCode是一款基于Javascript环境开发的二维码生成插件,适用所有Javascript运行环境的前端应用和Node.js。
+//
+// Copyright (c) Sansnn uQRCode All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0.
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// github地址:
+// https://github.com/Sansnn/uQRCode
+//
+// npm地址:
+// https://www.npmjs.com/package/uqrcodejs
+//
+// uni-app插件市场地址:
+// https://ext.dcloud.net.cn/plugin?id=1287
+//
+// 复制使用请保留本段注释,感谢支持开源!
+//
+//---------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// 当前文件格式为 es,将 bundle 保留为 ES 模块文件,适用于其他打包工具以及支持 <script type=module> 标签的浏览器(别名: esm,module)
+// 如需在其他环境使用,请获取环境对应的格式文件
+// 格式说明:
+// amd - 异步模块定义,适用于 RequireJS 等模块加载器
+// cjs - CommonJS,适用于 Node 环境和其他打包工具(别名:commonjs)
+// es - 将 bundle 保留为 ES 模块文件,适用于其他打包工具以及支持 <script type=module> 标签的浏览器(别名: esm,module)
+// umd - 通用模块定义,生成的包同时支持 amd、cjs 和 iife 三种格式
+//---------------------------------------------------------------------
+
+function o(o){this.mode=r.MODE_8BIT_BYTE,this.data=o;}function e(o,e){this.typeNumber=o,this.errorCorrectLevel=e,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=new Array;}o.prototype={getLength:function(o){return this.data.length},write:function(o){for(var e=0;e<this.data.length;e++)o.put(this.data.charCodeAt(e),8);}},e.prototype={addData:function(e){var r=new o(e);this.dataList.push(r),this.dataCache=null;},isDark:function(o,e){if(o<0||this.moduleCount<=o||e<0||this.moduleCount<=e)throw new Error(o+","+e);return this.modules[o][e]},getModuleCount:function(){return this.moduleCount},make:function(){if(this.typeNumber<1){var o=1;for(o=1;o<40;o++){for(var e=v.getRSBlocks(o,this.errorCorrectLevel),r=new p,t=0,i=0;i<e.length;i++)t+=e[i].dataCount;for(i=0;i<this.dataList.length;i++){var n=this.dataList[i];r.put(n.mode,4),r.put(n.getLength(),h.getLengthInBits(n.mode,o)),n.write(r);}if(r.getLengthInBits()<=8*t)break}this.typeNumber=o;}this.makeImpl(!1,this.getBestMaskPattern());},makeImpl:function(o,r){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var t=0;t<this.moduleCount;t++){this.modules[t]=new Array(this.moduleCount);for(var i=0;i<this.moduleCount;i++)this.modules[t][i]=null;}this.setupPositionProbePattern(0,0),this.setupPositionProbePattern(this.moduleCount-7,0),this.setupPositionProbePattern(0,this.moduleCount-7),this.setupPositionAdjustPattern(),this.setupTimingPattern(),this.setupTypeInfo(o,r),this.typeNumber>=7&&this.setupTypeNumber(o),null==this.dataCache&&(this.dataCache=e.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,r);},setupPositionProbePattern:function(o,e){for(var r=-1;r<=7;r++)if(!(o+r<=-1||this.moduleCount<=o+r))for(var t=-1;t<=7;t++)e+t<=-1||this.moduleCount<=e+t||(this.modules[o+r][e+t]=0<=r&&r<=6&&(0==t||6==t)||0<=t&&t<=6&&(0==r||6==r)||2<=r&&r<=4&&2<=t&&t<=4);},getBestMaskPattern:function(){for(var o=0,e=0,r=0;r<8;r++){this.makeImpl(!0,r);var t=h.getLostPoint(this);(0==r||o>t)&&(o=t,e=r);}return e},createMovieClip:function(o,e,r){var t=o.createEmptyMovieClip(e,r);this.make();for(var i=0;i<this.modules.length;i++)for(var n=1*i,a=0;a<this.modules[i].length;a++){var d=1*a;this.modules[i][a]&&(t.beginFill(0,100),t.moveTo(d,n),t.lineTo(d+1,n),t.lineTo(d+1,n+1),t.lineTo(d,n+1),t.endFill());}return t},setupTimingPattern:function(){for(var o=8;o<this.moduleCount-8;o++)null==this.modules[o][6]&&(this.modules[o][6]=o%2==0);for(var e=8;e<this.moduleCount-8;e++)null==this.modules[6][e]&&(this.modules[6][e]=e%2==0);},setupPositionAdjustPattern:function(){for(var o=h.getPatternPosition(this.typeNumber),e=0;e<o.length;e++)for(var r=0;r<o.length;r++){var t=o[e],i=o[r];if(null==this.modules[t][i])for(var n=-2;n<=2;n++)for(var a=-2;a<=2;a++)this.modules[t+n][i+a]=-2==n||2==n||-2==a||2==a||0==n&&0==a;}},setupTypeNumber:function(o){for(var e=h.getBCHTypeNumber(this.typeNumber),r=0;r<18;r++){var t=!o&&1==(e>>r&1);this.modules[Math.floor(r/3)][r%3+this.moduleCount-8-3]=t;}for(r=0;r<18;r++){t=!o&&1==(e>>r&1);this.modules[r%3+this.moduleCount-8-3][Math.floor(r/3)]=t;}},setupTypeInfo:function(o,e){for(var r=this.errorCorrectLevel<<3|e,t=h.getBCHTypeInfo(r),i=0;i<15;i++){var n=!o&&1==(t>>i&1);i<6?this.modules[i][8]=n:i<8?this.modules[i+1][8]=n:this.modules[this.moduleCount-15+i][8]=n;}for(i=0;i<15;i++){n=!o&&1==(t>>i&1);i<8?this.modules[8][this.moduleCount-i-1]=n:i<9?this.modules[8][15-i-1+1]=n:this.modules[8][15-i-1]=n;}this.modules[this.moduleCount-8][8]=!o;},mapData:function(o,e){for(var r=-1,t=this.moduleCount-1,i=7,n=0,a=this.moduleCount-1;a>0;a-=2)for(6==a&&a--;;){for(var d=0;d<2;d++)if(null==this.modules[t][a-d]){var u=!1;n<o.length&&(u=1==(o[n]>>>i&1)),h.getMask(e,t,a-d)&&(u=!u),this.modules[t][a-d]=u,-1==--i&&(n++,i=7);}if((t+=r)<0||this.moduleCount<=t){t-=r,r=-r;break}}}},e.PAD0=236,e.PAD1=17,e.createData=function(o,r,t){for(var i=v.getRSBlocks(o,r),n=new p,a=0;a<t.length;a++){var d=t[a];n.put(d.mode,4),n.put(d.getLength(),h.getLengthInBits(d.mode,o)),d.write(n);}var u=0;for(a=0;a<i.length;a++)u+=i[a].dataCount;if(n.getLengthInBits()>8*u)throw new Error("code length overflow. ("+n.getLengthInBits()+">"+8*u+")");for(n.getLengthInBits()+4<=8*u&&n.put(0,4);n.getLengthInBits()%8!=0;)n.putBit(!1);for(;!(n.getLengthInBits()>=8*u||(n.put(e.PAD0,8),n.getLengthInBits()>=8*u));)n.put(e.PAD1,8);return e.createBytes(n,i)},e.createBytes=function(o,e){for(var r=0,t=0,i=0,n=new Array(e.length),a=new Array(e.length),d=0;d<e.length;d++){var u=e[d].dataCount,s=e[d].totalCount-u;t=Math.max(t,u),i=Math.max(i,s),n[d]=new Array(u);for(var g=0;g<n[d].length;g++)n[d][g]=255&o.buffer[g+r];r+=u;var l=h.getErrorCorrectPolynomial(s),c=new f(n[d],l.getLength()-1).mod(l);a[d]=new Array(l.getLength()-1);for(g=0;g<a[d].length;g++){var m=g+c.getLength()-a[d].length;a[d][g]=m>=0?c.get(m):0;}}var v=0;for(g=0;g<e.length;g++)v+=e[g].totalCount;var p=new Array(v),C=0;for(g=0;g<t;g++)for(d=0;d<e.length;d++)g<n[d].length&&(p[C++]=n[d][g]);for(g=0;g<i;g++)for(d=0;d<e.length;d++)g<a[d].length&&(p[C++]=a[d][g]);return p};for(var r={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},t={L:1,M:0,Q:3,H:2},i=0,n=1,a=2,d=3,u=4,s=5,g=6,l=7,h={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(o){for(var e=o<<10;h.getBCHDigit(e)-h.getBCHDigit(h.G15)>=0;)e^=h.G15<<h.getBCHDigit(e)-h.getBCHDigit(h.G15);return (o<<10|e)^h.G15_MASK},getBCHTypeNumber:function(o){for(var e=o<<12;h.getBCHDigit(e)-h.getBCHDigit(h.G18)>=0;)e^=h.G18<<h.getBCHDigit(e)-h.getBCHDigit(h.G18);return o<<12|e},getBCHDigit:function(o){for(var e=0;0!=o;)e++,o>>>=1;return e},getPatternPosition:function(o){return h.PATTERN_POSITION_TABLE[o-1]},getMask:function(o,e,r){switch(o){case i:return (e+r)%2==0;case n:return e%2==0;case a:return r%3==0;case d:return (e+r)%3==0;case u:return (Math.floor(e/2)+Math.floor(r/3))%2==0;case s:return e*r%2+e*r%3==0;case g:return (e*r%2+e*r%3)%2==0;case l:return (e*r%3+(e+r)%2)%2==0;default:throw new Error("bad maskPattern:"+o)}},getErrorCorrectPolynomial:function(o){for(var e=new f([1],0),r=0;r<o;r++)e=e.multiply(new f([1,c.gexp(r)],0));return e},getLengthInBits:function(o,e){if(1<=e&&e<10)switch(o){case r.MODE_NUMBER:return 10;case r.MODE_ALPHA_NUM:return 9;case r.MODE_8BIT_BYTE:case r.MODE_KANJI:return 8;default:throw new Error("mode:"+o)}else if(e<27)switch(o){case r.MODE_NUMBER:return 12;case r.MODE_ALPHA_NUM:return 11;case r.MODE_8BIT_BYTE:return 16;case r.MODE_KANJI:return 10;default:throw new Error("mode:"+o)}else {if(!(e<41))throw new Error("type:"+e);switch(o){case r.MODE_NUMBER:return 14;case r.MODE_ALPHA_NUM:return 13;case r.MODE_8BIT_BYTE:return 16;case r.MODE_KANJI:return 12;default:throw new Error("mode:"+o)}}},getLostPoint:function(o){for(var e=o.getModuleCount(),r=0,t=0;t<e;t++)for(var i=0;i<e;i++){for(var n=0,a=o.isDark(t,i),d=-1;d<=1;d++)if(!(t+d<0||e<=t+d))for(var u=-1;u<=1;u++)i+u<0||e<=i+u||0==d&&0==u||a==o.isDark(t+d,i+u)&&n++;n>5&&(r+=3+n-5);}for(t=0;t<e-1;t++)for(i=0;i<e-1;i++){var s=0;o.isDark(t,i)&&s++,o.isDark(t+1,i)&&s++,o.isDark(t,i+1)&&s++,o.isDark(t+1,i+1)&&s++,0!=s&&4!=s||(r+=3);}for(t=0;t<e;t++)for(i=0;i<e-6;i++)o.isDark(t,i)&&!o.isDark(t,i+1)&&o.isDark(t,i+2)&&o.isDark(t,i+3)&&o.isDark(t,i+4)&&!o.isDark(t,i+5)&&o.isDark(t,i+6)&&(r+=40);for(i=0;i<e;i++)for(t=0;t<e-6;t++)o.isDark(t,i)&&!o.isDark(t+1,i)&&o.isDark(t+2,i)&&o.isDark(t+3,i)&&o.isDark(t+4,i)&&!o.isDark(t+5,i)&&o.isDark(t+6,i)&&(r+=40);var g=0;for(i=0;i<e;i++)for(t=0;t<e;t++)o.isDark(t,i)&&g++;return r+=10*(Math.abs(100*g/e/e-50)/5)}},c={glog:function(o){if(o<1)throw new Error("glog("+o+")");return c.LOG_TABLE[o]},gexp:function(o){for(;o<0;)o+=255;for(;o>=256;)o-=255;return c.EXP_TABLE[o]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},m=0;m<8;m++)c.EXP_TABLE[m]=1<<m;for(m=8;m<256;m++)c.EXP_TABLE[m]=c.EXP_TABLE[m-4]^c.EXP_TABLE[m-5]^c.EXP_TABLE[m-6]^c.EXP_TABLE[m-8];for(m=0;m<255;m++)c.LOG_TABLE[c.EXP_TABLE[m]]=m;function f(o,e){if(null==o.length)throw new Error(o.length+"/"+e);for(var r=0;r<o.length&&0==o[r];)r++;this.num=new Array(o.length-r+e);for(var t=0;t<o.length-r;t++)this.num[t]=o[t+r];}function v(o,e){this.totalCount=o,this.dataCount=e;}function p(){this.buffer=new Array,this.length=0;}function C(o){return o.setFillStyle=o.setFillStyle||function(e){o.fillStyle=e;},o.setFontSize=o.setFontSize||function(e){o.font=`${e}px`;},o.setTextAlign=o.setTextAlign||function(e){o.textAlign=e;},o.setTextBaseline=o.setTextBaseline||function(e){o.textBaseline=e;},o.setGlobalAlpha=o.setGlobalAlpha||function(e){o.globalAlpha=e;},o.setStrokeStyle=o.setStrokeStyle||function(e){o.strokeStyle=e;},o.setShadow=o.setShadow||function(e,r,t,i){o.shadowOffsetX=e,o.shadowOffsetY=r,o.shadowBlur=t,o.shadowColor=i;},o.draw=o.draw||function(o,e){e&&e();},o.clearRect=o.clearRect||function(e,r,t,i){o.draw(!1);},o}function b(o,e){var r=this.data="",t=this.size=200;this.useDynamicSize=!1,this.dynamicSize=t;var i=this.typeNumber=-1;this.errorCorrectLevel=b.errorCorrectLevel.H;var n=this.margin=0;this.areaColor="#FFFFFF",this.backgroundColor="rgba(255,255,255,0)",this.backgroundImageSrc=void 0;var a=this.backgroundImageWidth=void 0,d=this.backgroundImageHeight=void 0,u=this.backgroundImageX=void 0,s=this.backgroundImageY=void 0;this.backgroundImageAlpha=1,this.backgroundImageBorderRadius=0;var g=this.backgroundPadding=0;this.foregroundColor="#000000",this.foregroundImageSrc=void 0;var l=this.foregroundImageWidth=void 0,h=this.foregroundImageHeight=void 0,c=this.foregroundImageX=void 0,m=this.foregroundImageY=void 0,f=this.foregroundImagePadding=0;this.foregroundImageBackgroundColor="#FFFFFF";var v=this.foregroundImageBorderRadius=0,p=this.foregroundImageShadowOffsetX=0,k=this.foregroundImageShadowOffsetY=0,y=this.foregroundImageShadowBlur=0;this.foregroundImageShadowColor="#808080";var w=this.foregroundPadding=0,I=this.positionProbeBackgroundColor=void 0,B=this.positionProbeForegroundColor=void 0,S=this.separatorColor=void 0,P=this.positionAdjustBackgroundColor=void 0,L=this.positionAdjustForegroundColor=void 0,D=this.timingBackgroundColor=void 0,A=this.timingForegroundColor=void 0,E=this.typeNumberBackgroundColor=void 0,T=this.typeNumberForegroundColor=void 0,N=this.darkBlockColor=void 0;this.base=void 0,this.modules=[],this.moduleCount=0,this.drawModules=[];var M=this.canvasContext=void 0;this.loadImage,this.drawReserve=!1,this.isMaked=!1,Object.defineProperties(this,{data:{get(){if(""===r||void 0===r)throw console.error("[uQRCode]: data must be set!"),new b.Error("data must be set!");return r},set(o){r=String(o);}},size:{get:()=>t,set(o){t=Number(o);}},typeNumber:{get:()=>i,set(o){i=Number(o);}},margin:{get:()=>n,set(o){n=Number(o);}},backgroundImageWidth:{get(){return void 0===a?this.dynamicSize:this.useDynamicSize?this.dynamicSize/this.size*a:a},set(o){a=Number(o);}},backgroundImageHeight:{get(){return void 0===d?this.dynamicSize:this.useDynamicSize?this.dynamicSize/this.size*d:d},set(o){d=Number(o);}},backgroundImageX:{get(){return void 0===u?0:this.useDynamicSize?this.dynamicSize/this.size*u:u},set(o){u=Number(o);}},backgroundImageY:{get(){return void 0===s?0:this.useDynamicSize?this.dynamicSize/this.size*s:s},set(o){s=Number(o);}},backgroundPadding:{get:()=>g,set(o){g=o>1?1:o<0?0:o;}},foregroundImageWidth:{get(){return void 0===l?(this.dynamicSize-2*this.margin)/4:this.useDynamicSize?this.dynamicSize/this.size*l:l},set(o){l=Number(o);}},foregroundImageHeight:{get(){return void 0===h?(this.dynamicSize-2*this.margin)/4:this.useDynamicSize?this.dynamicSize/this.size*h:h},set(o){h=Number(o);}},foregroundImageX:{get(){return void 0===c?this.dynamicSize/2-this.foregroundImageWidth/2:this.useDynamicSize?this.dynamicSize/this.size*c:c},set(o){c=Number(o);}},foregroundImageY:{get(){return void 0===m?this.dynamicSize/2-this.foregroundImageHeight/2:this.useDynamicSize?this.dynamicSize/this.size*m:m},set(o){m=Number(o);}},foregroundImagePadding:{get(){return this.useDynamicSize?this.dynamicSize/this.size*f:f},set(o){f=Number(o);}},foregroundImageBorderRadius:{get(){return this.useDynamicSize?this.dynamicSize/this.size*v:v},set(o){v=Number(o);}},foregroundImageShadowOffsetX:{get(){return this.useDynamicSize?this.dynamicSize/this.size*p:p},set(o){p=Number(o);}},foregroundImageShadowOffsetY:{get(){return this.useDynamicSize?this.dynamicSize/this.size*k:k},set(o){k=Number(o);}},foregroundImageShadowBlur:{get(){return this.useDynamicSize?this.dynamicSize/this.size*y:y},set(o){y=Number(o);}},foregroundPadding:{get:()=>w,set(o){w=o>1?1:o<0?0:o;}},positionProbeBackgroundColor:{get(){return I||this.backgroundColor},set(o){I=o;}},positionProbeForegroundColor:{get(){return B||this.foregroundColor},set(o){B=o;}},separatorColor:{get(){return S||this.backgroundColor},set(o){S=o;}},positionAdjustBackgroundColor:{get(){return P||this.backgroundColor},set(o){P=o;}},positionAdjustForegroundColor:{get(){return L||this.foregroundColor},set(o){L=o;}},timingBackgroundColor:{get(){return D||this.backgroundColor},set(o){D=o;}},timingForegroundColor:{get(){return A||this.foregroundColor},set(o){A=o;}},typeNumberBackgroundColor:{get(){return E||this.backgroundColor},set(o){E=o;}},typeNumberForegroundColor:{get(){return T||this.foregroundColor},set(o){T=o;}},darkBlockColor:{get(){return N||this.foregroundColor},set(o){N=o;}},canvasContext:{get(){if(void 0===M)throw console.error("[uQRCode]: use drawCanvas, you need to set the canvasContext!"),new b.Error("use drawCanvas, you need to set the canvasContext!");return M},set(o){M=C(o);}}}),b.plugins.forEach((o=>o(b,this,!1))),o&&this.setOptions(o),e&&(this.canvasContext=C(e));}f.prototype={get:function(o){return this.num[o]},getLength:function(){return this.num.length},multiply:function(o){for(var e=new Array(this.getLength()+o.getLength()-1),r=0;r<this.getLength();r++)for(var t=0;t<o.getLength();t++)e[r+t]^=c.gexp(c.glog(this.get(r))+c.glog(o.get(t)));return new f(e,0)},mod:function(o){if(this.getLength()-o.getLength()<0)return this;for(var e=c.glog(this.get(0))-c.glog(o.get(0)),r=new Array(this.getLength()),t=0;t<this.getLength();t++)r[t]=this.get(t);for(t=0;t<o.getLength();t++)r[t]^=c.gexp(c.glog(o.get(t))+e);return new f(r,0).mod(o)}},v.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],v.getRSBlocks=function(o,e){var r=v.getRsBlockTable(o,e);if(null==r)throw new Error("bad rs block @ typeNumber:"+o+"/errorCorrectLevel:"+e);for(var t=r.length/3,i=new Array,n=0;n<t;n++)for(var a=r[3*n+0],d=r[3*n+1],u=r[3*n+2],s=0;s<a;s++)i.push(new v(d,u));return i},v.getRsBlockTable=function(o,e){switch(e){case t.L:return v.RS_BLOCK_TABLE[4*(o-1)+0];case t.M:return v.RS_BLOCK_TABLE[4*(o-1)+1];case t.Q:return v.RS_BLOCK_TABLE[4*(o-1)+2];case t.H:return v.RS_BLOCK_TABLE[4*(o-1)+3];default:return}},p.prototype={get:function(o){var e=Math.floor(o/8);return 1==(this.buffer[e]>>>7-o%8&1)},put:function(o,e){for(var r=0;r<e;r++)this.putBit(1==(o>>>e-r-1&1));},getLengthInBits:function(){return this.length},putBit:function(o){var e=Math.floor(this.length/8);this.buffer.length<=e&&this.buffer.push(0),o&&(this.buffer[e]|=128>>>this.length%8),this.length++;}},e.errorCorrectLevel=t,b.errorCorrectLevel=e.errorCorrectLevel,b.Error=function(o){this.errMsg="[uQRCode]: "+o;},b.plugins=[],b.use=function(o){"function"==typeof o&&b.plugins.push(o);},b.prototype.loadImage=function(o){return Promise.resolve(o)},b.prototype.setOptions=function(o){var e,r,t,i,n,a,d,u,s,g,l,h,c,m,f,v,p,C,b,k,y,w,I,B,S,P,L,D,A,E,T,N,M,z,R,_,O,F,x,H,X,Y,j,W,G,K,Q,U,$,J,q,V,Z,oo,eo,ro;o&&(Object.keys(o).forEach((e=>{this[e]=o[e];})),function(o={},e={},r=!1){let t;t=r?o:{...o};for(let o in e){var i=e[o];null!=i&&(i.constructor==Object?t[o]=this.deepReplace(t[o],i):i.constructor!=String||i?t[o]=i:t[o]=t[o]);}}(this,{data:o.data||o.text,size:o.size,useDynamicSize:o.useDynamicSize,typeNumber:o.typeNumber,errorCorrectLevel:o.errorCorrectLevel,margin:o.margin,areaColor:o.areaColor,backgroundColor:o.backgroundColor||(null===(e=o.background)||void 0===e?void 0:e.color),backgroundImageSrc:o.backgroundImageSrc||(null===(r=o.background)||void 0===r||null===(t=r.image)||void 0===t?void 0:t.src),backgroundImageWidth:o.backgroundImageWidth||(null===(i=o.background)||void 0===i||null===(n=i.image)||void 0===n?void 0:n.width),backgroundImageHeight:o.backgroundImageHeight||(null===(a=o.background)||void 0===a||null===(d=a.image)||void 0===d?void 0:d.height),backgroundImageX:o.backgroundImageX||(null===(u=o.background)||void 0===u||null===(s=u.image)||void 0===s?void 0:s.x),backgroundImageY:o.backgroundImageY||(null===(g=o.background)||void 0===g||null===(l=g.image)||void 0===l?void 0:l.y),backgroundImageAlpha:o.backgroundImageAlpha||(null===(h=o.background)||void 0===h||null===(c=h.image)||void 0===c?void 0:c.alpha),backgroundImageBorderRadius:o.backgroundImageBorderRadius||(null===(m=o.background)||void 0===m||null===(f=m.image)||void 0===f?void 0:f.borderRadius),backgroundPadding:o.backgroundPadding,foregroundColor:o.foregroundColor||(null===(v=o.foreground)||void 0===v?void 0:v.color),foregroundImageSrc:o.foregroundImageSrc||(null===(p=o.foreground)||void 0===p||null===(C=p.image)||void 0===C?void 0:C.src),foregroundImageWidth:o.foregroundImageWidth||(null===(b=o.foreground)||void 0===b||null===(k=b.image)||void 0===k?void 0:k.width),foregroundImageHeight:o.foregroundImageHeight||(null===(y=o.foreground)||void 0===y||null===(w=y.image)||void 0===w?void 0:w.height),foregroundImageX:o.foregroundImageX||(null===(I=o.foreground)||void 0===I||null===(B=I.image)||void 0===B?void 0:B.x),foregroundImageY:o.foregroundImageY||(null===(S=o.foreground)||void 0===S||null===(P=S.image)||void 0===P?void 0:P.y),foregroundImagePadding:o.foregroundImagePadding||(null===(L=o.foreground)||void 0===L||null===(D=L.image)||void 0===D?void 0:D.padding),foregroundImageBackgroundColor:o.foregroundImageBackgroundColor||(null===(A=o.foreground)||void 0===A||null===(E=A.image)||void 0===E?void 0:E.backgroundColor),foregroundImageBorderRadius:o.foregroundImageBorderRadius||(null===(T=o.foreground)||void 0===T||null===(N=T.image)||void 0===N?void 0:N.borderRadius),foregroundImageShadowOffsetX:o.foregroundImageShadowOffsetX||(null===(M=o.foreground)||void 0===M||null===(z=M.image)||void 0===z?void 0:z.shadowOffsetX),foregroundImageShadowOffsetY:o.foregroundImageShadowOffsetY||(null===(R=o.foreground)||void 0===R||null===(_=R.image)||void 0===_?void 0:_.shadowOffsetY),foregroundImageShadowBlur:o.foregroundImageShadowBlur||(null===(O=o.foreground)||void 0===O||null===(F=O.image)||void 0===F?void 0:F.shadowBlur),foregroundImageShadowColor:o.foregroundImageShadowColor||(null===(x=o.foreground)||void 0===x||null===(H=x.image)||void 0===H?void 0:H.shadowColor),foregroundPadding:o.foregroundPadding,positionProbeBackgroundColor:o.positionProbeBackgroundColor||(null===(X=o.positionProbe)||void 0===X?void 0:X.backgroundColor)||(null===(Y=o.positionDetection)||void 0===Y?void 0:Y.backgroundColor),positionProbeForegroundColor:o.positionProbeForegroundColor||(null===(j=o.positionProbe)||void 0===j?void 0:j.foregroundColor)||(null===(W=o.positionDetection)||void 0===W?void 0:W.foregroundColor),separatorColor:o.separatorColor||(null===(G=o.separator)||void 0===G?void 0:G.color),positionAdjustBackgroundColor:o.positionAdjustBackgroundColor||(null===(K=o.positionAdjust)||void 0===K?void 0:K.backgroundColor)||(null===(Q=o.alignment)||void 0===Q?void 0:Q.backgroundColor),positionAdjustForegroundColor:o.positionAdjustForegroundColor||(null===(U=o.positionAdjust)||void 0===U?void 0:U.foregroundColor)||(null===($=o.alignment)||void 0===$?void 0:$.foregroundColor),timingBackgroundColor:o.timingBackgroundColor||(null===(J=o.timing)||void 0===J?void 0:J.backgroundColor),timingForegroundColor:o.timingForegroundColor||(null===(q=o.timing)||void 0===q?void 0:q.foregroundColor),typeNumberBackgroundColor:o.typeNumberBackgroundColor||(null===(V=o.typeNumber)||void 0===V?void 0:V.backgroundColor)||(null===(Z=o.versionInformation)||void 0===Z?void 0:Z.backgroundColor),typeNumberForegroundColor:o.typeNumberForegroundColor||(null===(oo=o.typeNumber)||void 0===oo?void 0:oo.foregroundColor)||(null===(eo=o.versionInformation)||void 0===eo?void 0:eo.foregroundColor),darkBlockColor:o.darkBlockColor||(null===(ro=o.darkBlock)||void 0===ro?void 0:ro.color)},!0));},b.prototype.make=function(){let{foregroundColor:o,backgroundColor:r,typeNumber:t,errorCorrectLevel:i,data:n,size:a,margin:d,useDynamicSize:u}=this;if(o===r)throw console.error("[uQRCode]: foregroundColor and backgroundColor cannot be the same!"),new b.Error("foregroundColor and backgroundColor cannot be the same!");var s=new e(t,i);s.addData(function(o){o=o.toString();for(var e,r="",t=0;t<o.length;t++)(e=o.charCodeAt(t))>=1&&e<=127?r+=o.charAt(t):e>2047?(r+=String.fromCharCode(224|e>>12&15),r+=String.fromCharCode(128|e>>6&63),r+=String.fromCharCode(128|e>>0&63)):(r+=String.fromCharCode(192|e>>6&31),r+=String.fromCharCode(128|e>>0&63));return r}(n)),s.make(),this.base=s,this.typeNumber=s.typeNumber,this.modules=s.modules,this.moduleCount=s.moduleCount,this.dynamicSize=u?Math.ceil((a-2*d)/s.moduleCount)*s.moduleCount+2*d:a,function(o){let{dynamicSize:e,margin:r,backgroundColor:t,backgroundPadding:i,foregroundColor:n,foregroundPadding:a,modules:d,moduleCount:u}=o,s=(e-2*r)/u,g=s,l=0;i>0&&(l=g*i/2,g-=2*l);let h=s,c=0;a>0&&(c=h*a/2,h-=2*c);for(var m=0;m<u;m++)for(var f=0;f<u;f++){var v=f*s+r,p=m*s+r;if(d[m][f]){var C=c,b=v+c,k=p+c,y=h,w=h;d[m][f]={type:["foreground"],color:n,isBlack:!0,isDrawn:!1,destX:v,destY:p,destWidth:s,destHeight:s,x:b,y:k,width:y,height:w,paddingTop:C,paddingRight:C,paddingBottom:C,paddingLeft:C};}else C=l,b=v+l,k=p+l,y=g,w=g,d[m][f]={type:["background"],color:t,isBlack:!1,isDrawn:!1,destX:v,destY:p,destWidth:s,destHeight:s,x:b,y:k,width:y,height:w,paddingTop:C,paddingRight:C,paddingBottom:C,paddingLeft:C};}}(this),function(o){let{modules:e,moduleCount:r,positionProbeBackgroundColor:t,positionProbeForegroundColor:i}=o,n=r-7;[[0,0,1],[1,0,1],[2,0,1],[3,0,1],[4,0,1],[5,0,1],[6,0,1],[0,1,1],[1,1,0],[2,1,0],[3,1,0],[4,1,0],[5,1,0],[6,1,1],[0,2,1],[1,2,0],[2,2,1],[3,2,1],[4,2,1],[5,2,0],[6,2,1],[0,3,1],[1,3,0],[2,3,1],[3,3,1],[4,3,1],[5,3,0],[6,3,1],[0,4,1],[1,4,0],[2,4,1],[3,4,1],[4,4,1],[5,4,0],[6,4,1],[0,5,1],[1,5,0],[2,5,0],[3,5,0],[4,5,0],[5,5,0],[6,5,1],[0,6,1],[1,6,1],[2,6,1],[3,6,1],[4,6,1],[5,6,1],[6,6,1]].forEach((o=>{var r=e[o[0]][o[1]],a=e[o[0]+n][o[1]],d=e[o[0]][o[1]+n];d.type.push("positionProbe"),a.type.push("positionProbe"),r.type.push("positionProbe"),r.color=1==o[2]?i:t,a.color=1==o[2]?i:t,d.color=1==o[2]?i:t;}));}(this),function(o){let{modules:e,moduleCount:r,separatorColor:t}=o;[[7,0],[7,1],[7,2],[7,3],[7,4],[7,5],[7,6],[7,7],[0,7],[1,7],[2,7],[3,7],[4,7],[5,7],[6,7]].forEach((o=>{var i=e[o[0]][o[1]],n=e[r-o[0]-1][o[1]],a=e[o[0]][r-o[1]-1];a.type.push("separator"),n.type.push("separator"),i.type.push("separator"),i.color=t,n.color=t,a.color=t;}));}(this),function(o){let{typeNumber:e,modules:r,moduleCount:t,foregroundColor:i,backgroundColor:n,positionAdjustForegroundColor:a,positionAdjustBackgroundColor:d,timingForegroundColor:u,timingBackgroundColor:s}=o;const g=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]][e-1];if(g){const o=[[-2,-2,1],[-1,-2,1],[0,-2,1],[1,-2,1],[2,-2,1],[-2,-1,1],[-1,-1,0],[0,-1,0],[1,-1,0],[2,-1,1],[-2,0,1],[-1,0,0],[0,0,1],[1,0,0],[2,0,1],[-2,1,1],[-1,1,0],[0,1,0],[1,1,0],[2,1,1],[-2,2,1],[-1,2,1],[0,2,1],[1,2,1],[2,2,1]],e=g.length;for(let l=0;l<e;l++)for(let h=0;h<e;h++){let{x:e,y:c}={x:g[l],y:g[h]};e<9&&c<9||e>t-9-1&&c<9||c>t-9-1&&e<9||o.forEach((o=>{var t=r[e+o[0]][c+o[1]];t.type.push("positionAdjust"),t.type.includes("timing")?1==o[2]?t.color=a==i?u:a:t.color=a==i&&d==n?s:d:t.color=1==o[2]?a:d;}));}}}(this),function(o){let{modules:e,moduleCount:r,timingForegroundColor:t,timingBackgroundColor:i}=o,n=r-16;for(let o=0;o<n;o++){var a=e[6][8+o],d=e[8+o][6];a.type.push("timing"),d.type.push("timing"),a.color=1&o^1?t:i,d.color=1&o^1?t:i;}}(this),function(o){let{modules:e,moduleCount:r,darkBlockColor:t}=o;var i=e[r-7-1][8];i.type.push("darkBlock"),i.color=t;}(this),function(o){let{typeNumber:e,modules:r,moduleCount:t,typeNumberBackgroundColor:i,typeNumberForegroundColor:n}=o;if(e<7)return r;const a=[0,0,0,0,0,0,0,"000111110010010100","001000010110111100","001001101010011001","001010010011010011","001011101111110110","001100011101100010","001101100001000111","001110011000001101","001111100100101000","010000101101111000","010001010001011101","010010101000010111","010011010100110010","010100100110100110","010101011010000011","010110100011001001","010111011111101100","011000111011000100","011001000111100001","011010111110101011","011011000010001110","011100110000011010","011101001100111111","011110110101110101","011111001001010000","100000100111010101","100001011011110000","100010100010111010","100011011110011111","100100101100001011","100101010000101110","100110101001100100","100111010101000001","101000110001101001"];let d=a[e]+a[e],u=[t-11,t-10,t-9];[[5,u[2]],[5,u[1]],[5,u[0]],[4,u[2]],[4,u[1]],[4,u[0]],[3,u[2]],[3,u[1]],[3,u[0]],[2,u[2]],[2,u[1]],[2,u[0]],[1,u[2]],[1,u[1]],[1,u[0]],[0,u[2]],[0,u[1]],[0,u[0]],[u[2],5],[u[1],5],[u[0],5],[u[2],4],[u[1],4],[u[0],4],[u[2],3],[u[1],3],[u[0],3],[u[2],2],[u[1],2],[u[0],2],[u[2],1],[u[1],1],[u[0],1],[u[2],0],[u[1],0],[u[0],0]].forEach(((o,e)=>{var t=r[o[0]][o[1]];t.type.push("typeNumber"),t.color="1"==d[e]?n:i;}));}(this),this.isMaked=!0,this.drawModules=[];},b.prototype.getDrawModules=function(){if(this.drawModules&&this.drawModules.length>0)return this.drawModules;let o=this.drawModules=[],{modules:e,moduleCount:r,dynamicSize:t,areaColor:i,backgroundImageSrc:n,backgroundImageX:a,backgroundImageY:d,backgroundImageWidth:u,backgroundImageHeight:s,backgroundImageAlpha:g,backgroundImageBorderRadius:l,foregroundImageSrc:h,foregroundImageX:c,foregroundImageY:m,foregroundImageWidth:f,foregroundImageHeight:v,foregroundImagePadding:p,foregroundImageBackgroundColor:C,foregroundImageBorderRadius:b,foregroundImageShadowOffsetX:k,foregroundImageShadowOffsetY:y,foregroundImageShadowBlur:w,foregroundImageShadowColor:I}=this;i&&o.push({name:"area",type:"area",color:i,x:0,y:0,width:t,height:t}),n&&o.push({name:"backgroundImage",type:"image",imageSrc:n,mappingName:"backgroundImageSrc",x:a,y:d,width:u,height:s,alpha:g,borderRadius:l});for(var B=0;B<r;B++)for(var S=0;S<r;S++){var P=e[B][S];P.isDrawn||(P.type.includes("foreground")?o.push({name:"foreground",type:"tile",color:P.color,destX:P.destX,destY:P.destY,destWidth:P.destWidth,destHeight:P.destHeight,x:P.x,y:P.y,width:P.width,height:P.height,paddingTop:P.paddingTop,paddingRight:P.paddingRight,paddingBottom:P.paddingBottom,paddingLeft:P.paddingLeft,rowIndex:B,colIndex:S}):o.push({name:"background",type:"tile",color:P.color,destX:P.destX,destY:P.destY,destWidth:P.destWidth,destHeight:P.destHeight,x:P.x,y:P.y,width:P.width,height:P.height,paddingTop:P.paddingTop,paddingRight:P.paddingRight,paddingBottom:P.paddingBottom,paddingLeft:P.paddingLeft,rowIndex:B,colIndex:S}),P.isDrawn=!0);}return h&&o.push({name:"foregroundImage",type:"image",imageSrc:h,mappingName:"foregroundImageSrc",x:c,y:m,width:f,height:v,padding:p,backgroundColor:C,borderRadius:b,shadowOffsetX:k,shadowOffsetY:y,shadowBlur:w,shadowColor:I}),o},b.prototype.isBlack=function(o,e){var r=this.moduleCount;return !(0>o||0>e||o>=r||e>=r)&&this.modules[o][e].isBlack},b.prototype.drawCanvas=function(){let{isMaked:o,canvasContext:e,useDynamicSize:r,dynamicSize:t,foregroundColor:i,foregroundPadding:n,backgroundColor:a,backgroundPadding:d,drawReserve:u,margin:s}=this;if(!o)return console.error("[uQRCode]: please execute the make method first!"),Promise.reject(new b.Error("please execute the make method first!"));let g=this.getDrawModules(),l=async(o,r)=>{try{e.clearRect(0,0,t,t),e.draw(!1);for(var i=0;i<g.length;i++){var n=g[i];switch(e.save(),n.type){case"area":e.setFillStyle(n.color),e.fillRect(n.x,n.y,n.width,n.height);break;case"tile":var a=n.x,d=n.y,s=n.width,l=n.height;e.setFillStyle(n.color),e.fillRect(a,d,s,l);break;case"image":if("backgroundImage"===n.name){a=Math.round(n.x),d=Math.round(n.y),s=Math.round(n.width),l=Math.round(n.height);s<2*(c=Math.round(n.borderRadius))&&(c=s/2),l<2*c&&(c=l/2),e.setGlobalAlpha(n.alpha),c>0&&(e.beginPath(),e.moveTo(a+c,d),e.arcTo(a+s,d,a+s,d+l,c),e.arcTo(a+s,d+l,a,d+l,c),e.arcTo(a,d+l,a,d,c),e.arcTo(a,d,a+s,d,c),e.closePath(),e.setStrokeStyle("rgba(0,0,0,0)"),e.stroke(),e.clip());try{var h=await this.loadImage(n.imageSrc);e.drawImage(h,a,d,s,l);}catch(o){throw console.error(`[uQRCode]: ${n.mappingName} invalid!`),new b.Error(`${n.mappingName} invalid!`)}}else if("foregroundImage"===n.name){a=Math.round(n.x),d=Math.round(n.y),s=Math.round(n.width),l=Math.round(n.height);var c,m=Math.round(n.padding);s<2*(c=Math.round(n.borderRadius))&&(c=s/2),l<2*c&&(c=l/2);var f=a-m,v=d-m,p=s+2*m,C=l+2*m,k=Math.round(p/s*c);p<2*k&&(k=p/2),C<2*k&&(k=C/2),e.save(),e.setShadow(n.shadowOffsetX,n.shadowOffsetY,n.shadowBlur,n.shadowColor),k>0?(e.beginPath(),e.moveTo(f+k,v),e.arcTo(f+p,v,f+p,v+C,k),e.arcTo(f+p,v+C,f,v+C,k),e.arcTo(f,v+C,f,v,k),e.arcTo(f,v,f+p,v,k),e.closePath(),e.setFillStyle(n.backgroundColor),e.fill()):(e.setFillStyle(n.backgroundColor),e.fillRect(f,v,p,C)),e.restore(),e.save(),k>0?(e.beginPath(),e.moveTo(f+k,v),e.arcTo(f+p,v,f+p,v+C,k),e.arcTo(f+p,v+C,f,v+C,k),e.arcTo(f,v+C,f,v,k),e.arcTo(f,v,f+p,v,k),e.closePath(),e.setFillStyle(m>0?n.backgroundColor:"rgba(0,0,0,0)"),e.fill()):(e.setFillStyle(m>0?n.backgroundColor:"rgba(0,0,0,0)"),e.fillRect(f,v,p,C)),e.restore(),c>0&&(e.beginPath(),e.moveTo(a+c,d),e.arcTo(a+s,d,a+s,d+l,c),e.arcTo(a+s,d+l,a,d+l,c),e.arcTo(a,d+l,a,d,c),e.arcTo(a,d,a+s,d,c),e.closePath(),e.setStrokeStyle("rgba(0,0,0,0)"),e.stroke(),e.clip());try{h=await this.loadImage(n.imageSrc);e.drawImage(h,a,d,s,l);}catch(o){throw console.error(`[uQRCode]: ${n.mappingName} invalid!`),new b.Error(`${n.mappingName} invalid!`)}}}u&&e.draw(!0),e.restore();}e.draw(!0),setTimeout(o,150);}catch(o){if(!(o instanceof b.Error))throw o;r(o);}};return new Promise(((o,e)=>{l(o,e);}))},b.prototype.draw=function(){return this.drawCanvas()},b.prototype.register=function(o){o&&o(b,this,!0);};export{b as default};
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/queue.js b/uni_modules/uv-qrcode/components/uv-qrcode/queue.js
new file mode 100644
index 0000000..be6b1d2
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/queue.js
@@ -0,0 +1,41 @@
+function Queue() {
+ let waitingQueue = this.waitingQueue = [];
+ let isRunning = this.isRunning = false; // 记录是否有未完成的任务
+
+ function execute(task, resolve, reject) {
+ task()
+ .then((data) => {
+ resolve(data);
+ })
+ .catch((e) => {
+ reject(e);
+ })
+ .finally(() => {
+ // 等待任务队列中如果有任务,则触发它;否则设置isRunning = false,表示无任务状态
+ if (waitingQueue.length) {
+ const next = waitingQueue.shift();
+ execute(next.task, next.resolve, next.reject);
+ } else {
+ isRunning = false;
+ }
+ });
+ }
+ this.exec = function(task) {
+ return new Promise((resolve, reject) => {
+ if (isRunning) {
+ waitingQueue.push({
+ task,
+ resolve,
+ reject
+ });
+ } else {
+ isRunning = true;
+ execute(task, resolve, reject);
+ }
+ });
+ }
+}
+
+/* 队列实例,某些平台一起使用多个组件时需要通过队列逐一绘制,否则部分绘制方法异常,nvue端的iOS gcanvas尤其明显,在不通过队列绘制时会出现图片丢失的情况 */
+export const queueDraw = new Queue();
+export const queueLoadImage = new Queue();
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue b/uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue
new file mode 100644
index 0000000..dd8cfc0
--- /dev/null
+++ b/uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue
@@ -0,0 +1,1038 @@
+<template>
+ <view class="uqrcode"
+ :class="{ 'uqrcode-hide': hide }"
+ :style="{ width: `${templateOptions.width}px`, height: `${templateOptions.height}px` }">
+ <view class="uqrcode-canvas-wrapper">
+ <!-- 画布 -->
+ <!-- #ifndef APP-NVUE -->
+ <canvas class="uqrcode-canvas"
+ :id="canvasId"
+ :canvas-id="canvasId"
+ :type="canvasType"
+ :style="{
+ width: `${templateOptions.canvasWidth}px`,
+ height: `${templateOptions.canvasHeight}px`,
+ transform: templateOptions.canvasTransform
+ }"
+ v-if="templateOptions.canvasDisplay"
+ @click="onClick"></canvas>
+ <!-- #endif -->
+
+ <!-- nvue用gcanvas -->
+ <!-- #ifdef APP-NVUE -->
+ <gcanvas class="uqrcode-canvas"
+ ref="gcanvas"
+ :style="{
+ width: `${templateOptions.canvasWidth}px`,
+ height: `${templateOptions.canvasHeight}px`
+ }"
+ v-if="templateOptions.canvasDisplay"
+ @click="onClick"></gcanvas>
+ <!-- #endif -->
+ </view>
+
+ <!-- 加载效果 -->
+ <view class="uqrcode-makeing"
+ v-if="loading === undefined || !loading ? makeing : loading">
+ <slot name="loading">
+ <image class="uqrcode-makeing-image"
+ :style="{ width: `${templateOptions.size / 4}px`, height: `${templateOptions.size / 4}px` }"
+ src="data:image/gif;base64,R0lGODlhAAEAAfIEAOHh4SSsWuDg4N3d3f///wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDYuMC1jMDAyIDc5LjE2NDQ4OCwgMjAyMC8wNy8xMC0yMjowNjo1MyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIyLjAgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODhGMzM4RDEwMTExRUM4MDhCRkVBQkE2QUZDQzkwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjAyODhGMzM5RDEwMTExRUM4MDhCRkVBQkE2QUZDQzkwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MDI4OEYzMzZEMTAxMTFFQzgwOEJGRUFCQTZBRkNDOTAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4OEYzMzdEMTAxMTFFQzgwOEJGRUFCQTZBRkNDOTAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFFAAEACwAAAAAAAEAAQAD/0i63P4wykmrvTjrzbv/YCiOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSCwaj8ikcslsOp/QqHRKrVqv2Kx2y+16v+CweEwum8/otHrNbrvf8Lh8Tq/b7/i8fs/v+/+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanigCqq6ytrieusbISAbW2t7i5uru8vb66bLLCrLDDw7S/ycrLzLXBxsLF0LHIzdbXzc/Trybb1BHY4eK92t6r0uaq1ePs4+Xp6PDg7fTh7+bx+PP1/Mz33vkA7utH0Ne/bQERDizIMNfBaQkhLmxIMcBDaBExTqzI8P+isYwfN3Ik6PFYt3TnRI7kVzLaSZQA1q0s2HLWS5QyZ/ar+a0ETHUqdbLjyc3nz5xC6RFtBdIkhKQ01/yMeVPeU6g7pR6tqu8q1npLiXEV6PVru7ApjcJEquyEPa1rxyosm83EWzVTm7qk688uNrRA1eIMatDvNcBUBVt9cJdEYzR55Urku8ztX7iDFXdlfLnE4zORNZPlfNiwNcR6bVJua7ou3q2i55I+3brv67ixJ8927bhzmtAkgDv4HIJ4GeEikDMw/oH5GOUgoCtw3oF6GOkesFvfsP0L9g7afY/o7uU7h/ClPYsHDTt4++Hri8c//j55/eXzm+d/fj96/+n/+1UX4HX/ZVcgeRggyIV5G6BHmycMauAgb5xEmMGEtnViIQYYVvbJhhd0yBqEBYJ34ICUgGiBiMmAomIFLP7iYonnnZiehjQ2aOODOE7l449MERbVai1iBuSRO67EVpG3IenkYvDptKSMRj5pZUhENjRlYU1e6aVqu420JTlVfmlmYGFyNCYviJ2ZWZoVrblLm25uFuVMcgJTZp1X5gmWkGzuyeeTfioF6JyCDopkoWcdqmeXilrJ6FCOOpRopD9O6k6luNCJ6V5wUqSpRZd+mqSYnN7iqalFhaplqrasyqpYWXYEqzOlzmpnA0mNKquuiblqa61kQgrsqWreSqqx/8e+eaeSyqIi7bTUVmvttdhmq+223Hbr7bejCCDuuOSWa+656Kar7rrnSjDAu/DGK++89NZr77340vsru/z2224E+QYs8MAEw7uvvwj3627BDDfM8MEJR5zuwg5XbHG9EEusMbkUX+zxxRlvvHHHH5f8cK4ip+wvySa3HHDIKifMsss0Y4xyzDijO3PNPBt8c85Aj7tzzzzDHPS6QxNNs9FHTwyw0lAPwHTT/0IQNdRTU11u0ld/nLXWQj/dddE/g50y12Nb/LXZaKft8Npgt+32ycyafbTccxMMt9Z45y3w3lT37Xe+qEnGruDxzihxalU/ULHiETNuLuI+k7i44f9Ii013j5Fjri7l70Ius+dOW/32hxpLvrXmBYuOsOocs6436pfndrjsA7u+Muk64/437Z3bnrnpDeuuMO+NO/A48KML/7nvLzP/OvKTQ0+49Ls7X7rjp1sevHu1c1889sdr3zvxm1eYOvWro986+fzCHrb7s3vfPPjfK9895/ePMLL1+DKe3c6Hv/fZb4DPM5++4IfA9hWwfvxrIAH9tz/1STCBD8wdAy8oNfYlboMXlF/oQChBEXbwgByMnQLnJcAUmrCFHDTh4FhYNrZ5cIY2q5sLb4hDGuowhjzs4Qd/GMIgCnGERCyhEY8IOAxS8IgVZE8Kk2cfKI4viQ2UIRPAaxi3JQqxiXcDoBXtVbgVOlB/YzTgb9ZnRhWKL40axCIVQ/A/+sExgFwU1wvFeMchrjF8T8xfA/oYxz8Kko5sfCMh71XGDJZPkYvMoSH7V8VDLiCS15Nj9do4P0hiUl6NDCQlGfBJRoLrlKhMpSpXycpWuvKVsIylLGdJy1ra8pa4zKUud8nLXvryl8AMpjCHScxiGvOYyEymMpfJzGY685nQjKY0p0nNalrzmtjMpja3yc1uevOb4AynOMdJhwQAACH5BAUUAAQALDIAMgCcAJwAAAP/KLrcTjDKSWt0OFsIuv9gKI5kaZ6Ztq1s6iorKs90/apsTt1pbP/AIA+mK16Gj41wyWwan8ikpUmtRp/GaMNn7Xq3WJ2Wwf2arWHxmDg9u6np3JpdeduX8da8fO8j83xXSn6EQ4CDa4GFi2CHO3uIjJJkjo+JkZOTlZZjipmFmxNzAp6ffqESo6Wmd6hHl22sjK4ckLGyoLSqmLh9tAS7t72+urZ1QL+LycacNcuEz528M9HErsHHP9WtxbDZNtt24YbTMuNu5zerJulm7S7rJe9e8zjfzt2n+VrxJPVo+wQJo/GvSsFG9wgGFLeQ3EBqDdFFVFcOxUEnE1/0G3GR/0lHOs0UXss10ltIiCX1peRX8cRHIS83iniJLVRNUcgyfonZkp1Oej/tnTT3K87NSkdfgSuaJukhp8ByMsUCNQ/UIFPDVDXKDKe2rFC6IhWrFB/YIlubkq319awak5uuSnWrB+5Yu2VF0pUpBZXctnt7jhqMl63KhMMIU3z4hm9ixY4xMn6sGENkj4IpVyaVuctlzdImn/kMWiDixp1L/z08VPVm0lhTuw59WqLo2YNhz22NO7dsOL9789ANmLfwwlGhBT8Obzke58wtQ499O/qf6bu9WvddHWj37RqxF9cOHrky8ZvTs/wOkH2IwPDjy59Pv779+/jz69/Pv7////8ABijggAQWaOCBCCao4FQDNOjggxBGKOGEFFZooYQrBKDhhhx26OGHIIYo4ogfXmjiiSim6GCGJLbo4oswaqjijDTSyGKMOOYYY4089ljhjToGKWSJPhZpJJBDJimkkUz2iKSSUO7Y5JQqPhnllSRSqeWJVmLpJZFbhjlhl1+WKaOYaEJIpplfpulmg2uyieWbbsYpZ5R0pmnnnUrmieaefA7pp5iABhrkoGEWamiOiG6p6KJSNjrlo5C+KCmVlFba4qWTbqCpl5w2memnIvLIkwVB6mdqUBh6qqOqNZ5aQar5rbpSiqMGAKuNrEaY664zykoBrfjZ6lesruYIbJX/vaqZLI7L4trsg7/WiuytKFZb7LXH8orqq9Z6222wz8YYbbbTrlgujOdymS6c677YronCTkDsfcbaxO2w4G4rrr7/2tsvvvvGVbAE99qXr8EBIzywwgc7srDDyoZLLrbufluxv6EOUFTC9XWsLi0g0ycyvCQ/HPLJH6tsMsu/lDzfyR7H7PLMMKe8McEit7wzxD3b/PPKQesMrcWh+kxqnzm7sjSeTaPyNJQ0Kz31oVGHcnWSVQu9tY5dG/01jmE7PTbYWW9yNtpFm712pDQ3HMHbZEf8lN0E0A03sxjTG6/eIU4sMd6AW4q3VYQXvunhXMkNgeKLOw6I4I9DPiLlGZMnbnngjKsl+ealdq6V5qB7iDnin5f+YQIAIfkEBRQABAAsMgAyAJwAnAAAA/84utxOMMpJa3Q4Wyi6/2AojmRpnpm2rWzqKisqz3T9qmxO3Wls/8AgD6YrXoaPjXDJbBqfyKSlSa1Gn8Zow2fterdYnZbB/ZqtYfGYOD27qencml1525fx1rx87yPzfFdKfoRDgINrgYWLYIc7e4iMkmSOj4mRk5OVlmOKmYWbE3MDnp9+oRKjpaZ3qEeXbayMrhyQsbKgtKqYuH20BLu3vb66tnVAv4vJxpw1y4TPnbwz0cSuwcc/1a3FsNk223bhhtMy427nN6sm6WbtLusl717zON/O3af5WvEk9Wj7BAmj8a9KwUb3CAYUt5DcQGoN0UVUVw7FQScTX/QbcZH/SUc6zRReyzXSW0iIJfWl5FfxxEchLzeKeIktVE1RyDJ+idmSnU56P+2dNPcrzs1KR1+BK5om6SGnwHIyxQI1D9QgU8NUNcoMp7asULoiFasUH9giW5uSrfX1rBqTm65KdasH7li7ZUXSlSkFldy2e3uOGoyXrcqEwwhTfPiGb2LFjjEyfqwYQ2SPgilXJpW5y2XN0iaf+QxaIOLGnUv/PTxU9WbSWFO7Dn1aoujZg2HPbY07t2w4v3vz0A2Yt/DCUaEFPw5vOR7nzC1Dj307+p/pu71a910daPftGrEX1w4euTLxm9Oz/A6QfYjA8OPLn0+/vv37+PPr38+/v////wAGKOCABBZo4IEIJqjgVAE06OCDEEYo4YQUVmihhMQBoOGGHHbo4YcghsjhhSSWaOKJDmYo4oostqghijDGGKOKLtZo44sy5qgjhTTe6OOKOwYpZAA9/mikh0MmKWORRzYJgJJQnsikk0ZGaeWFU1Lp45VcTpilljZ2KeaDX4Lp4pholmkmi2iOqeaaIrYp5ptwgihnl3TWieSdV+ap54h8WunnnzgGCuWghBoaJaJ/KnooeoTW6KiSjOo5aZKV1pnjL5tCp1+nroBaG4ufLkmLqMaJWOqMp5rqXoerwsipq6OuGCuKs7L6Koe3StmqrrWqmh+qmxCbipG9mpirrP+eDktrKMbmVWOyJS6La7P4RXuItsn5SC2J1vq664bfYvkrs+NqWK6F4SqL7X3c5sHtketW2G6179oXbxzzIusssNA+S56N9fJ47rXpAlCwlweLG2yIC7fJU7aXkhnUhxGnebGHGbu5Maz/Vkzkx7yGXPHE8IrcIMr6qjzySgSbfCnL9bn8sl/+UqwyTZHeaDPPPUvqMtBBt/gzyUVvOTTSSYe5NMxNr3k01FGDOTXOVWv6NNZZS721TV3DaXO/YZu5bxpkl63l2WGkrbaTbGPh9ttHxv3E3HT/aLcReOfts8CV9O230AAXC7i0gxOOLiqCJ87m4dtC3q3jThceuOQElP+YAAAh+QQFFAAEACwyADIAnACcAAAD/xi63E4wyklrdDhbOLr/YCiOZGmKWcpsbEuoMHvOdG17sOruVJ7Kt6Aw6NPwjq/iYzNsOkvKJXIXbQCfWGx1NaVuFdesWPgFd13lQHjMpqXP6PK6TSe94ay7pc6HyvEbehV9hCGCgBOHE4WMHYqIEI8RjYySiJYElIWYeJiahJxwnp98oWejpHSmXaipbKtTra5isEiys1p/kIm6g7hjtUe3v03BPMM0uxTFvcpJX3M1zhLM0NORzYtD1xxDxl7We9vc1Vvcz+ZM49flVefIM+ftUe/Z1OvT80r14b5C8t7sQYJ3AiAZgZcQZsLnTF8RfunE/SMXsJ8zgiYMElHYSf9hE403vsWxqG0iu4oRp2EsAdKGyBYrSbSs8TKPR4bKHPqA6E6dyXwoe16LOWKmG46ibv5sGJQeN6IijM6oGUhpkHMdSe6CGgJrUq0Drd7wegppWbDdlpIFl/KiWBtrY5ll9VZaXGFz5aJdqPZu1b1Z25a86petUJV1kxUeKXhr4niLYaaZTFmKP03RjlbePDkzIc8nOIt+3Ae0idGonUrE7HNj6tc6WlMy7Qe2bcvLSNG2c7v3gt1tgKPw7Vv4GOMgiBeX3Qj5B+W9nWOR7gi6bepOsFu/zpyR9u2vsX/srhn8aPE47x00f578Z/eh2bdfPRv+afmi0fed1BQ/VzH/3/lXmX6E0eeSgAPaV0eACP6XBXaRRSjhhBRWaOGFGGao4YYcdujhhyCGKOKIJJZo4okopqjiimQB4OKLMMYo44w01mjjjTMSKMCOPPbo449ABinkkDgWaeSROOpI5JJMNonkk1BGqaSTVFYZ5ZVY3jillVx2meWXSG7p5Zhkgmmmi2KWqeaZbBqZ5ppwtilnjG/GaeecbNZ55554Yqknn4D2eeSfgRYqaI2EGqrooS8muiijkDr6KKSCSjoppXNaeimmeSq46aec2qgpqKH66SmpqJYKwKipqjroqa3yKVWSsP64oaknSVmrj7deOauWu/bYq665QgmhhrgCRexl/1UOayxFy+bGpbNP/ipqsDxSGya0zxropLavFlsttjuC6ya343rbpLlFWosouQKwS6u426rLpLzA0hsus1Tie62+59q7pL/vAtwuvATT6K7CCCPrK7r18vutw9Hm9LDARCacI8T7SmulxjIuvDHGQ4JMJ8cBS7wuxa6GjPK9LLcMo8i2xiwzmi8PbPPNNPO6s8w9C/tzy0FnO7SrRZd7tKpJx7t0qU2bzGjUT4fadKxYn2xw1lwfvHXXYDP8ddhkN5pz2WhfjTbQZ68dttpuM9123De7PDbddZvJatZUk4x3xbsk6/Hfa/atMuGCWww4f4gXPrfYhzferbKTDy554hmBXxz55R0rXvlgnGvO1OJphS665+luTncCADs=">
+ </image>
+ </slot>
+ </view>
+
+ <!-- 错误处理 -->
+ <view class="uqrcode-error"
+ v-if="isError"
+ @click="onClick">
+ <slot name="error"
+ :error="error">
+ <text class="uqrcode-error-message">{{ error.errMsg }}</text>
+ </slot>
+ </view>
+
+ <!-- H5保存提示 -->
+ <!-- #ifdef H5 -->
+ <view class="uqrcode-h5-save"
+ v-if="isH5Save">
+ <slot name="h5save"
+ :tempFilePath="tempFilePath">
+ <image class="uqrcode-h5-save-image"
+ :src="tempFilePath"></image>
+ <text class="uqrcode-h5-save-text">{{ h5SaveIsDownload ? '若保存失败,' : '' }}请长按二维码进行保存</text>
+ </slot>
+ <view class="uqrcode-h5-save-close"
+ @click.stop="isH5Save = false">
+ <view class="uqrcode-h5-save-close-before"></view>
+ <view class="uqrcode-h5-save-close-after"></view>
+ </view>
+ </view>
+ <!-- #endif -->
+ </view>
+</template>
+
+<script>
+ import props from './props.js';
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef VUE3
+ import { toRaw } from 'vue';
+ // #endif
+ /* 引入uvQRCode核心js */
+ import UQRCode from './qrcode';
+ /* 引入nvue所需模块 */
+ // #ifdef APP-NVUE
+ import { enable, WeexBridge } from './gcanvas';
+ const modal = weex.requireModule('modal');
+ // #endif
+ /* 引入队列 */
+ import { queueDraw, queueLoadImage } from './queue';
+ /* 引入缓存图片 */
+ import { cacheImageList } from './cache';
+ let instance = null;
+ /**
+ * qrcode 二维码
+ * @description 二维码生成插件,可扩展性高,它支持自定义渲染二维码,可通过uQRCode API得到二维码绘制关键信息后,使用canvas、svg或js操作dom的方式绘制二维码图案。还可自定义二维码样式,如随机颜色、圆点、方块、块与块之间的间距等。
+ * @tutorial https://www.uvui.cn/components/qrcode.html
+ * @property {String} value 二维码内容 (start为true时必填 )
+ * @property {Object} options 二维码配置选项 (data|size|margin...)
+ * @property {String} fileType 导出的文件类型 (jpg | png)
+ * @property {String} start 是否初始化组件后就开始生成 (默认 true)
+ * @property {String} auto 是否数据发生改变自动重绘 (默认 false)
+ * @property {String} hide 隐藏组件。如果只需要导出二维码作为图片使用,可设置为true,不能在组件或组件父级元素设置v-if="false"、v-show="false"、style="display:none;"等实现隐藏效果,这样会导致导出二维码空白 (默认 false)
+ * @property {String} type canvas组件类型。微信小程序默认2d (默认 undefined)
+ * @property {String} queue 队列绘制 (默认 false)
+ * @property {String} isQueueLoadImage 是否队列加载图片,选择true将通过队列缓存所需要加载的图片。优点是加载重复资源可减少资源请求次数,节省网络资源,缺点是会转化为同步请求,资源不重复且多的情况下,等待时间会更久。总之,请求重复资源较多则选择true,请求不重复资源较多则选择false (默认 false)
+ * @property {String} loading loading态 (默认 false)
+ * @property {String} h5SaveIsDownload H5保存即自动下载(在支持的环境下),默认false为仅弹层提示用户需要长按图片保存,不会自动下载 (默认 false)
+ * @property {String} h5DownloadName H5下载名称
+ * @property {String} h5SaveTip H5保存二维码时候是否显示提示
+ * @example <uv-qrcode ref="uvqrcode" size="400rpx" canvas-id="qrcode" value="https://www.uvui.cn"></uv-qrcode>
+ */
+ export default {
+ name: 'uv-qrcode',
+ mixins: [mpMixin,mixin,props],
+ emits: ['click','change','complete'],
+ data() {
+ return {
+ canvasId: "",
+ canvas: undefined,
+ canvasType: undefined,
+ canvasContext: undefined,
+ makeDelegate: undefined,
+ drawDelegate: undefined,
+ toTempFilePathDelegate: undefined,
+ makeExecuted: false,
+ makeing: false,
+ drawing: false,
+ isError: false,
+ error: undefined,
+ isH5Save: false,
+ tempFilePath: '',
+ templateOptions: {
+ size: 0,
+ width: 0, // 组件宽度
+ height: 0,
+ canvasWidth: 0, // canvas宽度
+ canvasHeight: 0,
+ canvasTransform: '',
+ canvasDisplay: false
+ },
+ uqrcodeOptions: {
+ data: ''
+ },
+ plugins: [],
+ makeingPattern: [
+ [
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true]
+ ],
+ [
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, false, false, false],
+ [true, true, true, true, true, true, false, true, true, true],
+ [true, true, true, true, true, true, false, true, true, true],
+ [true, true, true, true, true, true, false, true, true, true]
+ ],
+ [
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, true, true, true, true, false, false, false],
+ [true, true, true, true, true, true, true, false, false, false],
+ [true, true, true, true, true, true, true, false, false, false],
+ [true, true, true, false, false, false, false, true, true, true],
+ [true, true, true, false, false, false, false, true, true, true]
+ ],
+ [
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, false, false, false, false, false, false, false],
+ [true, true, true, false, false, false, false, false, false, false],
+ [true, true, true, false, false, false, false, false, false, false],
+ [true, true, true, false, false, false, false, false, false, false],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true],
+ [true, true, true, true, true, true, true, true, true, true]
+ ]
+ ]
+ };
+ },
+ watch: {
+ type: {
+ handler(val) {
+ const types = ['2d'];
+ if (types.includes(val)) {
+ this.canvasType = val;
+ } else {
+ this.canvasType = undefined;
+ }
+ },
+ immediate: true
+ },
+ value: {
+ handler() {
+ if (this.auto) {
+ this.remake();
+ }
+ }
+ },
+ size: {
+ handler() {
+ if (this.auto) {
+ this.remake();
+ }
+ }
+ },
+ options: {
+ handler() {
+ if (this.auto) {
+ this.remake();
+ }
+ },
+ deep: true
+ },
+ makeing: {
+ handler(val) {
+ if (!val) {
+ if (typeof this.toTempFilePathDelegate === 'function') {
+ this.toTempFilePathDelegate();
+ }
+ }
+ }
+ }
+ },
+ created() {
+ this.canvasId = this.$uv.guid();
+ },
+ mounted() {
+ this.templateOptions.size = this.$uv.getPx(this.size);
+ this.templateOptions.width = this.templateOptions.size;
+ this.templateOptions.height = this.templateOptions.size;
+ this.templateOptions.canvasWidth = this.templateOptions.size;
+ this.templateOptions.canvasHeight = this.templateOptions.size;
+ if (this.canvasType == '2d') {
+ // #ifndef MP-WEIXIN
+ this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
+ this.templateOptions.canvasHeight})`;
+ // #endif
+ } else {
+ this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
+ this.templateOptions.canvasHeight})`;
+ }
+ if (this.start) {
+ this.$nextTick(()=>{
+ this.make();
+ })
+ }
+ },
+ methods: {
+ /**
+ * 获取模板选项
+ */
+ getTemplateOptions() {
+ var size = this.$uv.getPx(this.size);
+ return deepReplace(this.templateOptions, {
+ size,
+ width: size,
+ height: size
+ });
+ },
+ /**
+ * 获取插件选项
+ */
+ getUqrcodeOptions() {
+ return deepReplace(this.options, {
+ data: String(this.value),
+ size: Number(this.templateOptions.size)
+ });
+ },
+ /**
+ * 重置画布
+ */
+ resetCanvas(callback) {
+ this.templateOptions.canvasDisplay = false;
+ this.$nextTick(() => {
+ this.templateOptions.canvasDisplay = true;
+ this.$nextTick(() => {
+ callback && callback();
+ });
+ });
+ },
+ /**
+ * 绘制二维码
+ */
+ async draw(callback = {}, isDrawDelegate = false) {
+ if (typeof callback.success != 'function') {
+ callback.success = () => {};
+ }
+ if (typeof callback.fail != 'function') {
+ callback.fail = () => {};
+ }
+ if (typeof callback.complete != 'function') {
+ callback.complete = () => {};
+ }
+
+ if (this.drawing) {
+ if (!isDrawDelegate) {
+ this.drawDelegate = () => {
+ this.draw(callback, true);
+ };
+ return;
+ }
+ } else {
+ this.drawing = true;
+ }
+
+ if (!this.canvasId) {
+ console.error('[uQRCode]: canvasId must be set!');
+ this.isError = true;
+ this.drawing = false;
+ callback.fail({
+ errMsg: '[uQRCode]: canvasId must be set!'
+ });
+ callback.complete({
+ errMsg: '[uQRCode]: canvasId must be set!'
+ });
+ return;
+ }
+ if (!this.value) {
+ console.error('[uQRCode]: value must be set!');
+ this.isError = true;
+ this.drawing = false;
+ callback.fail({
+ errMsg: '[uQRCode]: value must be set!'
+ });
+ callback.complete({
+ errMsg: '[uQRCode]: value must be set!'
+ });
+ return;
+ }
+
+ /* 组件数据 */
+ this.templateOptions = this.getTemplateOptions();
+ /* uQRCode选项 */
+ this.uqrcodeOptions = this.getUqrcodeOptions();
+ /* 纠错等级兼容字母写法 */
+ if (typeof this.uqrcodeOptions.errorCorrectLevel === 'string') {
+ this.uqrcodeOptions.errorCorrectLevel = UQRCode.errorCorrectLevel[this.uqrcodeOptions.errorCorrectLevel];
+ }
+ /* nvue不支持动态修改gcanvas尺寸,除nvue外,默认使用useDynamicSize */
+ // #ifndef APP-NVUE
+ if (typeof this.options.useDynamicSize === 'undefined') {
+ this.uqrcodeOptions.useDynamicSize = true;
+ }
+ // #endif
+ // #ifdef APP-NVUE
+ if (typeof this.options.useDynamicSize === 'undefined') {
+ this.uqrcodeOptions.useDynamicSize = false;
+ }
+ // if (typeof this.options.drawReserve === 'undefined') {
+ // this.uqrcodeOptions.drawReserve = true;
+ // }
+ // #endif
+
+ /* 获取uQRCode实例 */
+ const qr = instance = new UQRCode();
+ /* 注册扩展 */
+ this.plugins.forEach(p => qr.register(p.plugin));
+ /* 设置uQRCode选项 */
+ qr.setOptions(this.uqrcodeOptions);
+ /* 调用制作二维码方法 */
+ qr.make();
+
+ /* 获取canvas上下文 */
+ let canvasContext = null;
+ // #ifndef APP-NVUE
+ if (this.canvasType === '2d') {
+ // #ifdef MP-WEIXIN
+ /* 微信小程序获取canvas2d上下文方式 */
+ const canvas = (this.canvas = await new Promise(resolve => {
+ uni
+ .createSelectorQuery()
+ .in(this) // 在组件内使用需要
+ .select(`#${this.canvasId}`)
+ .fields({
+ node: true,
+ size: true
+ })
+ .exec(res => {
+ resolve(res[0].node);
+ });
+ }));
+ canvasContext = this.canvasContext = canvas.getContext('2d');
+ /* 2d的组件设置宽高与实际canvas绘制宽高不是一个,打个比方,组件size=200,canvas.width设置为100,那么绘制出来就是100=200,组件size=400,canvas.width设置为800,绘制大小还是800=400,所以无需理会下方返回的dynamicSize是多少,按dpr重新赋值给canvas即可 */
+ this.templateOptions.canvasWidth = qr.size;
+ this.templateOptions.canvasHeight = qr.size;
+ this.templateOptions.canvasTransform = '';
+ /* 使用dynamicSize+scale,可以解决小块间出现白线问题,dpr可以解决模糊问题 */
+ const dpr = uni.getSystemInfoSync().pixelRatio;
+ canvas.width = qr.dynamicSize * dpr;
+ canvas.height = qr.dynamicSize * dpr;
+ canvasContext.scale(dpr, dpr);
+ /* 微信小程序获取图像方式 */
+ qr.loadImage = this.getLoadImage(function(src) {
+ /* 小程序下获取网络图片信息需先配置download域名白名单才能生效 */
+ return new Promise((resolve, reject) => {
+ const img = canvas.createImage();
+ img.src = src;
+ img.onload = () => {
+ resolve(img);
+ };
+ img.onerror = err => {
+ reject(err);
+ };
+ });
+ });
+ // #endif
+ // #ifndef MP-WEIXIN
+ /* 非微信小程序不支持2d,切换回uniapp获取canvas上下文方式 */
+ canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this);
+ /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */
+ this.templateOptions.canvasWidth = qr.dynamicSize;
+ this.templateOptions.canvasHeight = qr.dynamicSize;
+ this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
+ this.templateOptions.canvasHeight})`;
+ /* uniapp获取图像方式 */
+ qr.loadImage = this.getLoadImage(function(src) {
+ return new Promise((resolve, reject) => {
+ if (src.startsWith('http')) {
+ uni.getImageInfo({
+ src,
+ success: res => {
+ resolve(res.path);
+ },
+ fail: err => {
+ reject(err);
+ }
+ });
+ } else {
+ if (src.startsWith('.')) {
+ console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
+ throw new Error('[uQRCode]: local image path only supports absolute path!');
+ } else {
+ resolve(src);
+ }
+ }
+ });
+ });
+ // #endif
+ } else {
+ /* uniapp获取canvas上下文方式 */
+ canvasContext = this.canvasContext = uni.createCanvasContext(this.canvasId, this);
+ /* 使用dynamicSize,可以解决小块间出现白线问题,再通过scale缩放至size,使其达到所设尺寸 */
+ this.templateOptions.canvasWidth = qr.dynamicSize;
+ this.templateOptions.canvasHeight = qr.dynamicSize;
+ this.templateOptions.canvasTransform = `scale(${this.templateOptions.size / this.templateOptions.canvasWidth}, ${this.templateOptions.size /
+ this.templateOptions.canvasHeight})`;
+ /* uniapp获取图像方式 */
+ qr.loadImage = this.getLoadImage(function(src) {
+ return new Promise((resolve, reject) => {
+ /* getImageInfo在微信小程序的bug:本地路径返回路径会把开头的/或../移除,导致路径错误,解决方法:限制只能使用绝对路径 */
+ if (src.startsWith('http')) {
+ uni.getImageInfo({
+ src,
+ success: res => {
+ resolve(res.path);
+ },
+ fail: err => {
+ reject(err);
+ }
+ });
+ } else {
+ if (src.startsWith('.')) {
+ console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
+ throw new Error('[uQRCode]: local image path only supports absolute path!');
+ } else {
+ resolve(src);
+ }
+ }
+ });
+ });
+ }
+ // #endif
+ // #ifdef APP-NVUE
+ /* NVue获取canvas上下文方式 */
+ const gcanvas = this.$refs['gcanvas'];
+ const canvas = enable(gcanvas, {
+ bridge: WeexBridge
+ });
+ canvasContext = this.canvasContext = canvas.getContext('2d');
+ /* NVue获取图像方式 */
+ qr.loadImage = this.getLoadImage(function(src) {
+ return new Promise((resolve, reject) => {
+ /* getImageInfo在nvue的bug:获取同一个路径的图片信息,同一时间第一次获取成功,后续失败,猜测是写入本地时产生文件写入冲突,所以没有返回,特别是对于网络资源 --- 已实现队列绘制,已解决此问题 */
+ if (src.startsWith('.')) {
+ console.error('[uQRCode]: 本地图片路径仅支持绝对路径!');
+ throw new Error('[uQRCode]: local image path only supports absolute path!');
+ } else {
+ uni.getImageInfo({
+ src,
+ success: res => {
+ resolve(res.path);
+ },
+ fail: err => {
+ reject(err);
+ }
+ });
+ }
+ });
+ });
+ // #endif
+
+ /* 设置uQRCode实例的canvas上下文 */
+ qr.canvasContext = canvasContext;
+ /* 延时等待页面重新绘制完毕 */
+ setTimeout(() => {
+ /* 从插件获取具体要调用哪一个扩展函数 */
+ var plugin = this.plugins.find(p => p.name == qr.style);
+ var drawCanvasName = plugin ? plugin.drawCanvas : 'drawCanvas';
+ /* 虽然qr[drawCanvasName]是直接返回Promise的,但由于js内部this指向问题,故不能直接exec(qr[drawCanvasName])此方式执行,需要改成exec(() => qr[drawCanvasName]())才能正确获取this */
+ var drawCanvas;
+ if (this.queue) {
+ drawCanvas = () => queueDraw.exec(() => qr[drawCanvasName]());
+ // drawCanvas = () => queueDraw.exec(() => new Promise((resolve, reject) => {
+ // setTimeout(() => {
+ // qr[drawCanvasName]().then(resolve).catch(reject);
+ // }, 1000);
+ // }));
+ } else {
+ drawCanvas = () => qr[drawCanvasName]();
+ }
+ /* 调用绘制方法将二维码图案绘制到canvas上 */
+ drawCanvas()
+ .then(() => {
+ if (this.drawDelegate) {
+ /* 高频重绘纠正 */
+ let delegate = this.drawDelegate;
+ this.drawDelegate = undefined;
+ delegate();
+ } else {
+ this.drawing = false;
+ callback.success();
+ }
+ })
+ .catch(err => {
+ console.log(err);
+ if (this.drawDelegate) {
+ /* 高频重绘纠正 */
+ let delegate = this.drawDelegate;
+ this.drawDelegate = undefined;
+ delegate();
+ } else {
+ this.drawing = false;
+ this.isError = true;
+ callback.fail(err);
+ }
+ })
+ .finally(() => {
+ callback.complete();
+ });
+ }, 300);
+ },
+ /**
+ * 生成二维码
+ */
+ make(callback = {}) {
+ this.makeExecuted = true;
+ this.makeing = true;
+ this.isError = false;
+
+ if (typeof callback.success != 'function') {
+ callback.success = () => {};
+ }
+ if (typeof callback.fail != 'function') {
+ callback.fail = () => {};
+ }
+ if (typeof callback.complete != 'function') {
+ callback.complete = () => {};
+ }
+
+ this.resetCanvas(() => {
+ clearTimeout(this.makeDelegate);
+ this.makeDelegate = setTimeout(() => {
+ this.draw({
+ success: () => {
+ setTimeout(() => {
+ callback.success();
+ this.complete(true);
+ }, 300);
+ },
+ fail: err => {
+ callback.fail(err);
+ this.error = err;
+ this.complete(false, err.errMsg);
+ },
+ complete: () => {
+ callback.complete();
+ this.makeing = false;
+ }
+ });
+ }, 300);
+ });
+ },
+ /**
+ * 重新生成
+ */
+ remake(callback) {
+ this.$emit('change');
+ this.make(callback);
+ },
+ /**
+ * 生成完成
+ */
+ complete(success = true, errMsg = '') {
+ if (success) {
+ this.$emit('complete', {
+ success
+ });
+ } else {
+ this.$emit('complete', {
+ success,
+ errMsg
+ });
+ }
+ },
+ /**
+ * 导出临时路径
+ */
+ toTempFilePath(callback = {}) {
+ if (typeof callback.success != 'function') {
+ callback.success = () => {};
+ }
+ if (typeof callback.fail != 'function') {
+ callback.fail = () => {};
+ }
+ if (typeof callback.complete != 'function') {
+ callback.complete = () => {};
+ }
+
+ if (!this.makeExecuted) {
+ console.error('[uQRCode]: make() 方法从未调用!请先成功调用 make() 后再进行操作。');
+ var err = {
+ errMsg: '[uQRCode]: make() method has never been executed! please execute make() successfully before operating.'
+ };
+ callback.fail(err);
+ callback.complete(err);
+ return;
+ }
+
+ if (this.isError) {
+ callback.fail(this.error);
+ callback.complete(this.error);
+ return;
+ }
+
+ if (this.makeing) {
+ /* 如果还在生成状态,那当前操作将托管到委托,监听生成完成后再通过委托复调当前方法 */
+ this.toTempFilePathDelegate = () => {
+ this.toTempFilePath(callback);
+ };
+ return;
+ } else {
+ this.toTempFilePathDelegate = null;
+ }
+
+ // #ifndef APP-NVUE
+ if (this.canvasType === '2d') {
+ // #ifdef MP-WEIXIN
+ try {
+ let dataURL = null;
+ // #ifdef VUE3
+ dataURL = toRaw(this.canvas)
+ .toDataURL();
+ // #endif
+ // #ifndef VUE3
+ dataURL = this.canvas.toDataURL();
+ // #endif
+ callback.success({
+ tempFilePath: dataURL
+ });
+ callback.complete({
+ tempFilePath: dataURL
+ });
+ } catch (e) {
+ callback.fail(e);
+ callback.complete(e);
+ }
+ // #endif
+ } else {
+ uni.canvasToTempFilePath({
+ canvasId: this.canvasId,
+ fileType: this.fileType,
+ width: Number(this.templateOptions.canvasWidth),
+ height: Number(this.templateOptions.canvasHeight),
+ destWidth: Number(this.templateOptions.size),
+ destHeight: Number(this.templateOptions.size),
+ success: res => {
+ callback.success(res);
+ },
+ fail: err => {
+ callback.fail(err);
+ },
+ complete: () => {
+ callback.complete();
+ }
+ },
+ this
+ );
+ }
+ // #endif
+ // #ifdef APP-NVUE
+ const dpr = uni.getSystemInfoSync().pixelRatio;
+ this.canvasContext.toTempFilePath(
+ 0,
+ 0,
+ this.templateOptions.canvasWidth * dpr,
+ this.templateOptions.canvasHeight * dpr,
+ this.templateOptions.size * dpr,
+ this.templateOptions.size * dpr,
+ '',
+ 1,
+ res => {
+ callback.success(res);
+ callback.complete(res);
+ }
+ );
+ // #endif
+ },
+ /**
+ * 保存
+ */
+ save(callback = {}) {
+ if (typeof callback.success != 'function') {
+ callback.success = () => {};
+ }
+ if (typeof callback.fail != 'function') {
+ callback.fail = () => {};
+ }
+ if (typeof callback.complete != 'function') {
+ callback.complete = () => {};
+ }
+
+ this.toTempFilePath({
+ success: res => {
+ // #ifndef H5
+ if (this.canvasType === '2d') {
+ // #ifdef MP-WEIXIN
+ /* 需要将 data:image/png;base64, 这段去除 writeFile 才能正常打开文件,否则是损坏文件,无法打开 */
+ const reg = new RegExp('^data:image/png;base64,', 'g');
+ const dataURL = res.tempFilePath.replace(reg, '');
+ const fs = wx.getFileSystemManager();
+ const tempFilePath = `${wx.env.USER_DATA_PATH}/${new Date().getTime()}${
+ Math.random()
+ .toString()
+ .split('.')[1]
+ }.png`;
+ fs.writeFile({
+ filePath: tempFilePath, // 要写入的文件路径 (本地路径)
+ data: dataURL, // base64图片
+ encoding: 'base64',
+ success: res1 => {
+ uni.saveImageToPhotosAlbum({
+ filePath: tempFilePath,
+ success: res2 => {
+ callback.success(res2);
+ },
+ fail: err2 => {
+ callback.fail(err2);
+ },
+ complete: () => {
+ callback.complete();
+ }
+ });
+ },
+ fail: err => {
+ callback.fail(err);
+ },
+ complete: () => {
+ callback.complete();
+ }
+ });
+ // #endif
+ } else {
+ uni.saveImageToPhotosAlbum({
+ filePath: res.tempFilePath,
+ success: res1 => {
+ callback.success(res1);
+ },
+ fail: err1 => {
+ callback.fail(err1);
+ },
+ complete: () => {
+ callback.complete();
+ }
+ });
+ }
+ // #endif
+
+ // #ifdef H5
+ /* 可以在电脑浏览器下载,移动端iOS不行,安卓微信浏览器不行,安卓外部浏览器可以 */
+ this.isH5Save = this.h5SaveTip;
+ this.tempFilePath = res.tempFilePath;
+ if (this.h5SaveIsDownload) {
+ const aEle = document.createElement('a');
+ aEle.download = this.h5DownloadName; // 设置下载的文件名,默认是'下载'
+ aEle.href = res.tempFilePath;
+ document.body.appendChild(aEle);
+ aEle.click();
+ aEle.remove(); // 下载之后把创建的元素删除
+ }
+ callback.success({
+ errMsg: 'ok'
+ });
+ callback.complete({
+ errMsg: 'ok'
+ });
+ // #endif
+ },
+ fail: err => {
+ callback.fail(err);
+ callback.complete(err);
+ }
+ });
+ },
+ /**
+ * 注册click事件
+ */
+ onClick(e) {
+ this.$emit('click', e);
+ },
+ /**
+ * 获取实例
+ */
+ getInstance() {
+ return instance;
+ },
+ /**
+ * 注册扩展,组件仅支持注册type为style的drawCanvas扩展
+ * @param {Object} plugin
+ */
+ registerStyle(plugin) {
+ if (plugin.Type != 'style') {
+ console.warn('[uQRCode]: registerStyle 仅支持注册 style 类型的扩展!');
+ return {
+ errMsg: 'registerStyle 仅支持注册 style 类型的扩展!'
+ };
+ }
+ if (typeof plugin === 'function') {
+ this.plugins.push({
+ plugin,
+ name: plugin.Name,
+ drawCanvas: plugin.DrawCanvas
+ });
+ }
+ },
+ getLoadImage(loadImage) {
+ var that = this;
+ if (typeof loadImage == 'function') {
+ return function(src) {
+ /* 判断是否是队列加载图片的 */
+ if (that.isQueueLoadImage) {
+ /* 解决iOS APP||NVUE同时绘制多个二维码导致图片丢失需使用队列 */
+ return queueLoadImage.exec(() => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ const cache = cacheImageList.find(x => x.src == src);
+ if (cache) {
+ resolve(cache.img);
+ } else {
+ loadImage(src)
+ .then(img => {
+ cacheImageList.push({
+ src,
+ img
+ });
+ resolve(img);
+ })
+ .catch(err => {
+ reject(err);
+ });
+ }
+ }, 10);
+ });
+ });
+ } else {
+ return loadImage(src);
+ }
+ };
+ } else {
+ return function(src) {
+ return Promise.resolve(src);
+ };
+ }
+ }
+ }
+ };
+
+ /**
+ * 对象属性深度替换
+ * @param {Object} o 原始对象/默认对象/被替换的对象
+ * @param {Object} r 从这个对象里取值替换到o对象里
+ * @return {Object} 替换后的新对象
+ */
+ function deepReplace(o = {}, r = {}, c = false) {
+ let obj;
+ if (c) {
+ // 从源替换
+ obj = o;
+ } else {
+ // 不替换源,copy一份备份来替换
+ obj = {
+ ...o
+ };
+ }
+ for (let k in r) {
+ var vr = r[k];
+ if (vr != undefined) {
+ if (vr.constructor == Object) {
+ obj[k] = this.deepReplace(obj[k], vr);
+ } else if (vr.constructor == String && !vr) {
+ obj[k] = obj[k];
+ } else {
+ obj[k] = vr;
+ }
+ }
+ }
+ return obj;
+ }
+</script>
+
+<style scoped>
+ .uqrcode {
+ position: relative;
+ }
+ .uqrcode-hide {
+ position: fixed;
+ left: 7500rpx;
+ }
+ .uqrcode-canvas {
+ transform-origin: top left;
+ }
+ .uqrcode-makeing {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 10;
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ justify-content: center;
+ align-items: center;
+ }
+ .uqrcode-makeing-image {
+ /* #ifndef APP-NVUE */
+ display: block;
+ max-width: 120px;
+ max-height: 120px;
+ /* #endif */
+ }
+ .uqrcode-error {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ /* #ifndef APP-NVUE */
+ display: flex;
+ /* #endif */
+ justify-content: center;
+ align-items: center;
+ }
+ .uqrcode-error-message {
+ font-size: 12px;
+ color: #939291;
+ }
+ /* #ifdef H5 */
+ .uqrcode-h5-save {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 100;
+ background-color: rgba(0, 0, 0, 0.68);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ }
+ .uqrcode-h5-save-image {
+ width: 512rpx;
+ height: 512rpx;
+ padding: 32rpx;
+ }
+ .uqrcode-h5-save-text {
+ margin-top: 20rpx;
+ font-size: 32rpx;
+ font-weight: 700;
+ color: #ffffff;
+ }
+ .uqrcode-h5-save-close {
+ position: relative;
+ margin-top: 72rpx;
+ width: 60rpx;
+ height: 60rpx;
+ border: 2rpx solid #ffffff;
+ border-radius: 60rpx;
+ padding: 10rpx;
+ }
+ .uqrcode-h5-save-close-before {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) rotate(45deg);
+ width: 40rpx;
+ height: 4rpx;
+ background: #ffffff;
+ }
+ .uqrcode-h5-save-close-after {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%) rotate(-45deg);
+ width: 40rpx;
+ height: 4rpx;
+ background: #ffffff;
+ }
+ /* #endif */
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/package.json b/uni_modules/uv-qrcode/package.json
new file mode 100644
index 0000000..c85e74f
--- /dev/null
+++ b/uni_modules/uv-qrcode/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-qrcode",
+ "displayName": "uv-qrcode 二维码 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "该组件是超级强大二维码生成功能,可扩展性高。自定义二维码样式,如随机颜色、圆点、方块、块与块之间的间距等等,具体请查看文档。",
+ "keywords": [
+ "uv-qrcode",
+ "qrcode",
+ "uvui",
+ "uv-ui",
+ "二维码"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-qrcode/readme.md b/uni_modules/uv-qrcode/readme.md
new file mode 100644
index 0000000..fd4acfe
--- /dev/null
+++ b/uni_modules/uv-qrcode/readme.md
@@ -0,0 +1,21 @@
+## QRCode 二维码生成器
+
+> **组件名:uv-qrcode**
+
+超级强大二维码生成组件,可扩展性高。自定义二维码样式,如随机颜色、圆点、方块、块与块之间的间距等等,具体请在下方查看文档。
+
+灵活配置,开箱即用,文档全面,支持自定义二维码风格。
+
+# <a href="https://www.uvui.cn/components/qrcode.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small style="font-size:14px;font-weight:700;">(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-radio/changelog.md b/uni_modules/uv-radio/changelog.md
new file mode 100644
index 0000000..e51b181
--- /dev/null
+++ b/uni_modules/uv-radio/changelog.md
@@ -0,0 +1,31 @@
+## 1.0.13(2023-12-19)
+1. 修复在group设置labelColor不生效的BUG
+## 1.0.12(2023-11-04)
+1. 修复label文字较多不分行的问题
+## 1.0.11(2023-10-11)
+1. 优化:https://gitee.com/climblee/uv-ui/issues/I872VD
+## 1.0.10(2023-09-01)
+1. 修复点击空隙处无效的问题
+2. label支持插槽下可点击
+## 1.0.9(2023-08-27)
+1. 优化
+## 1.0.8(2023-08-26)
+1. 修复v-model 绑定布尔值控制台报警
+## 1.0.7(2023-08-26)
+1. 修复设置 labelSize 属性设置无效的问题:https://gitee.com/climblee/uv-ui/issues/I7W6UN
+## 1.0.6(2023-08-04)
+1. 修复name为数字0时不能选中的BUG
+## 1.0.5(2023-07-13)
+1. 修复 uv-radio设置value属性不生效的BUG
+## 1.0.4(2023-07-05)
+修复vue3模式下,动态修改v-model绑定的值无效的BUG
+## 1.0.3(2023-06-06)
+1. 修正语法问题
+## 1.0.2(2023-06-06)
+1. uv-radio-group 兼容自定义样式customStyle,方便通过样式调整整体位置等
+2. .uv-radio-group--row增加flex-wrap: wrap;允许换行
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-radio 单选框
diff --git a/uni_modules/uv-radio/components/uv-radio-group/props.js b/uni_modules/uv-radio/components/uv-radio-group/props.js
new file mode 100644
index 0000000..b0dbd15
--- /dev/null
+++ b/uni_modules/uv-radio/components/uv-radio-group/props.js
@@ -0,0 +1,89 @@
+export default {
+ props: {
+ // 绑定的值
+ value: {
+ type: [String, Number, Boolean],
+ default: ''
+ },
+ modelValue: {
+ type: [String, Number, Boolean],
+ default: ''
+ },
+ // 是否禁用全部radio
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 形状,circle-圆形,square-方形
+ shape: {
+ type: String,
+ default: 'circle'
+ },
+ // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+ activeColor: {
+ type: String,
+ default: '#2979ff'
+ },
+ // 未选中的颜色
+ inactiveColor: {
+ type: String,
+ default: '#c8c9cc'
+ },
+ // 标识符
+ name: {
+ type: String,
+ default: ''
+ },
+ // 整个组件的尺寸,默认px
+ size: {
+ type: [String, Number],
+ default: 18
+ },
+ // 布局方式,row-横向,column-纵向
+ placement: {
+ type: String,
+ default: 'row'
+ },
+ // label的文本
+ label: {
+ type: [String],
+ default: ''
+ },
+ // label的颜色 (默认 '#303133' )
+ labelColor: {
+ type: [String],
+ default: '#303133'
+ },
+ // label的字体大小,px单位
+ labelSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 是否禁止点击文本操作checkbox(默认 false )
+ labelDisabled: {
+ type: Boolean,
+ default: false
+ },
+ // 图标颜色
+ iconColor: {
+ type: String,
+ default: '#fff'
+ },
+ // 图标的大小,单位px
+ iconSize: {
+ type: [String, Number],
+ default: 12
+ },
+ // 竖向配列时,是否显示下划线
+ borderBottom: {
+ type: Boolean,
+ default: false
+ },
+ // 图标与文字的对齐方式
+ iconPlacement: {
+ type: String,
+ default: 'left'
+ },
+ ...uni.$uv?.props?.radioGroup
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue b/uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue
new file mode 100644
index 0000000..6b3069f
--- /dev/null
+++ b/uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue
@@ -0,0 +1,115 @@
+<template>
+ <view
+ class="uv-radio-group"
+ :class="bemClass"
+ :style="[$uv.addStyle(this.customStyle)]"
+ >
+ <slot></slot>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * radioRroup 单选框父组件
+ * @description 单选框用于有一个选择,用户只能选择其中一个的场景。搭配uv-radio使用
+ * @tutorial https://www.uvui.cn/components/radio.html
+ * @property {String | Number | Boolean} value 绑定的值
+ * @property {Boolean} disabled 是否禁用所有radio(默认 false )
+ * @property {String} shape 外观形状,shape-方形,circle-圆形(默认 circle )
+ * @property {String} activeColor 选中时的颜色,应用到所有子Radio组件(默认 '#2979ff' )
+ * @property {String} inactiveColor 未选中的颜色 (默认 '#c8c9cc' )
+ * @property {String} name 标识符
+ * @property {String | Number} size 组件整体的大小,单位px(默认 18 )
+ * @property {String} placement 布局方式,row-横向,column-纵向 (默认 'row' )
+ * @property {String} label 文本
+ * @property {String} labelColor label的颜色 (默认 '#303133' )
+ * @property {String | Number} labelSize label的字体大小,px单位 (默认 14 )
+ * @property {Boolean} labelDisabled 是否禁止点击文本操作checkbox(默认 false )
+ * @property {String} iconColor 图标颜色 (默认 '#ffffff' )
+ * @property {String | Number} iconSize 图标的大小,单位px (默认 12 )
+ * @property {Boolean} borderBottom placement为row时,是否显示下边框 (默认 false )
+ * @property {String} iconPlacement 图标与文字的对齐方式 (默认 'left' )
+ * @property {Object} customStyle 组件的样式,对象形式
+ * @event {Function} change 任一个radio状态发生变化时触发
+ * @example <uv-radio-group v-model="value"></uv-radio-group>
+ */
+ export default {
+ name: 'uv-radio-group',
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ // 这里computed的变量,都是子组件uv-radio需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化
+ // 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(uv-radio-group)
+ // 拉取父组件新的变化后的参数
+ parentData() {
+ const value = this.value || this.modelValue;
+ return [value, this.disabled, this.inactiveColor, this.activeColor, this.size, this.labelDisabled, this.shape,
+ this.iconSize, this.borderBottom, this.placement]
+ },
+ bemClass() {
+ // this.bem为一个computed变量,在mixin中
+ return this.bem('radio-group', ['placement'])
+ },
+ },
+ watch: {
+ // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+ parentData() {
+ if (this.children.length) {
+ this.children.map(child => {
+ // 判断子组件(uv-radio)如果有init方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+ typeof(child.init) === 'function' && child.init()
+ })
+ }
+ },
+ },
+ data() {
+ return {
+
+ }
+ },
+ created() {
+ this.children = []
+ },
+ methods: {
+ // 将其他的radio设置为未选中的状态
+ unCheckedOther(childInstance) {
+ this.children.map(child => {
+ // 所有子radio中,被操作组件实例的checked的值无需修改
+ if (childInstance !== child) {
+ child.checked = false
+ }
+ })
+ const {
+ name
+ } = childInstance
+ // 通过emit事件,设置父组件通过v-model双向绑定的值
+ // #ifdef VUE2
+ this.$emit('input', name)
+ // #endif
+ // #ifdef VUE3
+ this.$emit('update:modelValue',name)
+ // #endif
+ // 发出事件
+ this.$emit('change', name)
+ },
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-radio-group {
+ flex: 1;
+
+ &--row {
+ @include flex;
+ flex-wrap: wrap;
+ }
+
+ &--column {
+ @include flex(column);
+ }
+ }
+</style>
diff --git a/uni_modules/uv-radio/components/uv-radio/props.js b/uni_modules/uv-radio/components/uv-radio/props.js
new file mode 100644
index 0000000..1e5e2ac
--- /dev/null
+++ b/uni_modules/uv-radio/components/uv-radio/props.js
@@ -0,0 +1,65 @@
+export default {
+ props: {
+ // radio的名称
+ name: {
+ type: [String, Number, Boolean],
+ default: ''
+ },
+ // 形状,square为方形,circle为圆型
+ shape: {
+ type: String,
+ default: ''
+ },
+ // 是否禁用
+ disabled: {
+ type: [String, Boolean],
+ default: ''
+ },
+ // 是否禁止点击提示语选中单选框
+ labelDisabled: {
+ type: [String, Boolean],
+ default: ''
+ },
+ // 选中状态下的颜色,如设置此值,将会覆盖parent的activeColor值
+ activeColor: {
+ type: String,
+ default: ''
+ },
+ // 未选中的颜色
+ inactiveColor: {
+ type: String,
+ default: ''
+ },
+ // 图标的大小,单位px
+ iconSize: {
+ type: [String, Number],
+ default: ''
+ },
+ // label的字体大小,px单位
+ labelSize: {
+ type: [String, Number],
+ default: ''
+ },
+ // label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
+ label: {
+ type: [String, Number, Boolean],
+ default: ''
+ },
+ // 整体的大小
+ size: {
+ type: [String, Number],
+ default: ''
+ },
+ // 图标颜色
+ iconColor: {
+ type: String,
+ default: ''
+ },
+ // label的颜色
+ labelColor: {
+ type: String,
+ default: ''
+ },
+ ...uni.$uv?.props?.radio
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-radio/components/uv-radio/uv-radio.vue b/uni_modules/uv-radio/components/uv-radio/uv-radio.vue
new file mode 100644
index 0000000..3c0236c
--- /dev/null
+++ b/uni_modules/uv-radio/components/uv-radio/uv-radio.vue
@@ -0,0 +1,348 @@
+<template>
+ <view
+ class="uv-radio"
+ @tap.stop="wrapperClickHandler"
+ :style="[radioStyle]"
+ :class="[`uv-radio-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'uv-border-bottom']"
+ >
+ <view
+ class="uv-radio__icon-wrap"
+ @tap.stop="iconClickHandler"
+ :class="iconClasses"
+ :style="[iconWrapStyle]"
+ >
+ <slot name="icon">
+ <uv-icon
+ class="uv-radio__icon-wrap__icon"
+ name="checkbox-mark"
+ :size="elIconSize"
+ :color="elIconColor"
+ />
+ </slot>
+ </view>
+ <view
+ class="uv-radio__label-wrap"
+ @tap.stop="labelClickHandler">
+ <slot>
+ <text
+ :style="{
+ color: elDisabled ? elInactiveColor : elLabelColor,
+ fontSize: elLabelSize,
+ lineHeight: elLabelSize
+ }"
+ >{{label}}</text>
+ </slot>
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * radio 单选框
+ * @description 单选框用于有一个选择,用户只能选择其中一个的场景。搭配uv-radio-group使用
+ * @tutorial https://www.uvui.cn/components/radio.html
+ * @property {String | Number} name radio的名称
+ * @property {String} shape 形状,square为方形,circle为圆型
+ * @property {Boolean} disabled 是否禁用
+ * @property {String | Boolean} labelDisabled 是否禁止点击提示语选中单选框
+ * @property {String} activeColor 选中时的颜色,如设置parent的active-color将失效
+ * @property {String} inactiveColor 未选中的颜色
+ * @property {String | Number} iconSize 图标大小,单位px
+ * @property {String | Number} labelSize label字体大小,单位px
+ * @property {String | Number} label label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
+ * @property {String | Number} size 整体的大小
+ * @property {String} iconColor 图标颜色
+ * @property {String} labelColor label的颜色
+ * @property {Object} customStyle 组件的样式,对象形式
+ *
+ * @event {Function} change 某个radio状态发生变化时触发(选中状态)
+ * @example <uv-radio :labelDisabled="false">门掩黄昏,无计留春住</uv-radio>
+ */
+ export default {
+ name: "uv-radio",
+
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ checked: false,
+ // 当你看到这段代码的时候,
+ // 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式
+ // 故只能使用如此方法
+ parentData: {
+ iconSize: 12,
+ labelSize: 14,
+ labelColor: '#303133',
+ labelDisabled: null,
+ disabled: null,
+ shape: null,
+ activeColor: null,
+ inactiveColor: null,
+ size: 18,
+ value: null,
+ modelValue: null,
+ iconColor: null,
+ placement: 'row',
+ borderBottom: false,
+ iconPlacement: 'left'
+ }
+ }
+ },
+ computed: {
+ // 是否禁用,如果父组件uv-raios-group禁用的话,将会忽略子组件的配置
+ elDisabled() {
+ return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false;
+ },
+ // 是否禁用label点击
+ elLabelDisabled() {
+ return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled :
+ false;
+ },
+ // 组件尺寸,对应size的值,默认值为21px
+ elSize() {
+ return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21);
+ },
+ // 组件的勾选图标的尺寸,默认12px
+ elIconSize() {
+ return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12);
+ },
+ // 组件选中激活时的颜色
+ elActiveColor() {
+ return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff');
+ },
+ // 组件选未中激活时的颜色
+ elInactiveColor() {
+ return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor :
+ '#c8c9cc');
+ },
+ // label的颜色
+ elLabelColor() {
+ return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266')
+ },
+ // 组件的形状
+ elShape() {
+ return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle');
+ },
+ // label大小
+ elLabelSize() {
+ return this.$uv.addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize :
+ '15'))
+ },
+ elIconColor() {
+ const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor :
+ '#ffffff');
+ // 图标的颜色
+ if (this.elDisabled) {
+ // disabled状态下,已勾选的radio图标改为elInactiveColor
+ return this.checked ? this.elInactiveColor : 'transparent'
+ } else {
+ return this.checked ? iconColor : 'transparent'
+ }
+ },
+ iconClasses() {
+ let classes = []
+ // 组件的形状
+ classes.push('uv-radio__icon-wrap--' + this.elShape)
+ if (this.elDisabled) {
+ classes.push('uv-radio__icon-wrap--disabled')
+ }
+ if (this.checked && this.elDisabled) {
+ classes.push('uv-radio__icon-wrap--disabled--checked')
+ }
+ // 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+ // #ifdef MP-ALIPAY || MP-TOUTIAO
+ classes = classes.join(' ')
+ // #endif
+ return classes
+ },
+ iconWrapStyle() {
+ // radio的整体样式
+ const style = {}
+ style.backgroundColor = this.checked && !this.elDisabled ? this.elActiveColor : '#ffffff'
+ style.borderColor = this.checked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor
+ style.width = this.$uv.addUnit(this.elSize)
+ style.height = this.$uv.addUnit(this.elSize)
+ // 如果是图标在右边的话,移除它的右边距
+ if (this.parentData.iconPlacement === 'right') {
+ style.marginRight = 0
+ }
+ return style
+ },
+ radioStyle() {
+ const style = {}
+ if (this.parentData.borderBottom && this.parentData.placement === 'row') {
+ this.$uv.error('检测到您将borderBottom设置为true,需要同时将uv-radio-group的placement设置为column才有效')
+ }
+ // 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔
+ if (this.parentData.borderBottom && this.parentData.placement === 'column') {
+ // ios像素密度高,需要多一点的距离
+ style.paddingBottom = this.$uv.os() === 'ios' ? '12px' : '8px'
+ }
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ }
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
+ this.updateParentData()
+ if (!this.parent) {
+ this.$uv.error('uv-radio必须搭配uv-radio-group组件使用')
+ }
+ // 设置初始化时,是否默认选中的状态
+ this.$nextTick(()=>{
+ let parentValue = null;
+ // #ifdef VUE2
+ parentValue = this.parentData.value;
+ // #endif
+ // #ifdef VUE3
+ parentValue = this.parentData.modelValue;
+ // #endif
+ this.checked = this.name === parentValue
+ })
+ },
+ updateParentData() {
+ this.getParentData('uv-radio-group')
+ },
+ // 点击图标
+ iconClickHandler(e) {
+ this.preventEvent(e)
+ // 如果整体被禁用,不允许被点击
+ if (!this.elDisabled) {
+ this.setRadioCheckedStatus()
+ }
+ },
+ // 横向两端排列时,点击组件即可触发选中事件
+ wrapperClickHandler(e) {
+ this.parentData.iconPlacement === 'right' && this.iconClickHandler(e)
+ },
+ // 点击label
+ labelClickHandler(e) {
+ this.preventEvent(e)
+ // 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态
+ if (!this.elLabelDisabled && !this.elDisabled) {
+ this.setRadioCheckedStatus()
+ }
+ },
+ emitEvent() {
+ // uv-radio的checked不为true时(意味着未选中),才发出事件,避免多次点击触发事件
+ if (!this.checked) {
+ this.$emit('change', this.name)
+ // 尝试调用uv-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时
+ this.$nextTick(() => {
+ this.$uv.formValidate(this, 'change')
+ })
+ }
+ },
+ // 改变组件选中状态
+ // 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有uv-radio实例
+ // 将本组件外的其他uv-radio的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态
+ setRadioCheckedStatus() {
+ this.emitEvent()
+ // 将本组件标记为选中状态
+ this.checked = true
+ typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this)
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-border: 1;
+ $show-border-bottom: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-radio-label-wrap-padding-right: 6px !default;
+ $uv-radio-wrap-font-size: 20px !default;
+ $uv-radio-wrap-border-width: 1px !default;
+ $uv-radio-wrap-border-color: #c8c9cc !default;
+ $uv-radio-line-height: 0 !default;
+ $uv-radio-circle-border-radius: 100% !default;
+ $uv-radio-square-border-radius: 3px !default;
+ $uv-radio-checked-color: #fff !default;
+ $uv-radio-checked-background-color: red !default;
+ $uv-radio-checked-border-color: #2979ff !default;
+ $uv-radio-disabled-background-color: #ebedf0 !default;
+ $uv-radio-disabled--checked-color: #c8c9cc !default;
+ $uv-radio-label-margin-left: 5px !default;
+ $uv-radio-label-margin-right: 12px !default;
+ $uv-radio-label-color: $uv-content-color !default;
+ $uv-radio-label-font-size: 15px !default;
+ $uv-radio-label-disabled-color: #c8c9cc !default;
+ .uv-radio {
+ /* #ifndef APP-NVUE */
+ @include flex(row);
+ /* #endif */
+ overflow: hidden;
+ flex-direction: row;
+ align-items: center;
+ &-label--left {
+ flex-direction: row
+ }
+ &-label--right {
+ flex-direction: row-reverse;
+ justify-content: space-between
+ }
+ &__icon-wrap {
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ // nvue下,border-color过渡有问题
+ transition-property: border-color, background-color, color;
+ transition-duration: 0.2s;
+ /* #endif */
+ color: $uv-content-color;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ color: transparent;
+ text-align: center;
+ font-size: $uv-radio-wrap-font-size;
+ border-width: $uv-radio-wrap-border-width;
+ border-color: $uv-radio-wrap-border-color;
+ border-style: solid;
+ /* #ifdef MP-TOUTIAO */
+ // 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
+ &__icon {
+ line-height: $uv-radio-line-height;
+ }
+ /* #endif */
+ &--circle {
+ border-radius: $uv-radio-circle-border-radius;
+ }
+ &--square {
+ border-radius: $uv-radio-square-border-radius;
+ }
+ &--checked {
+ color: $uv-radio-checked-color;
+ background-color: $uv-radio-checked-background-color;
+ border-color: $uv-radio-checked-border-color;
+ }
+ &--disabled {
+ background-color: $uv-radio-disabled-background-color !important;
+ }
+ &--disabled--checked {
+ color: $uv-radio-disabled--checked-color !important;
+ }
+ }
+ &__label {
+ /* #ifndef APP-NVUE */
+ word-wrap: break-word;
+ /* #endif */
+ margin-left: $uv-radio-label-margin-left;
+ margin-right: $uv-radio-label-margin-right;
+ color: $uv-radio-label-color;
+ font-size: $uv-radio-label-font-size;
+ &--disabled {
+ color: $uv-radio-label-disabled-color;
+ }
+ }
+ &__label-wrap {
+ flex: 1;
+ padding-left: $uv-radio-label-wrap-padding-right;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-radio/package.json b/uni_modules/uv-radio/package.json
new file mode 100644
index 0000000..e789246
--- /dev/null
+++ b/uni_modules/uv-radio/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-radio",
+ "displayName": "uv-radio 单选框 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.13",
+ "description": "uv-radio 单选框用于有一个选择,用户只能选择其中一个的场景。",
+ "keywords": [
+ "uv-radio",
+ "uvui",
+ "uv-ui",
+ "radio",
+ "单选框"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-radio/readme.md b/uni_modules/uv-radio/readme.md
new file mode 100644
index 0000000..c826898
--- /dev/null
+++ b/uni_modules/uv-radio/readme.md
@@ -0,0 +1,19 @@
+## Radio 单选框
+
+> **组件名:uv-radio**
+
+单选框用于有一个选择,用户只能选择其中一个的场景。
+
+# <a href="https://www.uvui.cn/components/radio.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-rate/changelog.md b/uni_modules/uv-rate/changelog.md
new file mode 100644
index 0000000..f1cbeea
--- /dev/null
+++ b/uni_modules/uv-rate/changelog.md
@@ -0,0 +1,17 @@
+## 1.0.7(2023-08-24)
+1. 修复支付宝不能选半星的BUG
+## 1.0.6(2023-08-23)
+1. 修复支付宝报错的BUG
+## 1.0.5(2023-07-13)
+优化代码
+## 1.0.4(2023-07-13)
+1. 修复value/v-model更改不生效的BUG
+## 1.0.3(2023-06-05)
+1. 修复只读或禁止状态下设置value无效的问题
+## 1.0.2(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.1(2023-05-12)
+1. 修复vue3中双向绑定问题
+## 1.0.0(2023-05-10)
+uv-rate 评分
diff --git a/uni_modules/uv-rate/components/uv-rate/props.js b/uni_modules/uv-rate/components/uv-rate/props.js
new file mode 100644
index 0000000..6eac114
--- /dev/null
+++ b/uni_modules/uv-rate/components/uv-rate/props.js
@@ -0,0 +1,73 @@
+export default {
+ props: {
+ value: {
+ type: [String, Number],
+ default: 0
+ },
+ modelValue: {
+ type: [String, Number],
+ default: 0
+ },
+ // 要显示的星星数量
+ count: {
+ type: [String, Number],
+ default: 5
+ },
+ // 是否不可选中
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否只读
+ readonly: {
+ type: Boolean,
+ default: false
+ },
+ // 星星的大小,单位px
+ size: {
+ type: [String, Number],
+ default: 18
+ },
+ // 未选中时的颜色
+ inactiveColor: {
+ type: String,
+ default: '#b2b2b2'
+ },
+ // 选中的颜色
+ activeColor: {
+ type: String,
+ default: '#FA3534'
+ },
+ // 星星之间的间距,单位px
+ gutter: {
+ type: [String, Number],
+ default: 4
+ },
+ // 最少能选择的星星个数
+ minCount: {
+ type: [String, Number],
+ default: 1
+ },
+ // 是否允许半星
+ allowHalf: {
+ type: Boolean,
+ default: false
+ },
+ // 选中时的图标(星星)
+ activeIcon: {
+ type: String,
+ default: 'star-fill'
+ },
+ // 未选中时的图标(星星)
+ inactiveIcon: {
+ type: String,
+ default: 'star'
+ },
+ // 是否可以通过滑动手势选择评分
+ touchable: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.rate
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-rate/components/uv-rate/uv-rate.vue b/uni_modules/uv-rate/components/uv-rate/uv-rate.vue
new file mode 100644
index 0000000..48e7558
--- /dev/null
+++ b/uni_modules/uv-rate/components/uv-rate/uv-rate.vue
@@ -0,0 +1,274 @@
+<template>
+ <view
+ class="uv-rate"
+ :id="elId"
+ ref="uv-rate"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <view class="uv-rate__content"
+ @touchmove.stop="touchMove"
+ @touchend.stop="touchEnd">
+ <view class="uv-rate__content__item"
+ v-for="(item, index) in Number(count)"
+ :key="index"
+ :class="[elClass]">
+ <view class="uv-rate__content__item__icon-wrap"
+ ref="uv-rate__content__item__icon-wrap"
+ @tap.stop="clickHandler($event, index + 1)">
+ <uv-icon
+ :name="Math.floor(activeIndex) > index? activeIcon : inactiveIcon"
+ :color="disabled ? '#c8c9cc' : Math.floor(activeIndex) > index ? activeColor : inactiveColor"
+ :custom-style="{
+ 'padding-left': $uv.addUnit(gutter / 2),
+ 'padding-right': $uv.addUnit(gutter / 2)
+ }"
+ :size="size"
+ ></uv-icon>
+ </view>
+ <view v-if="allowHalf"
+ @tap.stop="clickHandler($event, index + 1)"
+ class="uv-rate__content__item__icon-wrap uv-rate__content__item__icon-wrap--half"
+ :style="[{ width: $uv.addUnit(rateWidth / 2)}]"
+ ref="uv-rate__content__item__icon-wrap">
+ <uv-icon
+ :name=" Math.ceil(activeIndex) > index ? activeIcon : inactiveIcon "
+ :color=" disabled ? '#c8c9cc' : Math.ceil(activeIndex) > index ? activeColor : inactiveColor "
+ :custom-style="{
+ 'padding-left': $uv.addUnit(gutter / 2),
+ 'padding-right': $uv.addUnit(gutter / 2)
+ }"
+ :size="size">
+ </uv-icon>
+ </view>
+ </view>
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = weex.requireModule("dom");
+ // #endif
+ /**
+ * rate 评分
+ * @description 该组件一般用于满意度调查,星型评分的场景
+ * @tutorial https://www.uvui.cn/components/rate.html
+ * @property {String | Number} value 用于v-model双向绑定选中的星星数量 (默认 1 )
+ * @property {String | Number} count 最多可选的星星数量 (默认 5 )
+ * @property {Boolean} disabled 是否禁止用户操作 (默认 false )
+ * @property {Boolean} readonly 是否只读 (默认 false )
+ * @property {String | Number} size 星星的大小,单位px (默认 18 )
+ * @property {String} inactiveColor 未选中星星的颜色 (默认 '#b2b2b2' )
+ * @property {String} activeColor 选中的星星颜色 (默认 '#FA3534' )
+ * @property {String | Number} gutter 星星之间的距离 (默认 4 )
+ * @property {String | Number} minCount 最少选中星星的个数 (默认 1 )
+ * @property {Boolean} allowHalf 是否允许半星选择 (默认 false )
+ * @property {String} activeIcon 选中时的图标名,只能为uvui的内置图标 (默认 'star-fill' )
+ * @property {String} inactiveIcon 未选中时的图标名,只能为uvui的内置图标 (默认 'star' )
+ * @property {Boolean} touchable 是否可以通过滑动手势选择评分 (默认 'false' )
+ * @property {Object} customStyle 组件的样式,对象形式
+ * @event {Function} change 选中的星星发生变化时触发
+ * @example <uv-rate :count="count" :value="2"></uv-rate>
+ */
+ export default {
+ name: "uv-rate",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 生成一个唯一id,否则一个页面多个评分组件,会造成冲突
+ elId: '',
+ elClass: '',
+ rateBoxLeft: 0, // 评分盒子左边到屏幕左边的距离,用于滑动选择时计算距离
+ activeIndex: 0,
+ rateWidth: 0, // 每个星星的宽度
+ // 标识是否正在滑动,由于iOS事件上touch比click先触发,导致快速滑动结束后,接着触发click,导致事件混乱而出错
+ moving: false
+ }
+ },
+ watch: {
+ value(newVal){
+ this.activeIndex = newVal;
+ },
+ modelValue(newVal){
+ this.activeIndex = newVal;
+ }
+ },
+ created() {
+ this.activeIndex = Number(this.value || this.modelValue);
+ this.elId = this.$uv.guid();
+ this.elClass = this.$uv.guid();
+ },
+ mounted() {
+ this.init();
+ },
+ methods: {
+ init() {
+ this.$uv.sleep(200).then(() => {
+ this.getRateItemRect();
+ this.getRateIconWrapRect();
+ })
+ },
+ // 获取评分组件盒子的布局信息
+ async getRateItemRect() {
+ await this.$uv.sleep();
+ // uvui封装的获取节点的方法,详见文档
+ // #ifndef APP-NVUE
+ this.$uvGetRect("#" + this.elId).then((res) => {
+ this.rateBoxLeft = res.left;
+ });
+ // #endif
+ // #ifdef APP-NVUE
+ dom.getComponentRect(this.$refs["uv-rate"], (res) => {
+ this.rateBoxLeft = res.size.left;
+ });
+ // #endif
+ },
+ // 获取单个星星的尺寸
+ getRateIconWrapRect() {
+ // uvui封装的获取节点的方法,详见文档
+ // #ifndef APP-NVUE
+ this.$uvGetRect("." + this.elClass).then((res) => {
+ this.rateWidth = res.width;
+ });
+ // #endif
+ // #ifdef APP-NVUE
+ dom.getComponentRect(this.$refs["uv-rate__content__item__icon-wrap"][0],
+ (res) => {
+ this.rateWidth = res.size.width;
+ });
+ // #endif
+ },
+ // 手指滑动
+ touchMove(e) {
+ // 如果禁止通过手动滑动选择,返回
+ if (!this.touchable) {
+ return;
+ }
+ this.preventEvent(e);
+ const x = e.changedTouches && e.changedTouches[0].pageX || e.detail && e.detail.pageX;
+ this.getActiveIndex(x);
+ },
+ // 停止滑动
+ touchEnd(e) {
+ // 如果禁止通过手动滑动选择,返回
+ if (!this.touchable) {
+ return;
+ }
+ this.preventEvent(e);
+ const x = e.changedTouches && e.changedTouches[0].pageX || e.detail && e.detail.pageX;
+ this.getActiveIndex(x);
+ },
+ // 通过点击,直接选中
+ clickHandler(e, index) {
+ // ios上,moving状态取消事件触发
+ if (this.$uv.os() === "ios" && this.moving) {
+ return;
+ }
+ this.preventEvent(e);
+ let x = 0;
+ // 点击时,在nvue上,无法获得点击的坐标,所以无法实现点击半星选择
+ // #ifndef APP-NVUE
+ x = e.changedTouches && e.changedTouches[0].pageX || e.detail && e.detail.pageX;
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue下,无法通过点击获得坐标信息,这里通过元素的位置尺寸值模拟坐标
+ x = index * this.rateWidth + this.rateBoxLeft;
+ // #endif
+ this.getActiveIndex(x, true);
+ },
+ // 发出事件
+ changeEvent() {
+ this.$emit("change", this.activeIndex);
+ this.$emit("input", this.activeIndex);
+ this.$emit("update:modelValue", this.activeIndex);
+ },
+ // 获取当前激活的评分图标
+ getActiveIndex(x, isClick = false) {
+ if (this.disabled || this.readonly) {
+ return;
+ }
+ // 判断当前操作的点的x坐标值,是否在允许的边界范围内
+ const allRateWidth = this.rateWidth * this.count + this.rateBoxLeft;
+ // 如果小于第一个图标的左边界,设置为最小值,如果大于所有图标的宽度,则设置为最大值
+ x = this.$uv.range(this.rateBoxLeft, allRateWidth, x) - this.rateBoxLeft
+ // 滑动点相对于评分盒子左边的距离
+ const distance = x;
+ // 滑动的距离,相当于多少颗星星
+ let index;
+ // 判断是否允许半星
+ if (this.allowHalf) {
+ index = Math.floor(distance / this.rateWidth);
+ // 取余,判断小数的区间范围
+ const decimal = distance % this.rateWidth;
+ if (decimal <= this.rateWidth / 2 && decimal > 0) {
+ index += 0.5;
+ } else if (decimal > this.rateWidth / 2) {
+ index++;
+ }
+ } else {
+ index = Math.floor(distance / this.rateWidth);
+ // 取余,判断小数的区间范围
+ const decimal = distance % this.rateWidth;
+ // 非半星时,只有超过了图标的一半距离,才认为是选择了这颗星
+ if (isClick) {
+ if (decimal > 0) index++;
+ } else {
+ if (decimal > this.rateWidth / 2) index++;
+ }
+ }
+ this.activeIndex = Math.min(index, this.count);
+ // 对最少颗星星的限制
+ if (this.activeIndex < this.minCount) {
+ this.activeIndex = this.minCount;
+ }
+ this.changeEvent();
+ // 设置延时为了让click事件在touchmove之前触发
+ setTimeout(() => {
+ this.moving = true;
+ }, 10);
+ // 一定时间后,取消标识为移动中状态,是为了让click事件无效
+ setTimeout(() => {
+ this.moving = false;
+ }, 10);
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-rate-margin: 0 !default;
+ $uv-rate-padding: 0 !default;
+ $uv-rate-item-icon-wrap-half-top: 0 !default;
+ $uv-rate-item-icon-wrap-half-left: 0 !default;
+ .uv-rate {
+ @include flex;
+ align-items: center;
+ margin: $uv-rate-margin;
+ padding: $uv-rate-padding;
+ /* #ifndef APP-NVUE */
+ touch-action: none;
+ /* #endif */
+ &__content {
+ @include flex;
+ &__item {
+ position: relative;
+ &__icon-wrap {
+ &--half {
+ position: absolute;
+ overflow: hidden;
+ top: $uv-rate-item-icon-wrap-half-top;
+ left: $uv-rate-item-icon-wrap-half-left;
+ }
+ }
+ }
+ }
+ }
+ .uv-icon {
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-rate/package.json b/uni_modules/uv-rate/package.json
new file mode 100644
index 0000000..ed204d8
--- /dev/null
+++ b/uni_modules/uv-rate/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-rate",
+ "displayName": "uv-rate 评分 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.7",
+ "description": "该组件为星型评分,可用于满意度调查等场景...",
+ "keywords": [
+ "uv-rate",
+ "uvui",
+ "uv-ui",
+ "rate",
+ "评分"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-rate/readme.md b/uni_modules/uv-rate/readme.md
new file mode 100644
index 0000000..ec6a034
--- /dev/null
+++ b/uni_modules/uv-rate/readme.md
@@ -0,0 +1,19 @@
+## Rate 评分
+
+> **组件名:uv-rate**
+
+该组件为星型评分,可用于满意度调查等场景。
+
+# <a href="https://www.uvui.cn/components/rate.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-read-more/changelog.md b/uni_modules/uv-read-more/changelog.md
new file mode 100644
index 0000000..890f974
--- /dev/null
+++ b/uni_modules/uv-read-more/changelog.md
@@ -0,0 +1,7 @@
+## 1.0.2(2023-09-13)
+1. 修复全局设置rpx,导致展开高度不对的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-read-more 展开阅读更多
diff --git a/uni_modules/uv-read-more/components/uv-read-more/props.js b/uni_modules/uv-read-more/components/uv-read-more/props.js
new file mode 100644
index 0000000..c691a01
--- /dev/null
+++ b/uni_modules/uv-read-more/components/uv-read-more/props.js
@@ -0,0 +1,62 @@
+export default {
+ props: {
+ // 默认的显示占位高度
+ showHeight: {
+ type: [String, Number],
+ default: 400
+ },
+ // 展开后是否显示"收起"按钮
+ toggle: {
+ type: Boolean,
+ default: false
+ },
+ // 关闭时的提示文字
+ closeText: {
+ type: String,
+ default: '展开阅读全文'
+ },
+ // 展开时的提示文字
+ openText: {
+ type: String,
+ default: '收起'
+ },
+ // 提示的文字颜色
+ color: {
+ type: String,
+ default: '#2979ff'
+ },
+ // 提示文字的大小
+ fontSize: {
+ type: [String, Number],
+ default: 14
+ },
+ // 是否显示阴影
+ // 此参数不能写在props/readMore.js中进行默认配置,因为使用了条件编译,在外部js中
+ // uni无法准确识别当前是否处于nvue还是非nvue下
+ shadowStyle: {
+ type: Object,
+ default: () => ({
+ // #ifndef APP-NVUE
+ backgroundImage: 'linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, #fff 80%)',
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue上不支持设置复杂的backgroundImage属性
+ backgroundImage: 'linear-gradient(to top, #fff, rgba(255, 255, 255, 0.5))',
+ // #endif
+ paddingTop: '100px',
+ marginTop: '-100px'
+ })
+ },
+ // 段落首行缩进的字符个数
+ textIndent: {
+ type: String,
+ default: '2em'
+ },
+ // open和close事件时,将此参数返回在回调参数中
+ name: {
+ type: [String, Number],
+ default: ''
+ },
+ ...uni.$uv?.props?.readMore
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue b/uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue
new file mode 100644
index 0000000..9ee47db
--- /dev/null
+++ b/uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue
@@ -0,0 +1,162 @@
+<template>
+ <view class="uv-read-more">
+ <view
+ class="uv-read-more__content"
+ :style="{
+ height: isLongContent && status === 'close' ? $uv.addUnit(showHeight) : $uv.addUnit(contentHeight,'px'),
+ textIndent: textIndent
+ }"
+ >
+ <view
+ class="uv-read-more__content__inner"
+ ref="uv-read-more__content__inner"
+ :class="[elId]"
+ >
+ <slot></slot>
+ </view>
+ </view>
+ <view
+ class="uv-read-more__toggle"
+ :style="[innerShadowStyle]"
+ v-if="isLongContent"
+ >
+ <slot name="toggle">
+ <view
+ class="uv-read-more__toggle__text"
+ @tap="toggleReadMore"
+ >
+ <uv-text
+ :text="status === 'close' ? closeText : openText"
+ :color="color"
+ :size="fontSize"
+ :lineHeight="fontSize"
+ margin="0 5px 0 0"
+ ></uv-text>
+ <view class="uv-read-more__toggle__icon">
+ <uv-icon
+ :color="color"
+ :size="fontSize + 2"
+ :name="status === 'close' ? 'arrow-down' : 'arrow-up'"
+ ></uv-icon>
+ </view>
+ </view>
+ </slot>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ import props from './props.js';
+ /**
+ * readMore 阅读更多
+ * @description 该组件一般用于内容较长,预先收起一部分,点击展开全部内容的场景。
+ * @tutorial https://www.uvui.cn/components/readMore.html
+ * @property {String | Number} showHeight 内容超出此高度才会显示展开全文按钮,单位px(默认 400 )
+ * @property {Boolean} toggle 展开后是否显示收起按钮(默认 false )
+ * @property {String} closeText 关闭时的提示文字(默认 '展开阅读全文' )
+ * @property {String} openText 展开时的提示文字(默认 '收起' )
+ * @property {String} color 提示文字的颜色(默认 '#2979ff' )
+ * @property {String | Number} fontSize 提示文字的大小,单位px (默认 14 )
+ * @property {Object} shadowStyle 显示阴影的样式
+ * @property {String} textIndent 段落首行缩进的字符个数 (默认 '2em' )
+ * @property {String | Number} name 用于在 open 和 close 事件中当作回调参数返回
+ * @event {Function} open 内容被展开时触发
+ * @event {Function} close 内容被收起时触发
+ * @example <uv-read-more><rich-text :nodes="content"></rich-text></uv-read-more>
+ */
+ export default {
+ name: 'uv-read-more',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ isLongContent: false, // 是否需要隐藏一部分内容
+ status: 'close', // 当前隐藏与显示的状态,close-收起状态,open-展开状态
+ elId: '', // 生成唯一class
+ contentHeight: 100, // 内容高度
+ }
+ },
+ computed: {
+ // 展开后无需阴影,收起时才需要阴影样式
+ innerShadowStyle() {
+ if (this.status === 'open') return {}
+ else return this.shadowStyle
+ }
+ },
+ created() {
+ this.elId = this.$uv.guid();
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ async init() {
+ this.getContentHeight().then(height => {
+ this.contentHeight = height
+ // 判断高度,如果真实内容高度大于占位高度,则显示收起与展开的控制按钮
+ if (height > this.$uv.getPx(this.showHeight)) {
+ this.isLongContent = true
+ this.status = 'close'
+ }
+ })
+ },
+ // 获取内容的高度
+ async getContentHeight() {
+ // 延时一定时间再获取节点
+ await this.$uv.sleep(30)
+ return new Promise(resolve => {
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.' + this.elId).then(res => {
+ resolve(res.height)
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-read-more__content__inner']
+ dom.getComponentRect(ref, (res) => {
+ resolve(res.size.height)
+ })
+ // #endif
+ })
+ },
+ // 展开或者收起
+ toggleReadMore() {
+ this.status = this.status === 'close' ? 'open' : 'close'
+ // 如果toggle为false,隐藏"收起"部分的内容
+ if (this.toggle == false) this.isLongContent = false
+ // 发出打开或者收齐的事件
+ this.$emit(this.status, this.name)
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+.uv-read-more {
+
+ &__content {
+ overflow: hidden;
+ color: $uv-content-color;
+ font-size: 15px;
+ text-align: left;
+ }
+
+ &__toggle {
+ @include flex;
+ justify-content: center;
+
+ &__text {
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ margin-top: 5px;
+ }
+ }
+}
+</style>
diff --git a/uni_modules/uv-read-more/package.json b/uni_modules/uv-read-more/package.json
new file mode 100644
index 0000000..77df674
--- /dev/null
+++ b/uni_modules/uv-read-more/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-read-more",
+ "displayName": "uv-read-more 展开阅读更多 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "uv-read-more 该组件一般用于内容较长,预先收起一部分,点击展开全部内容的场景。",
+ "keywords": [
+ "uv-read-more",
+ "uvui",
+ "uv-ui",
+ "read",
+ "展开阅读更多"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-text"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-read-more/readme.md b/uni_modules/uv-read-more/readme.md
new file mode 100644
index 0000000..79e2eae
--- /dev/null
+++ b/uni_modules/uv-read-more/readme.md
@@ -0,0 +1,11 @@
+## ReadMore 展开阅读更多
+
+> **组件名:uv-read-more**
+
+该组件一般用于内容较长,预先收起一部分,点击展开全部内容的场景。
+
+### <a href="https://www.uvui.cn/components/readMore.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-row/changelog.md b/uni_modules/uv-row/changelog.md
new file mode 100644
index 0000000..3953b4b
--- /dev/null
+++ b/uni_modules/uv-row/changelog.md
@@ -0,0 +1,5 @@
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-row layout布局
diff --git a/uni_modules/uv-row/components/uv-col/props.js b/uni_modules/uv-row/components/uv-col/props.js
new file mode 100644
index 0000000..bd9c7e0
--- /dev/null
+++ b/uni_modules/uv-row/components/uv-col/props.js
@@ -0,0 +1,30 @@
+export default {
+ props: {
+ // 占父容器宽度的多少等分,总分为12份
+ span: {
+ type: [String, Number],
+ default: 12
+ },
+ // 指定栅格左侧的间隔数(总12栏)
+ offset: {
+ type: [String, Number],
+ default: 0
+ },
+ // 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)
+ justify: {
+ type: String,
+ default: 'start'
+ },
+ // 垂直对齐方式,可选值为top、center、bottom、stretch
+ align: {
+ type: String,
+ default: 'stretch'
+ },
+ // 文字对齐方式
+ textAlign: {
+ type: String,
+ default: 'left'
+ },
+ ...uni.$uv?.props?.col
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-row/components/uv-col/uv-col.vue b/uni_modules/uv-row/components/uv-col/uv-col.vue
new file mode 100644
index 0000000..9198725
--- /dev/null
+++ b/uni_modules/uv-row/components/uv-col/uv-col.vue
@@ -0,0 +1,165 @@
+<template>
+ <view
+ class="uv-col"
+ ref="uv-col"
+ :class="[
+ 'uv-col-' + span
+ ]"
+ :style="[colStyle]"
+ @tap="clickHandler"
+ >
+ <slot></slot>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * CodeInput 栅格系统的列
+ * @description 该组件一般用于Layout 布局 通过基础的 12 分栏,迅速简便地创建布局
+ * @tutorial https://www.uvui.cn/components/Layout.html
+ * @property {String | Number} span 栅格占据的列数,总12等份 (默认 12 )
+ * @property {String | Number} offset 分栏左边偏移,计算方式与span相同 (默认 0 )
+ * @property {String} justify 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) (默认 'start' )
+ * @property {String} align 垂直对齐方式,可选值为top、center、bottom、stretch (默认 'stretch' )
+ * @property {String} textAlign 文字水平对齐方式 (默认 'left' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ * @event {Function} click col被点击,会阻止事件冒泡到row
+ * @example <uv-col span="3" offset="3" > <view class="demo-layout bg-purple"></view> </uv-col>
+ */
+ export default {
+ name: 'uv-col',
+ emits: ['click'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ width: 0,
+ parentData: {
+ gutter: 0
+ },
+ gridNum: 12
+ }
+ },
+ computed: {
+ uJustify() {
+ if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify
+ else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify
+ else return this.justify
+ },
+ uAlignItem() {
+ if (this.align == 'top') return 'flex-start'
+ if (this.align == 'bottom') return 'flex-end'
+ else return this.align
+ },
+ colStyle() {
+ const style = {
+ // 这里写成"padding: 0 10px"的形式是因为nvue的需要
+ paddingLeft: this.$uv.addUnit(this.$uv.getPx(this.parentData.gutter)/2),
+ paddingRight: this.$uv.addUnit(this.$uv.getPx(this.parentData.gutter)/2),
+ alignItems: this.uAlignItem,
+ justifyContent: this.uJustify,
+ textAlign: this.textAlign,
+ // #ifndef APP-NVUE
+ // 在非nvue上,使用百分比形式
+ flex: `0 0 ${100 / this.gridNum * this.span}%`,
+ marginLeft: 100 / 12 * this.offset + '%',
+ // #endif
+ // #ifdef APP-NVUE
+ // 在nvue上,由于无法使用百分比单位,这里需要获取父组件的宽度,再计算得出该有对应的百分比尺寸
+ width: this.$uv.addUnit(Math.floor(this.width / this.gridNum * Number(this.span))),
+ marginLeft: this.$uv.addUnit(Math.floor(this.width / this.gridNum * Number(this.offset))),
+ // #endif
+ }
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ async init() {
+ // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
+ this.updateParentData()
+ this.width = await this.parent.getComponentWidth()
+ },
+ updateParentData() {
+ this.getParentData('uv-row')
+ },
+ clickHandler(e) {
+ this.$emit('click');
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-col {
+ padding: 0;
+ /* #ifndef APP-NVUE */
+ box-sizing:border-box;
+ /* #endif */
+ /* #ifdef MP */
+ display: block;
+ /* #endif */
+ }
+
+ // nvue下百分比无效
+ /* #ifndef APP-NVUE */
+ .uv-col-0 {
+ width: 0;
+ }
+
+ .uv-col-1 {
+ width: calc(100%/12);
+ }
+
+ .uv-col-2 {
+ width: calc(100%/12 * 2);
+ }
+
+ .uv-col-3 {
+ width: calc(100%/12 * 3);
+ }
+
+ .uv-col-4 {
+ width: calc(100%/12 * 4);
+ }
+
+ .uv-col-5 {
+ width: calc(100%/12 * 5);
+ }
+
+ .uv-col-6 {
+ width: calc(100%/12 * 6);
+ }
+
+ .uv-col-7 {
+ width: calc(100%/12 * 7);
+ }
+
+ .uv-col-8 {
+ width: calc(100%/12 * 8);
+ }
+
+ .uv-col-9 {
+ width: calc(100%/12 * 9);
+ }
+
+ .uv-col-10 {
+ width: calc(100%/12 * 10);
+ }
+
+ .uv-col-11 {
+ width: calc(100%/12 * 11);
+ }
+
+ .uv-col-12 {
+ width: calc(100%/12 * 12);
+ }
+
+ /* #endif */
+</style>
diff --git a/uni_modules/uv-row/components/uv-row/props.js b/uni_modules/uv-row/components/uv-row/props.js
new file mode 100644
index 0000000..590570d
--- /dev/null
+++ b/uni_modules/uv-row/components/uv-row/props.js
@@ -0,0 +1,20 @@
+export default {
+ props: {
+ // 给col添加间距,左右边距各占一半
+ gutter: {
+ type: [String, Number],
+ default: 0
+ },
+ // 水平排列方式,可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`)
+ justify: {
+ type: String,
+ default: 'start'
+ },
+ // 垂直对齐方式,可选值为top、center、bottom
+ align: {
+ type: String,
+ default: 'center'
+ },
+ ...uni.$uv?.props?.row
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-row/components/uv-row/uv-row.vue b/uni_modules/uv-row/components/uv-row/uv-row.vue
new file mode 100644
index 0000000..78ebbe9
--- /dev/null
+++ b/uni_modules/uv-row/components/uv-row/uv-row.vue
@@ -0,0 +1,95 @@
+<template>
+ <view
+ class="uv-row"
+ ref="uv-row"
+ :style="[rowStyle]"
+ @tap="clickHandler"
+ >
+ <slot />
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ import props from './props.js';
+ /**
+ * Row 栅格系统中的行
+ * @description 通过基础的 12 分栏,迅速简便地创建布局
+ * @tutorial https://www.uvui.cn/components/layout.html
+ * @property {String | Number} gutter 栅格间隔,左右各为此值的一半,单位px (默认 0 )
+ * @property {String} justify 水平排列方式(微信小程序暂不支持) 可选值为`start`(或`flex-start`)、`end`(或`flex-end`)、`center`、`around`(或`space-around`)、`between`(或`space-between`) (默认 'start' )
+ * @property {String} align 垂直排列方式 (默认 'center' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} click row被点击
+ * @example <uv-row justify="space-between" customStyle="margin-bottom: 10px"></uv-row>
+ */
+ export default {
+ name: "uv-row",
+ emits: ['click'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+
+ }
+ },
+ computed: {
+ uJustify() {
+ if (this.justify == 'end' || this.justify == 'start') return 'flex-' + this.justify
+ else if (this.justify == 'around' || this.justify == 'between') return 'space-' + this.justify
+ else return this.justify
+ },
+ uAlignItem() {
+ if (this.align == 'top') return 'flex-start'
+ if (this.align == 'bottom') return 'flex-end'
+ else return this.align
+ },
+ rowStyle() {
+ const style = {
+ alignItems: this.uAlignItem,
+ justifyContent: this.uJustify
+ }
+ // 通过给uv-row左右两边的负外边距,消除uv-col在有gutter时,第一个和最后一个元素的左内边距和右内边距造成的影响
+ if(this.gutter) {
+ style.marginLeft = this.$uv.addUnit(-Number(this.gutter)/2)
+ style.marginRight = this.$uv.addUnit(-Number(this.gutter)/2)
+ }
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ }
+ },
+ methods: {
+ clickHandler(e) {
+ this.$emit('click')
+ },
+ async getComponentWidth() {
+ // 延时一定时间,以确保节点渲染完成
+ await this.$uv.sleep()
+ return new Promise(resolve => {
+ // uvui封装的获取节点的方法,详见文档
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-row').then(res => {
+ resolve(res.width)
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue的dom模块用于获取节点
+ dom.getComponentRect(this.$refs['uv-row'], (res) => {
+ resolve(res.size.width)
+ })
+ // #endif
+ })
+ },
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-row {
+ @include flex;
+ }
+</style>
diff --git a/uni_modules/uv-row/package.json b/uni_modules/uv-row/package.json
new file mode 100644
index 0000000..4bba4de
--- /dev/null
+++ b/uni_modules/uv-row/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-row",
+ "displayName": "uv-row layout布局 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.1",
+ "description": "uv-row 通过基础的 12 分栏,迅速简便地创建布局",
+ "keywords": [
+ "uv-row",
+ "uvui",
+ "uv-ui",
+ "layout",
+ "布局"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-row/readme.md b/uni_modules/uv-row/readme.md
new file mode 100644
index 0000000..830b7bf
--- /dev/null
+++ b/uni_modules/uv-row/readme.md
@@ -0,0 +1,11 @@
+## Layout 布局
+
+> **组件名:uv-row**
+
+通过基础的 12 分栏,迅速简便地创建布局
+
+### <a href="https://www.uvui.cn/components/layout.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-scroll-list/changelog.md b/uni_modules/uv-scroll-list/changelog.md
new file mode 100644
index 0000000..2725c3f
--- /dev/null
+++ b/uni_modules/uv-scroll-list/changelog.md
@@ -0,0 +1,12 @@
+## 1.0.5(2023-11-15)
+1. 修复微信报错的BUG
+## 1.0.4(2023-11-14)
+1. 兼容全局设置rpx
+## 1.0.3(2023-08-19)
+1. 修复报错导致不能移动指示器的BUG
+## 1.0.2(2023-08-08)
+1. 修复vue2编译报错的BUG
+## 1.0.1(2023-07-21)
+1. 优化
+## 1.0.0(2023-07-21)
+1. 新增uv-scroll-liast 横向滚动表组件
diff --git a/uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js b/uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js
new file mode 100644
index 0000000..6bfcfe7
--- /dev/null
+++ b/uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js
@@ -0,0 +1,29 @@
+// 引入bindingx,此库类似于微信小程序wxs,目的是让js运行在视图层,减少视图层和逻辑层的通信折损
+const BindingX = uni.requireNativePlugin('bindingx')
+export default {
+ methods: {
+ // 此处不写注释,请自行体会
+ nvueScrollHandler(e) {
+ if (this.indicator) {
+ const anchor = this.$refs['uv-scroll-list__scroll-view'].ref
+ const element = this.$refs['uv-scroll-list__indicator__line__bar'].ref
+ const scrollLeft = e.contentOffset.x
+ const contentSize = e.contentSize.width
+ const { scrollWidth } = this
+ const barAllMoveWidth = this.indicatorWidth - this.indicatorBarWidth
+ // 在安卓和iOS上,需要除的倍数不一样,iOS需要除以2
+ const actionNum = this.$uv.os() === 'ios' ? 2 : 1
+ const expression = `(x / ${actionNum}) / ${contentSize - scrollWidth} * ${this.$uv.getPx(this.$uv.addUnit(barAllMoveWidth))}`
+ BindingX.bind({
+ anchor,
+ eventType: 'scroll',
+ props: [{
+ element,
+ property: 'transform.translateX',
+ expression
+ }]
+ })
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-scroll-list/components/uv-scroll-list/props.js b/uni_modules/uv-scroll-list/components/uv-scroll-list/props.js
new file mode 100644
index 0000000..b38637e
--- /dev/null
+++ b/uni_modules/uv-scroll-list/components/uv-scroll-list/props.js
@@ -0,0 +1,35 @@
+export default {
+ props: {
+ // 指示器的整体宽度
+ indicatorWidth: {
+ type: [String, Number],
+ default: 50
+ },
+ // 滑块的宽度
+ indicatorBarWidth: {
+ type: [String, Number],
+ default: 20
+ },
+ // 是否显示面板指示器
+ indicator: {
+ type: Boolean,
+ default: true
+ },
+ // 指示器非激活颜色
+ indicatorColor: {
+ type: String,
+ default: '#f2f2f2'
+ },
+ // 指示器的激活颜色
+ indicatorActiveColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 指示器样式,可通过bottom,left,right进行定位
+ indicatorStyle: {
+ type: [String, Object],
+ default: ''
+ },
+ ...uni.$uv?.props?.scrollList
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs b/uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs
new file mode 100644
index 0000000..7aa7f1e
--- /dev/null
+++ b/uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs
@@ -0,0 +1,51 @@
+function scroll(event, ownerInstance) {
+ // detail中含有scroll-view的信息,比如scroll-view的实际宽度,当前时间点scroll-view的移动距离等
+ var detail = event.detail
+ var scrollWidth = detail.scrollWidth
+ var scrollLeft = detail.scrollLeft
+ // 获取当前组件的dataset,说白了就是祸国殃民的腾xun搞出来的垃ji
+ var dataset = event.currentTarget.dataset
+ // 此为scroll-view外部包裹元素的宽度
+ // 某些HX版本(3.1.18),发现view元素中大写的data-scrollWidth,在wxs中,变成了全部小写,所以这里需要特别处理
+ var scrollComponentWidth = dataset.scrollWidth || dataset.scrollwidth || 0
+ // 指示器和滑块的宽度
+ var indicatorWidth = dataset.indicatorWidth || dataset.indicatorwidth || 0
+ var barWidth = dataset.barWidth || dataset.barwidth || 0
+ // 此处的计算理由为:scroll-view的滚动距离与目标滚动距离(scroll-view的实际宽度减去包裹元素的宽度)之比,等于滑块当前移动距离与总需
+ // 滑动距离(指示器的总宽度减去滑块宽度)的比值
+ var x = scrollLeft / (scrollWidth - scrollComponentWidth) * (indicatorWidth - barWidth)
+ setBarStyle(ownerInstance, x, dataset.unit)
+}
+
+// 由于webview的无能,无法保证scroll-view在滑动过程中,一直触发scroll事件,会导致
+// 无法监听到某些滚动值,当在首尾临界值无法监听到时,这是致命的,因为错失这些值会导致滑块无法回到起点和终点
+// 所以这里需要对临界值做监听并处理
+function scrolltolower(event, ownerInstance) {
+ ownerInstance.callMethod('scrollEvent', 'right')
+ // 获取当前组件的dataset
+ var dataset = event.currentTarget.dataset
+ // 指示器和滑块的宽度
+ var indicatorWidth = dataset.indicatorWidth || dataset.indicatorwidth || 0
+ var barWidth = dataset.barWidth || dataset.barwidth || 0
+ // scroll-view滚动到右边终点时,将滑块也设置为到右边的终点,它所需移动的距离为:指示器宽度 - 滑块宽度
+ setBarStyle(ownerInstance, indicatorWidth - barWidth, dataset.unit)
+}
+
+function scrolltoupper(event, ownerInstance) {
+ var dataset = event.currentTarget.dataset
+ ownerInstance.callMethod('scrollEvent', 'left')
+ // 滚动到左边时,将滑块设置为0的偏移距离,回到起点
+ setBarStyle(ownerInstance, 0, dataset.unit)
+}
+
+function setBarStyle(ownerInstance, x, unit) {
+ ownerInstance.selectComponent('.uv-scroll-list__indicator__line__bar') && ownerInstance.selectComponent('.uv-scroll-list__indicator__line__bar').setStyle({
+ transform: unit == 'rpx' ? 'translateX(' + x + 'rpx)' : 'translateX(' + x + 'px)'
+ })
+}
+
+module.exports = {
+ scroll: scroll,
+ scrolltolower: scrolltolower,
+ scrolltoupper: scrolltoupper
+}
\ No newline at end of file
diff --git a/uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue b/uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue
new file mode 100644
index 0000000..1e051c5
--- /dev/null
+++ b/uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue
@@ -0,0 +1,218 @@
+<template>
+ <view
+ class="uv-scroll-list"
+ ref="uv-scroll-list"
+ >
+ <!-- #ifdef APP-NVUE -->
+ <!-- nvue使用bindingX实现,以得到更好的性能 -->
+ <scroller
+ class="uv-scroll-list__scroll-view"
+ ref="uv-scroll-list__scroll-view"
+ scroll-direction="horizontal"
+ :show-scrollbar="false"
+ :offset-accuracy="1"
+ @scroll="nvueScrollHandler"
+ >
+ <view class="uv-scroll-list__scroll-view__content">
+ <slot />
+ </view>
+ </scroller>
+ <!-- #endif -->
+ <!-- #ifndef APP-NVUE -->
+ <!-- #ifdef MP-WEIXIN || APP-VUE || H5 || MP-QQ -->
+ <!-- 以上平台,支持wxs -->
+ <scroll-view
+ class="uv-scroll-list__scroll-view"
+ scroll-x
+ @scroll="wxs.scroll"
+ @scrolltoupper="wxs.scrolltoupper"
+ @scrolltolower="wxs.scrolltolower"
+ :data-scrollWidth="scrollWidth"
+ :data-barWidth="$uv.getPx(indicatorBarWidth)"
+ :data-indicatorWidth="$uv.getPx(indicatorWidth)"
+ :show-scrollbar="false"
+ :upper-threshold="0"
+ :lower-threshold="0"
+ :data-unit="$uv.unit"
+ >
+ <!-- #endif -->
+ <!-- #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ -->
+ <!-- 非以上平台,只能使用普通js实现 -->
+ <scroll-view
+ class="uv-scroll-list__scroll-view"
+ scroll-x
+ @scroll="scrollHandler"
+ @scrolltoupper="scrolltoupperHandler"
+ @scrolltolower="scrolltolowerHandler"
+ :show-scrollbar="false"
+ :upper-threshold="0"
+ :lower-threshold="0"
+ >
+ <!-- #endif -->
+ <view class="uv-scroll-list__scroll-view__content">
+ <slot />
+ </view>
+ </scroll-view>
+ <!-- #endif -->
+ <view
+ class="uv-scroll-list__indicator"
+ v-if="indicator"
+ :style="[$uv.addStyle(indicatorStyle)]"
+ >
+ <view
+ class="uv-scroll-list__indicator__line"
+ :style="[lineStyle]"
+ >
+ <view
+ class="uv-scroll-list__indicator__line__bar"
+ :style="[barStyle]"
+ ref="uv-scroll-list__indicator__line__bar"
+ ></view>
+ </view>
+ </view>
+ <!-- 避免报错 -->
+ <!-- #ifdef H5 -->
+ <view v-else class="uv-scroll-list__indicator__line__bar"></view>
+ <!-- #endif -->
+ </view>
+</template>
+<script src="./scrollWxs.wxs" module="wxs" lang="wxs"></script>
+<script>
+ /**
+ * scrollList 横向滚动列表
+ * @description 该组件一般用于同时展示多个商品、分类的场景,也可以完成左右滑动的列表。
+ * @tutorial https://www.uviewui.com/components/scrollList.html
+ * @property {String | Number} indicatorWidth 指示器的整体宽度 (默认 50 )
+ * @property {String | Number} indicatorBarWidth 滑块的宽度 (默认 20 )
+ * @property {Boolean} indicator 是否显示面板指示器 (默认 true )
+ * @property {String} indicatorColor 指示器非激活颜色 (默认 '#f2f2f2' )
+ * @property {String} indicatorActiveColor 指示器的激活颜色 (默认 '#3c9cff' )
+ * @property {String | Object} indicatorStyle 指示器样式,可通过bottom,left,right进行定位
+ * @event {Function} left 滑动到左边时触发
+ * @event {Function} right 滑动到右边时触发
+ * @example
+ */
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ import nvueMixin from "./nvue.js"
+ // #endif
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ export default {
+ name: 'uv-scroll-list',
+ // #ifndef APP-NVUE
+ mixins: [mpMixin, mixin, props],
+ // #endif
+ // #ifdef APP-NVUE
+ mixins: [mpMixin, mixin, nvueMixin, props],
+ // #endif
+ data() {
+ return {
+ scrollInfo: {
+ scrollLeft: 0,
+ scrollWidth: 0
+ },
+ scrollWidth: 0
+ }
+ },
+ computed: {
+ // 指示器为线型的样式
+ barStyle() {
+ const style = {}
+ // #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ
+ // 此为普通js方案,只有在非nvue和不支持wxs方案的端才使用、
+ // 此处的计算理由为:scroll-view的滚动距离与目标滚动距离(scroll-view的实际宽度减去包裹元素的宽度)之比,等于滑块当前移动距离与总需
+ // 滑动距离(指示器的总宽度减去滑块宽度)的比值
+ const scrollLeft = this.scrollInfo.scrollLeft,
+ scrollWidth = this.scrollInfo.scrollWidth,
+ barAllMoveWidth = this.indicatorWidth - this.indicatorBarWidth
+ const x = scrollLeft / (scrollWidth - this.scrollWidth) * this.$uv.getPx(this.$uv.addUnit(barAllMoveWidth))
+ style.transform = `translateX(${ x }px)`
+ // #endif
+ // 设置滑块的宽度和背景色,是每个平台都需要的
+ style.width = this.$uv.addUnit(this.indicatorBarWidth)
+ style.backgroundColor = this.indicatorActiveColor
+ return style
+ },
+ lineStyle() {
+ const style = {}
+ // 指示器整体的样式,需要设置其宽度和背景色
+ style.width = this.$uv.addUnit(this.indicatorWidth)
+ style.backgroundColor = this.indicatorColor
+ return style
+ }
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.getComponentWidth()
+ },
+ // #ifndef APP-NVUE || MP-WEIXIN || H5 || APP-VUE || MP-QQ
+ // scroll-view触发滚动事件
+ scrollHandler(e) {
+ this.scrollInfo = e.detail
+ },
+ scrolltoupperHandler() {
+ this.scrollEvent('left')
+ this.scrollInfo.scrollLeft = 0
+ },
+ scrolltolowerHandler() {
+ this.scrollEvent('right')
+ // 在普通js方案中,滚动到右边时,通过设置this.scrollInfo,模拟出滚动到右边的情况
+ // 因为上方是用过computed计算的,设置后,会自动调整滑块的位置
+ this.scrollInfo.scrollLeft = this.$uv.getPx(this.indicatorWidth) - this.$uv.getPx(this.indicatorBarWidth)
+ },
+ // #endif
+ scrollEvent(status) {
+ this.$emit(status)
+ },
+ // 获取组件的宽度
+ async getComponentWidth() {
+ // 延时一定时间,以获取dom尺寸
+ await this.$uv.sleep(30)
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-scroll-list').then(size => {
+ this.scrollWidth = size.width
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-scroll-list']
+ ref && dom.getComponentRect(ref, (res) => {
+ this.scrollWidth = res.size.width
+ })
+ // #endif
+ },
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-scroll-list {
+ padding-bottom: 10px;
+ &__scroll-view {
+ @include flex;
+ &__content {
+ @include flex;
+ }
+ }
+ &__indicator {
+ @include flex;
+ justify-content: center;
+ margin-top: 15px;
+ &__line {
+ width: 60px;
+ height: 4px;
+ border-radius: 100px;
+ overflow: hidden;
+ &__bar {
+ width: 20px;
+ height: 4px;
+ border-radius: 100px;
+ }
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-scroll-list/package.json b/uni_modules/uv-scroll-list/package.json
new file mode 100644
index 0000000..942a836
--- /dev/null
+++ b/uni_modules/uv-scroll-list/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-scroll-list",
+ "displayName": "uv-scroll-list 横向滚动列表 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "该组件一般用于同时展示多个商品、分类的场景,也可以完成左右滑动的列表,往往数量不确定,数量较多支持左右滚动。灵活配置,开箱即用。",
+ "keywords": [
+ "uv-scroll-list",
+ "uvui",
+ "uv-ui",
+ "横向滚动列表",
+ "scroll-view"
+ ],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-scroll-list/readme.md b/uni_modules/uv-scroll-list/readme.md
new file mode 100644
index 0000000..fdf60e4
--- /dev/null
+++ b/uni_modules/uv-scroll-list/readme.md
@@ -0,0 +1,19 @@
+## ScrollList 横向滚动列表
+
+> **组件名:uv-scroll-list**
+
+该组件一般用于同时展示多个商品、分类的场景,也可以完成左右滑动的列表,往往数量不确定,数量较多支持左右滚动。灵活配置,开箱即用。
+
+# <a href="https://www.uvui.cn/components/scrollList.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-search/changelog.md b/uni_modules/uv-search/changelog.md
new file mode 100644
index 0000000..6519601
--- /dev/null
+++ b/uni_modules/uv-search/changelog.md
@@ -0,0 +1,25 @@
+## 1.0.11(2023-11-10)
+1. 调整清除按钮样式的marginLeft,避免微信上多数情况触发不了的BUG
+## 1.0.10(2023-11-04)
+1. 修复设置disabled情况下,@click回调事件触发不了的bug
+## 1.0.9(2023-08-18)
+1. 修复boxStyle失效的BUG
+## 1.0.8(2023-08-07)
+修复值为null或undefined时显示错误的bug
+## 1.0.7(2023-08-05)
+1. 修复在vue2模式下,v-model设置为0时不生效的BUG
+## 1.0.6(2023-07-26)
+1. 增加boxStyle参数,方便控制输入框部分的样式
+## 1.0.5(2023-07-26)
+1. 增加prefix和suffix 前置和后置插槽
+## 1.0.4(2023-07-13)
+1. 修复value值清空不生效的BUG
+## 1.0.3(2023-07-13)
+1. 修复value/v-model更改不生效的BUG
+## 1.0.2(2023-07-03)
+去除无用的插槽
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-search 搜索
diff --git a/uni_modules/uv-search/components/uv-search/props.js b/uni_modules/uv-search/components/uv-search/props.js
new file mode 100644
index 0000000..338d99c
--- /dev/null
+++ b/uni_modules/uv-search/components/uv-search/props.js
@@ -0,0 +1,127 @@
+export default {
+ props: {
+ value: {
+ type: [String, Number],
+ default: ''
+ },
+ modelValue: {
+ type: [String, Number],
+ default: ''
+ },
+ // 搜索框形状,round-圆形,square-方形
+ shape: {
+ type: String,
+ default: 'round'
+ },
+ // 搜索框背景色,默认值#f2f2f2
+ bgColor: {
+ type: String,
+ default: '#f2f2f2'
+ },
+ // 占位提示文字
+ placeholder: {
+ type: String,
+ default: '请输入关键字'
+ },
+ // 是否启用清除控件
+ clearabled: {
+ type: Boolean,
+ default: true
+ },
+ // 是否自动聚焦
+ focus: {
+ type: Boolean,
+ default: false
+ },
+ // 是否在搜索框右侧显示取消按钮
+ showAction: {
+ type: Boolean,
+ default: true
+ },
+ // 右边控件的样式
+ actionStyle: {
+ type: Object,
+ default: () => ({})
+ },
+ // 取消按钮文字
+ actionText: {
+ type: String,
+ default: '搜索'
+ },
+ // 输入框内容对齐方式,可选值为 left|center|right
+ inputAlign: {
+ type: String,
+ default: 'left'
+ },
+ // input输入框的样式,可以定义文字颜色,大小等,对象形式
+ inputStyle: {
+ type: Object,
+ default: () => ({})
+ },
+ // 是否禁用输入框
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 边框颜色
+ borderColor: {
+ type: String,
+ default: 'transparent'
+ },
+ // 搜索图标的颜色,默认同输入框字体颜色
+ searchIconColor: {
+ type: String,
+ default: '#909399'
+ },
+ // 输入框字体颜色
+ color: {
+ type: String,
+ default: '#606266'
+ },
+ // placeholder的颜色
+ placeholderColor: {
+ type: String,
+ default: '#909399'
+ },
+ // 左边输入框的图标,可以为uvui图标名称或图片路径
+ searchIcon: {
+ type: String,
+ default: 'search'
+ },
+ searchIconSize: {
+ type: [Number, String],
+ default: 22
+ },
+ // 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30px"、"30px 20px"等写法
+ margin: {
+ type: String,
+ default: '0'
+ },
+ // 开启showAction时,是否在input获取焦点时才显示
+ animation: {
+ type: Boolean,
+ default: false
+ },
+ // 输入框最大能输入的长度,-1为不限制长度(来自uniapp文档)
+ maxlength: {
+ type: [String, Number],
+ default: -1
+ },
+ // 搜索框高度,单位px
+ height: {
+ type: [String, Number],
+ default: 32
+ },
+ // 搜索框左侧文本
+ label: {
+ type: [String, Number, null],
+ default: null
+ },
+ // 搜索框扩展样式
+ boxStyle: {
+ type: [String, Object],
+ default: () => ({})
+ },
+ ...uni.$uv?.props?.search
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-search/components/uv-search/uv-search.vue b/uni_modules/uv-search/components/uv-search/uv-search.vue
new file mode 100644
index 0000000..216f4e1
--- /dev/null
+++ b/uni_modules/uv-search/components/uv-search/uv-search.vue
@@ -0,0 +1,311 @@
+<template>
+ <view
+ class="uv-search"
+ @tap="clickHandler"
+ :style="[{
+ margin: margin,
+ }, $uv.addStyle(customStyle)]"
+ >
+ <view
+ class="uv-search__content"
+ :style="[{
+ backgroundColor: bgColor,
+ borderRadius: shape == 'round' ? '100px' : '4px',
+ borderColor: borderColor,
+ },$uv.addStyle(boxStyle)]"
+ >
+ <view class="uv-search__content__disabled" v-if="disabled"></view>
+ <slot name="prefix">
+ <view class="uv-search__content__icon">
+ <uv-icon
+ @tap="clickIcon"
+ :size="searchIconSize"
+ :name="searchIcon"
+ :color="searchIconColor ? searchIconColor : color"
+ ></uv-icon>
+ </view>
+ </slot>
+ <input
+ confirm-type="search"
+ @blur="blur"
+ :value="keyword"
+ @confirm="search"
+ @input="inputChange"
+ :disabled="disabled"
+ @focus="getFocus"
+ :focus="focus"
+ :maxlength="maxlength"
+ placeholder-class="uv-search__content__input--placeholder"
+ :placeholder="placeholder"
+ :placeholder-style="`color: ${placeholderColor}`"
+ class="uv-search__content__input"
+ type="text"
+ :style="[{
+ textAlign: inputAlign,
+ color: color,
+ backgroundColor: bgColor,
+ height: $uv.addUnit(height)
+ }, inputStyle]"
+ />
+ <view
+ class="uv-search__content__icon uv-search__content__close"
+ v-if="keyword && clearabled && focused"
+ @tap="clear"
+ >
+ <uv-icon
+ name="close"
+ size="11"
+ color="#ffffff"
+ customStyle="line-height: 12px"
+ ></uv-icon>
+ </view>
+ <slot name="suffix"></slot>
+ </view>
+ <text
+ :style="[actionStyle]"
+ class="uv-search__action"
+ :class="[(showActionBtn || show) && 'uv-search__action--active']"
+ @tap.stop.prevent="custom"
+ >{{ actionText }}</text>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * search 搜索框
+ * @description 搜索组件,集成了常见搜索框所需功能,用户可以一键引入,开箱即用。
+ * @tutorial https://www.uvui.cn/components/search.html
+ * @property {String} value/v-model 输入框初始值
+ * @property {String} shape 搜索框形状,round-圆形,square-方形(默认 'round' )
+ * @property {String} bgColor 搜索框背景颜色(默认 '#f2f2f2' )
+ * @property {String} placeholder 占位文字内容(默认 '请输入关键字' )
+ * @property {Boolean} clearabled 是否启用清除控件(默认 true )
+ * @property {Boolean} focus 是否自动获得焦点(默认 false )
+ * @property {Boolean} showAction 是否显示右侧控件(默认 true )
+ * @property {Object} actionStyle 右侧控件的样式,对象形式
+ * @property {String} actionText 右侧控件文字(默认 '搜索' )
+ * @property {String} inputAlign 输入框内容水平对齐方式 (默认 'left' )
+ * @property {Object} inputStyle 自定义输入框样式,对象形式
+ * @property {Boolean} disabled 是否启用输入框(默认 false )
+ * @property {String} borderColor 边框颜色,配置了颜色,才会有边框 (默认 'transparent' )
+ * @property {String} searchIconColor 搜索图标的颜色,默认同输入框字体颜色 (默认 '#909399' )
+ * @property {Number | String} searchIconSize 搜索图标的字体,默认22
+ * @property {String} color 输入框字体颜色(默认 '#606266' )
+ * @property {String} placeholderColor placeholder的颜色(默认 '#909399' )
+ * @property {String} searchIcon 输入框左边的图标,可以为uvui图标名称或图片路径 (默认 'search' )
+ * @property {String} margin 组件与其他上下左右元素之间的距离,带单位的字符串形式,如"30px" (默认 '0' )
+ * @property {Boolean} animation 是否开启动画,见上方说明(默认 false )
+ * @property {String | Number} maxlength 输入框最大能输入的长度,-1为不限制长度 (默认 '-1' )
+ * @property {String | Number} height 输入框高度,单位px(默认 64 )
+ * @property {String | Number} label 搜索框左边显示内容
+ * @property {Object} boxStyle 自定义内容框样式,设置padding
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} change 输入框内容发生变化时触发
+ * @event {Function} search 用户确定搜索时触发,用户按回车键,或者手机键盘右下角的"搜索"键时触发
+ * @event {Function} custom 用户点击右侧控件时触发
+ * @event {Function} clear 用户点击清除按钮时触发
+ * @example <uv-search placeholder="日照香炉生紫烟" v-model="keyword"></uv-search>
+ */
+ export default {
+ name: "uv-search",
+ emits: ['click', 'input', 'change', 'clear', 'search', 'custom', 'focus', 'blur', 'clickIcon', 'update:modelValue'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ keyword: '',
+ showClear: false, // 是否显示右边的清除图标
+ show: false,
+ // 标记input当前状态是否处于聚焦中,如果是,才会显示右侧的清除控件
+ focused: this.focus
+ };
+ },
+ created() {
+ // #ifndef VUE3
+ this.keyword = this.value;
+ // #endif
+ // #ifdef VUE3
+ this.keyword = this.modelValue;
+ // #endif
+ },
+ watch: {
+ value(nVal){
+ this.keyword = nVal;
+ },
+ modelValue(nVal){
+ this.keyword = nVal;
+ }
+ },
+ computed: {
+ showActionBtn() {
+ return !this.animation && this.showAction
+ }
+ },
+ methods: {
+ keywordChange(){
+ this.$emit('input', this.keyword)
+ this.$emit('update:modelValue', this.keyword)
+ this.$emit('change', this.keyword);
+ },
+ // 目前HX2.6.9 v-model双向绑定无效,故监听input事件获取输入框内容的变化
+ inputChange(e) {
+ this.keyword = e.detail.value;
+ this.keywordChange();
+ },
+ // 清空输入
+ // 也可以作为用户通过this.$refs形式调用清空输入框内容
+ clear() {
+ this.keyword = '';
+ this.$nextTick(() => {
+ this.$emit('clear');
+ })
+ this.keywordChange();
+ },
+ // 确定搜索
+ search(e) {
+ this.$emit('search', e.detail.value);
+ try {
+ // 收起键盘
+ uni.hideKeyboard();
+ } catch (e) {}
+ },
+ // 点击右边自定义按钮的事件
+ custom() {
+ this.$emit('custom', this.keyword);
+ try {
+ // 收起键盘
+ uni.hideKeyboard();
+ } catch (e) {}
+ },
+ // 获取焦点
+ getFocus() {
+ this.focused = true;
+ // 开启右侧搜索按钮展开的动画效果
+ if (this.animation && this.showAction) this.show = true;
+ this.$emit('focus', this.keyword);
+ },
+ // 失去焦点
+ blur() {
+ // 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错
+ // 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时
+ setTimeout(() => {
+ this.focused = false;
+ }, 100)
+ this.show = false;
+ this.$emit('blur', this.keyword);
+ },
+ // 点击搜索框,只有disabled=true时才发出事件,因为禁止了输入,意味着是想跳转真正的搜索页
+ clickHandler() {
+ if (this.disabled) this.$emit('click');
+ },
+ // 点击左边图标
+ clickIcon() {
+ this.$emit('clickIcon');
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-search-content-padding: 0 10px !default;
+ $uv-search-label-color: $uv-main-color !default;
+ $uv-search-label-font-size: 14px !default;
+ $uv-search-label-margin: 0 4px !default;
+ $uv-search-close-size: 20px !default;
+ $uv-search-close-radius: 100px !default;
+ $uv-search-close-bgColor: #C6C7CB !default;
+ $uv-search-close-transform: scale(0.82) !default;
+ $uv-search-input-font-size: 14px !default;
+ $uv-search-input-margin: 0 15px 0 5px !default;
+ $uv-search-input-color: $uv-main-color !default;
+ $uv-search-input-placeholder-color: $uv-tips-color !default;
+ $uv-search-action-font-size: 14px !default;
+ $uv-search-action-color: $uv-main-color !default;
+ $uv-search-action-width: 0 !default;
+ $uv-search-action-active-width: 40px !default;
+ $uv-search-action-margin-left: 5px !default;
+ /* #ifdef H5 */
+ // iOS15在H5下,hx的某些版本,input type=search时,会多了一个搜索图标,进行移除
+ [type="search"]::-webkit-search-decoration {
+ display: none;
+ }
+ /* #endif */
+ .uv-search {
+ @include flex(row);
+ align-items: center;
+ flex: 1;
+ &__content {
+ position: relative;
+ @include flex;
+ align-items: center;
+ padding: $uv-search-content-padding;
+ flex: 1;
+ justify-content: space-between;
+ border-width: 1px;
+ border-color: transparent;
+ border-style: solid;
+ overflow: hidden;
+ &__icon {
+ @include flex;
+ align-items: center;
+ }
+ &__label {
+ color: $uv-search-label-color;
+ font-size: $uv-search-label-font-size;
+ margin: $uv-search-label-margin;
+ }
+ &__close {
+ width: $uv-search-close-size;
+ height: $uv-search-close-size;
+ border-top-left-radius: $uv-search-close-radius;
+ border-top-right-radius: $uv-search-close-radius;
+ border-bottom-left-radius: $uv-search-close-radius;
+ border-bottom-right-radius: $uv-search-close-radius;
+ background-color: $uv-search-close-bgColor;
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+ transform: $uv-search-close-transform;
+ }
+ &__input {
+ flex: 1;
+ font-size: $uv-search-input-font-size;
+ line-height: 1;
+ margin: $uv-search-input-margin;
+ color: $uv-search-input-color;
+ &--placeholder {
+ color: $uv-search-input-placeholder-color;
+ }
+ }
+ &__disabled {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ z-index: 2;
+ }
+ }
+ &__action {
+ font-size: $uv-search-action-font-size;
+ color: $uv-search-action-color;
+ width: $uv-search-action-width;
+ overflow: hidden;
+ transition-property: width;
+ transition-duration: 0.3s;
+ /* #ifndef APP-NVUE */
+ white-space: nowrap;
+ /* #endif */
+ text-align: center;
+ &--active {
+ width: $uv-search-action-active-width;
+ margin-left: $uv-search-action-margin-left;
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-search/package.json b/uni_modules/uv-search/package.json
new file mode 100644
index 0000000..3709059
--- /dev/null
+++ b/uni_modules/uv-search/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-search",
+ "displayName": "uv-search 搜索 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.11",
+ "description": "搜索组件,集成了常见搜索框所需功能,支持前后插槽,用户可以一键引入,开箱即用",
+ "keywords": [
+ "uv-search",
+ "uvui",
+ "uv-ui",
+ "search",
+ "搜索"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-loading-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-search/readme.md b/uni_modules/uv-search/readme.md
new file mode 100644
index 0000000..1e85b27
--- /dev/null
+++ b/uni_modules/uv-search/readme.md
@@ -0,0 +1,19 @@
+## Search 搜索
+
+> **组件名:uv-search**
+
+搜索组件,集成了常见搜索框所需功能,支持前后插槽,用户可以一键引入,开箱即用。
+
+# <a href="https://www.uvui.cn/components/search.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <span>(请不要 下载插件ZIP)</span>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-skeleton/changelog.md b/uni_modules/uv-skeleton/changelog.md
new file mode 100644
index 0000000..c5c0d78
--- /dev/null
+++ b/uni_modules/uv-skeleton/changelog.md
@@ -0,0 +1,9 @@
+## 1.0.2(2023-11-02)
+1. **`uv-skeleton`不再维护,强烈推荐使用新版本`uv-skeletons`**
+2. 推荐大家使用新版骨架屏,作者已经运用到大量实战中了,绝对正品:[https://ext.dcloud.net.cn/plugin?name=uv-skeletons](https://ext.dcloud.net.cn/plugin?name=uv-skeletons)
+3. 新版本`uv-skeletons`的优点:灵活度非常高,直接配置数组就可以完成想要的布局,作者已经运用到大量实战中。鼓励老用户迁移新版本,有任何问题可以加Q群549833913交流。
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-skeleton 骨架屏
diff --git a/uni_modules/uv-skeleton/components/uv-skeleton/props.js b/uni_modules/uv-skeleton/components/uv-skeleton/props.js
new file mode 100644
index 0000000..127e9dd
--- /dev/null
+++ b/uni_modules/uv-skeleton/components/uv-skeleton/props.js
@@ -0,0 +1,65 @@
+export default {
+ props: {
+ // 是否展示骨架组件
+ loading: {
+ type: Boolean,
+ default: true
+ },
+ // 是否开启动画效果
+ animate: {
+ type: Boolean,
+ default: true
+ },
+ // 段落占位图行数
+ rows: {
+ type: [String, Number],
+ default: 0
+ },
+ // 段落占位图的宽度
+ rowsWidth: {
+ type: [String, Number, Array],
+ default: '100%'
+ },
+ // 段落占位图的高度
+ rowsHeight: {
+ type: [String, Number, Array],
+ default: 18
+ },
+ // 段落占位图的左边距
+ rowsLeft: {
+ type: [String, Number, Array],
+ default: 0
+ },
+ // 是否展示标题占位图
+ title: {
+ type: Boolean,
+ default: true
+ },
+ // 段落标题的宽度
+ titleWidth: {
+ type: [String, Number],
+ default: '50%'
+ },
+ // 段落标题的高度
+ titleHeight: {
+ type: [String, Number],
+ default: 18
+ },
+ // 是否展示头像占位图
+ avatar: {
+ type: Boolean,
+ default: false
+ },
+ // 头像占位图大小
+ avatarSize: {
+ type: [String, Number],
+ default: 32
+ },
+ // 头像占位图的形状,circle-圆形,square-方形
+ avatarShape: {
+ type: String,
+ default: 'circle'
+ },
+ ...uni.$uv?.props?.skeleton
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue b/uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue
new file mode 100644
index 0000000..2184b0e
--- /dev/null
+++ b/uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue
@@ -0,0 +1,250 @@
+<template>
+ <view class="uv-skeleton">
+ <view
+ class="uv-skeleton__wrapper"
+ ref="uv-skeleton__wrapper"
+ v-if="loading"
+ style="display: flex; flex-direction: row;"
+ >
+ <view
+ class="uv-skeleton__wrapper__avatar"
+ v-if="avatar"
+ :class="[`uv-skeleton__wrapper__avatar--${avatarShape}`, animate && 'animate']"
+ :style="{
+ height: $uv.addUnit(avatarSize),
+ width: $uv.addUnit(avatarSize)
+ }"
+ ></view>
+ <view
+ class="uv-skeleton__wrapper__content"
+ ref="uv-skeleton__wrapper__content"
+ style="flex: 1;"
+ >
+ <view
+ class="uv-skeleton__wrapper__content__title"
+ v-if="title"
+ :style="{
+ width: uTitleWidth,
+ height: $uv.addUnit(titleHeight),
+ }"
+ :class="[animate && 'animate']"
+ ></view>
+ <view
+ class="uv-skeleton__wrapper__content__rows"
+ :class="[animate && 'animate']"
+ v-for="(item, index) in rowsArray"
+ :key="index"
+ :style="{
+ width: item.width,
+ height: item.height,
+ marginTop: item.marginTop,
+ marginLeft: item.marginLeft
+ }"
+ >
+
+ </view>
+ </view>
+ </view>
+ <slot v-else />
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ // 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+ const dom = uni.requireNativePlugin('dom')
+ const animation = uni.requireNativePlugin('animation')
+ // #endif
+ /**
+ * Skeleton 骨架屏
+ * @description 骨架屏一般用于页面在请求远程数据尚未完成时,页面用灰色块预显示本来的页面结构,给用户更好的体验。
+ * @tutorial https://www.uvui.cn/components/skeleton.html
+ * @property {Boolean} loading 是否显示骨架占位图,设置为false将会展示子组件内容 (默认 true )
+ * @property {Boolean} animate 是否开启动画效果 (默认 true )
+ * @property {String | Number} rows 段落占位图行数 (默认 0 )
+ * @property {String | Number | Array} rowsWidth 段落占位图的宽度,可以为百分比,数值,带单位字符串等,可通过数组传入指定每个段落行的宽度 (默认 '100%' )
+ * @property {String | Number | Array} rowsHeight 段落的高度 (默认 18 )
+ * @property {String | Number | Array} rowsLeft 段落的左边距 (默认 0 )
+ * @property {Boolean} title 是否展示标题占位图 (默认 true )
+ * @property {String | Number} titleWidth 标题的宽度 (默认 '50%' )
+ * @property {String | Number} titleHeight 标题的高度 (默认 18 )
+ * @property {Boolean} avatar 是否展示头像占位图 (默认 false )
+ * @property {String | Number} avatarSize 头像占位图大小 (默认 32 )
+ * @property {String} avatarShape 头像占位图的形状,circle-圆形,square-方形 (默认 'circle' )
+ * @example <uv-skeleton :loading="true" :animate="true"></uv-skeleton>
+ */
+ export default {
+ name: 'uv-skeleton',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ width: 0,
+ }
+ },
+ watch: {
+ loading() {
+ this.getComponentWidth()
+ }
+ },
+ computed: {
+ rowsArray() {
+ if (/%$/.test(this.rowsHeight)) {
+ this.$uv.error('rowsHeight参数不支持百分比单位')
+ }
+ const rows = []
+ for (let i = 0; i < this.rows; i++) {
+ let item = {},
+ // 需要预防超出数组边界的情况
+ rowWidth = this.$uv.test.array(this.rowsWidth) ? (this.rowsWidth[i] || (i === this.row - 1 ? '70%' : '100%')) : i ===
+ this.rows - 1 ? '70%' : this.rowsWidth,
+ rowHeight = this.$uv.test.array(this.rowsHeight) ? (this.rowsHeight[i] || '18px') : this.rowsHeight,
+ rowLeft = this.$uv.test.array(this.rowsLeft) ? (this.rowsLeft[i] || 0) : this.rowsLeft;
+ // 如果有title占位图,第一个段落占位图的外边距需要大一些,如果没有title占位图,第一个段落占位图则无需外边距
+ // 之所以需要这么做,是因为weex的无能,以提升性能为借口不支持css的一些伪类
+ item.marginTop = !this.title && i === 0 ? 0 : this.title && i === 0 ? '20px' : '12px'
+ // 如果设置的为百分比的宽度,转换为px值,因为nvue不支持百分比单位
+ if (/%$/.test(rowWidth)) {
+ // 通过parseInt提取出百分比单位中的数值部分,除以100得到百分比的小数值
+ item.width = this.$uv.addUnit(this.width * parseInt(rowWidth) / 100)
+ } else {
+ item.width = this.$uv.addUnit(rowWidth)
+ }
+ item.height = this.$uv.addUnit(rowHeight)
+ item.marginLeft = this.$uv.addUnit(rowLeft)
+ rows.push(item)
+ }
+ // console.log(rows);
+ return rows
+ },
+ uTitleWidth() {
+ let tWidth = 0
+ if (/%$/.test(this.titleWidth)) {
+ // 通过parseInt提取出百分比单位中的数值部分,除以100得到百分比的小数值
+ tWidth = this.$uv.addUnit(this.width * parseInt(this.titleWidth) / 100)
+ } else {
+ tWidth = this.$uv.addUnit(this.titleWidth)
+ }
+ return this.$uv.addUnit(tWidth)
+ },
+
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.getComponentWidth()
+ // #ifdef APP-NVUE
+ this.loading && this.animate && this.setNvueAnimation()
+ // #endif
+ },
+ async setNvueAnimation() {
+ // #ifdef APP-NVUE
+ // 为了让opacity:1的状态保持一定时间,这里做一个延时
+ await this.$uv.sleep(500)
+ const skeleton = this.$refs['uv-skeleton__wrapper'];
+ skeleton && this.loading && this.animate && animation.transition(skeleton, {
+ styles: {
+ opacity: 0.5
+ },
+ duration: 600,
+ }, () => {
+ // 这里无需判断是否loading和开启动画状态,因为最终的状态必须达到opacity: 1,否则可能
+ // 会停留在opacity: 0.5的状态中
+ animation.transition(skeleton, {
+ styles: {
+ opacity: 1
+ },
+ duration: 600,
+ }, () => {
+ // 只有在loading中时,才执行动画
+ this.loading && this.animate && this.setNvueAnimation()
+ })
+ })
+ // #endif
+ },
+ // 获取组件的宽度
+ async getComponentWidth() {
+ // 延时一定时间,以获取dom尺寸
+ await this.$uv.sleep(20)
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-skeleton__wrapper__content').then(size => {
+ this.width = size.width
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-skeleton__wrapper__content']
+ ref && dom.getComponentRect(ref, (res) => {
+ this.width = res.size.width
+ })
+ // #endif
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ @mixin background {
+ /* #ifdef APP-NVUE */
+ background-color: #F1F2F4;
+ /* #endif */
+ /* #ifndef APP-NVUE */
+ background: linear-gradient(90deg, #F1F2F4 25%, #e6e6e6 37%, #F1F2F4 50%);
+ background-size: 400% 100%;
+ /* #endif */
+ }
+
+ .uv-skeleton {
+ flex: 1;
+
+ &__wrapper {
+ @include flex(row);
+
+ &__avatar {
+ @include background;
+ margin-right: 15px;
+
+ &--circle {
+ border-radius: 100px;
+ }
+
+ &--square {
+ border-radius: 4px;
+ }
+ }
+
+ &__content {
+ flex: 1;
+
+ &__rows,
+ &__title {
+ @include background;
+ border-radius: 3px;
+ }
+ }
+ }
+ }
+
+ /* #ifndef APP-NVUE */
+ .animate {
+ animation: skeleton 1.8s ease infinite
+ }
+
+ @keyframes skeleton {
+ 0% {
+ background-position: 100% 50%
+ }
+
+ 100% {
+ background-position: 0 50%
+ }
+ }
+
+ /* #endif */
+</style>
diff --git a/uni_modules/uv-skeleton/package.json b/uni_modules/uv-skeleton/package.json
new file mode 100644
index 0000000..388e096
--- /dev/null
+++ b/uni_modules/uv-skeleton/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-skeleton",
+ "displayName": "uv-skeleton 骨架屏 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.2",
+ "description": "骨架屏一般用于页面在请求远程数据尚未完成时,页面用灰色块预显示本来的页面结构,给用户更好的体验。",
+ "keywords": [
+ "uv-skeleton",
+ "uvui",
+ "uv-ui",
+ "skeleton",
+ "骨架屏"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-skeleton/readme.md b/uni_modules/uv-skeleton/readme.md
new file mode 100644
index 0000000..02314cd
--- /dev/null
+++ b/uni_modules/uv-skeleton/readme.md
@@ -0,0 +1,11 @@
+## Skeleton 骨架屏
+
+> **组件名:uv-skeleton**
+
+骨架屏一般用于页面在请求远程数据尚未完成时,页面用灰色块预显示本来的页面结构,给用户更好的体验。
+
+### <a href="https://www.uvui.cn/components/skeleton.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-skeletons/changelog.md b/uni_modules/uv-skeletons/changelog.md
new file mode 100644
index 0000000..c2d58dc
--- /dev/null
+++ b/uni_modules/uv-skeletons/changelog.md
@@ -0,0 +1,4 @@
+## 1.0.1(2023-11-15)
+1. 修复支付宝小程序报错的BUG
+## 1.0.0(2023-10-30)
+1. 新版骨架屏上线
diff --git a/uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue b/uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue
new file mode 100644
index 0000000..2b2243b
--- /dev/null
+++ b/uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue
@@ -0,0 +1,247 @@
+<template>
+ <view class="uv-skeleton">
+ <view v-if="loading">
+ <view v-for="(item, index) in elements" :key="index">
+ <!-- 横向并列布局 -->
+ <view class="uv-skeleton__group" :style="[style(item)]" v-if="item.type=='flex'">
+ <view v-for="(item2,index2) in item.children" :class="[item2.clas]" :style="[style(item2)]" :key="index2">
+ <view v-for="(item3,index3) in item2.elements" :key="index3">
+ <!-- 垂直高度站位 -->
+ <view :style="{height: $uv.addUnit(item3.height,'rpx')}" v-if="item3.type=='gap'"></view>
+ <view :class="[item3.clas, roundClass, animateClass]" :style="[style(item3)]" v-else ref="skeleton"></view>
+ </view>
+ </view>
+ </view>
+ <!-- 自定义骨架屏内容 -->
+ <!-- ps 自定义扩展说明: -->
+ <!-- 如果你需要自定义模板,可以参照这个custom的写法,增加一个skeleton配置类型,编写布局和样式就可以了 -->
+ <!-- 注意事项:为了保证骨架效果和动态效果的一致,扩展时,在你希望实际展示在页面中的元素上加上 :class="[style, animateClass]" 和 :style="[style(item)]" 和 ref="skeleton" -->
+ <view :class="[item.clas, animateClass]" :style="[style(item)]" ref="skeleton" v-else-if="item.type == 'custom'"></view>
+ <!-- 垂直高度站位 -->
+ <view :style="{height: $uv.addUnit(item.height,'rpx')}" v-else-if="item.type=='gap'"></view>
+ <!-- 其他基本单位 line avatar 等 -->
+ <view :class="[item.clas, roundClass, animateClass]" :style="[style(item)]" v-else ref="skeleton"></view>
+ </view>
+ </view>
+ <view v-else>
+ <slot />
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ const animation = weex.requireModule('animation');
+ // #endif
+ export default {
+ name: 'uv-skeletons',
+ mixins: [mpMixin, mixin],
+ props: {
+ // 是否显示骨架
+ loading: {
+ type: Boolean,
+ default: true
+ },
+ // 骨架内容 具体说明参考文档:
+ skeleton: {
+ type: Array,
+ default: () => []
+ },
+ // 是否开启动画效果
+ animate: {
+ type: Boolean,
+ default: true
+ },
+ // 是否圆角骨架风格
+ round: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.skeleton
+ },
+ data() {
+ return {
+ elements: [],
+ opacity: 0.5,
+ duration: 600
+ }
+ },
+ computed: {
+ animateClass() {
+ return this.animate ? 'uv-skeleton--animation' : 'uv-skeleton--static';
+ },
+ roundClass() {
+ return this.round ? 'uv-skeleton--round' : 'uv-skeleton--radius';
+ },
+ style(item) {
+ return item => {
+ const style = {};
+ return this.$uv.deepMerge(style, this.$uv.addStyle(item.style));
+ }
+ }
+ },
+ created() {
+ this.init();
+ // #ifdef APP-NVUE
+ if (this.loading && this.animate) {
+ this.$uv.sleep(50).then(res => {
+ this.createAnimation(this.opacity);
+ })
+ }
+ // #endif
+ },
+ methods: {
+ /**
+ * 初始化数据
+ */
+ init() {
+ let elements = [];
+ if (!this.$uv.test.array(this.skeleton)) return this.$uv.error('skeleton参数必须为数组形式,参考文档示例:');
+ this.skeleton.forEach(el => {
+ const elClass = this.getElCounts(el);
+ elements = elements.concat(elClass);
+ })
+ this.elements = [...elements];
+ },
+ /**
+ * 处理骨架屏参数内容
+ * @param {Object} el 每项数据
+ */
+ getElCounts(el) {
+ let elements = [];
+ let children = [];
+ if (this.$uv.test.number(el)) {
+ elements.push({
+ type: 'gap',
+ height: el
+ });
+ return elements;
+ } else {
+ const type = el.type ? el.type : 'line';
+ const num = el.num ? el.num : 1;
+ const style = el.style ? el.style : {};
+ const styleIsArray = this.$uv.test.array(style);
+ const gap = el.gap ? el.gap : '20rpx';
+ const child = el.children ? el.children : [];
+ for (let i = 0; i < child.length; i++) {
+ children[i] = {
+ clas: child[i].type.indexOf('avatar') > -1 || child[i].type.indexOf('custom') > -1 ? '' : 'uv-skeleton__group__sub',
+ elements: this.getElCounts(child[i])
+ };
+ }
+ for (let i = 0; i < num; i++) {
+ if (gap && i < num && i > 0) {
+ elements.push({
+ type: 'gap',
+ height: gap
+ });
+ }
+ elements.push({
+ clas: `uv-skeleton__${type}`,
+ type,
+ style: styleIsArray ? style[i] : style,
+ gap,
+ children
+ });
+ }
+ return elements;
+ }
+ },
+ /**
+ * 创建动画
+ */
+ createAnimation(opacity = 1) {
+ // loading结束之后或未开启动画不执行
+ if (!this.loading || !this.animate) return;
+ let count = 0;
+ const skeletons = this.$refs.skeleton;
+ skeletons.forEach(item => {
+ animation.transition(item, {
+ styles: {
+ opacity: opacity,
+ },
+ duration: this.duration
+ }, () => {
+ count++;
+ if (count >= skeletons.length) {
+ setTimeout(() => {
+ this.createAnimation(opacity < 1 ? 1 : this.opacity);
+ }, 200)
+ }
+ });
+ })
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @mixin background {
+ /* #ifdef APP-NVUE */
+ background-color: #e6e6e6;
+ /* #endif */
+ /* #ifndef APP-NVUE */
+ background: linear-gradient(90deg, #F1F2F4 25%, #e6e6e6 37%, #F1F2F4 50%);
+ background-size: 400% 100%;
+ /* #endif */
+ }
+ .uv-skeleton__line {
+ height: 32rpx;
+ // margin-bottom: 30rpx;
+ }
+ .uv-skeleton__line--sm {
+ height: 24rpx;
+ margin-bottom: 30rpx;
+ }
+ .uv-skeleton__line--lg {
+ height: 48rpx;
+ margin-bottom: 30rpx;
+ }
+ .uv-skeleton--static {
+ @include background;
+ }
+ .uv-skeleton--radius {
+ border-radius: 8rpx;
+ }
+ .uv-skeleton--round {
+ border-radius: 30rpx;
+ }
+ .uv-skeleton__avatar {
+ width: 100rpx;
+ height: 100rpx;
+ border-radius: 50rpx;
+ }
+ .uv-skeleton__avatar--sm {
+ width: 50rpx;
+ height: 50rpx;
+ border-radius: 25rpx;
+ }
+ .uv-skeleton__avatar--lg {
+ width: 200rpx;
+ height: 200rpx;
+ border-radius: 100rpx;
+ }
+ .uv-skeleton__group {
+ @include flex;
+ &__sub {
+ flex: 1;
+ }
+ }
+ .uv-skeleton--animation {
+ @include background;
+ /* #ifndef APP-NVUE */
+ animation: skeleton 1.8s ease infinite
+ /* #endif */
+ }
+ /* #ifndef APP-NVUE */
+ @keyframes skeleton {
+ 0% {
+ background-position: 100% 50%
+ }
+ 100% {
+ background-position: 0 50%
+ }
+ }
+ /* #endif */
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-skeletons/package.json b/uni_modules/uv-skeletons/package.json
new file mode 100644
index 0000000..9a36299
--- /dev/null
+++ b/uni_modules/uv-skeletons/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-skeletons",
+ "displayName": "uv-skeletons 骨架屏 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.1",
+ "description": "全新升级骨架屏,更加灵活,体验更加,强烈推荐使用新版骨架屏。一般用于页面在请求远程数据尚未完成时,在内容加载出来前展示与内容布局结构一致的灰白块,提升用户视觉体验。",
+ "keywords": [
+ "uv-skeletons",
+ "uvui",
+ "uv-ui",
+ "skeleton",
+ "骨架屏"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-text"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-skeletons/readme.md b/uni_modules/uv-skeletons/readme.md
new file mode 100644
index 0000000..a08cec1
--- /dev/null
+++ b/uni_modules/uv-skeletons/readme.md
@@ -0,0 +1,23 @@
+# Skeletons 新版骨架屏
+
+> **组件名:uv-skeletons**
+
+骨架屏一般用于页面在请求远程数据尚未完成时,在内容加载出来前展示与内容布局结构一致的灰白块,提升用户视觉体验。
+
+新版骨架屏与老版本使用参数有很大的区别,所以才有了新版本,无法替换老版本。
+
+新版骨架屏比老版本骨架屏更加灵活,理论上简单的左右上下布局结构均可以轻松实现,样式自由度较高,只要你会写css,推荐使用新版。
+
+# <a href="https://www.uvui.cn/components/skeletons.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-slider/changelog.md b/uni_modules/uv-slider/changelog.md
new file mode 100644
index 0000000..d065ed0
--- /dev/null
+++ b/uni_modules/uv-slider/changelog.md
@@ -0,0 +1,9 @@
+## 1.0.3(2023-07-25)
+1. 将inactiveColor修正为backgroundColor 参数,避免不生效
+## 1.0.2(2023-07-13)
+1. 修复 uv-slider设置value属性不生效的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-slider 滑动选择器
diff --git a/uni_modules/uv-slider/components/uv-slider/props.js b/uni_modules/uv-slider/components/uv-slider/props.js
new file mode 100644
index 0000000..1c622de
--- /dev/null
+++ b/uni_modules/uv-slider/components/uv-slider/props.js
@@ -0,0 +1,58 @@
+export default {
+ props: {
+ value: {
+ type: [Number, String],
+ default: 0
+ },
+ modelValue: {
+ type: [Number, String],
+ default: 0
+ },
+ // 最小可选值
+ min: {
+ type: [Number, String],
+ default: 0
+ },
+ // 最大可选值
+ max: {
+ type: [Number, String],
+ default: 100
+ },
+ // 步长,取值必须大于 0,并且可被(max - min)整除
+ step: {
+ type: [Number, String],
+ default: 1
+ },
+ // 滑块右侧已选择部分的背景色
+ activeColor: {
+ type: String,
+ default: '#2979ff'
+ },
+ // 滑块左侧未选择部分的背景色
+ backgroundColor: {
+ type: String,
+ default: '#c0c4cc'
+ },
+ // 滑块的大小,取值范围为 12 - 28
+ blockSize: {
+ type: [Number, String],
+ default: 18
+ },
+ // 滑块的颜色
+ blockColor: {
+ type: String,
+ default: '#ffffff'
+ },
+ // 禁用状态
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示当前的选择值
+ showValue: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.slider
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-slider/components/uv-slider/uv-slider.vue b/uni_modules/uv-slider/components/uv-slider/uv-slider.vue
new file mode 100644
index 0000000..9c2a86a
--- /dev/null
+++ b/uni_modules/uv-slider/components/uv-slider/uv-slider.vue
@@ -0,0 +1,58 @@
+<template>
+ <view
+ class="uv-slider"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <slider
+ :min="min"
+ :max="max"
+ :step="step"
+ :value="sliderValue"
+ :activeColor="activeColor"
+ :backgroundColor="backgroundColor"
+ :blockSize="$uv.getPx(blockSize)"
+ :blockColor="blockColor"
+ :showValue="showValue"
+ :disabled="disabled"
+ @changing="changingHandler"
+ @change="changeHandler"
+ ></slider>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js'
+ export default {
+ name: 'uv-slider',
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ sliderValue(){
+ return this.value || this.modelValue;
+ }
+ },
+ methods: {
+ // 拖动过程中触发
+ changingHandler(e) {
+ const { value } = e.detail
+ // 更新v-model的值
+ this.$emit('input', value)
+ this.$emit('update:modelValue',value)
+ // 触发事件
+ this.$emit('changing', value)
+ },
+ // 滑动结束时触发
+ changeHandler(e) {
+ const {
+ value
+ } = e.detail
+ // 更新v-model的值
+ this.$emit('input', value)
+ this.$emit('update:modelValue',value)
+ // 触发事件
+ this.$emit('change', value)
+ }
+ }
+ }
+</script>
+
diff --git a/uni_modules/uv-slider/package.json b/uni_modules/uv-slider/package.json
new file mode 100644
index 0000000..1393951
--- /dev/null
+++ b/uni_modules/uv-slider/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-slider",
+ "displayName": "uv-slider 滑动选择器 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.3",
+ "description": "滑动选择器组件一般用于表单中,手动选择一个区间范围的场景。",
+ "keywords": [
+ "uv-slider",
+ "uvui",
+ "uv-ui",
+ "slider",
+ "滑动选择器"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-slider/readme.md b/uni_modules/uv-slider/readme.md
new file mode 100644
index 0000000..c9c00cc
--- /dev/null
+++ b/uni_modules/uv-slider/readme.md
@@ -0,0 +1,15 @@
+## Slider 滑动选择器
+
+> **组件名:uv-slider**
+
+该组件一般用于表单中,手动选择一个区间范围的场景。
+
+# <a href="https://www.uvui.cn/components/slider.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-steps/changelog.md b/uni_modules/uv-steps/changelog.md
new file mode 100644
index 0000000..b05c894
--- /dev/null
+++ b/uni_modules/uv-steps/changelog.md
@@ -0,0 +1,10 @@
+## 1.0.3(2023-06-29)
+1. 增加customStyle参数,方便设置样式
+## 1.0.2(2023-06-29)
+1. 新增title自定义
+2. 完善文档说明
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-steps 步骤条
diff --git a/uni_modules/uv-steps/components/uv-steps-item/props.js b/uni_modules/uv-steps/components/uv-steps-item/props.js
new file mode 100644
index 0000000..1af6711
--- /dev/null
+++ b/uni_modules/uv-steps/components/uv-steps-item/props.js
@@ -0,0 +1,25 @@
+export default {
+ props: {
+ // 标题
+ title: {
+ type: [String, Number],
+ default: ''
+ },
+ // 描述文本
+ desc: {
+ type: [String, Number],
+ default: ''
+ },
+ // 图标大小
+ iconSize: {
+ type: [String, Number],
+ default: 17
+ },
+ // 当前步骤是否处于失败状态
+ error: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.stepsItem
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue b/uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue
new file mode 100644
index 0000000..cfb6166
--- /dev/null
+++ b/uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue
@@ -0,0 +1,347 @@
+<template>
+ <view
+ class="uv-steps-item"
+ ref="uv-steps-item"
+ :class="[`uv-steps-item--${parentData.direction}`]"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <view
+ class="uv-steps-item__line"
+ v-if="index + 1 < childLength"
+ :class="[`uv-steps-item__line--${parentData.direction}`]"
+ :style="[lineStyle]">
+ </view>
+ <view
+ :class="[
+ 'uv-steps-item__wrapper',
+ `uv-steps-item__wrapper--${parentData.direction}`,
+ parentData.dot && `uv-steps-item__wrapper--${parentData.direction}--dot`
+ ]">
+ <slot name="icon">
+ <view
+ class="uv-steps-item__wrapper__dot"
+ v-if="parentData.dot"
+ :style="{
+ backgroundColor: statusColor
+ }">
+ </view>
+ <view
+ class="uv-steps-item__wrapper__icon"
+ v-else-if="parentData.activeIcon || parentData.inactiveIcon">
+ <uv-icon
+ :name="index <= parentData.current ? parentData.activeIcon : parentData.inactiveIcon"
+ :size="iconSize"
+ :color="index <= parentData.current ? parentData.activeColor : parentData.inactiveColor">
+ </uv-icon>
+ </view>
+ <view
+ v-else
+ :style="{
+ backgroundColor: statusClass === 'process' ? parentData.activeColor : 'transparent',
+ borderColor: statusColor
+ }"
+ class="uv-steps-item__wrapper__circle">
+ <text
+ v-if="statusClass === 'process' || statusClass === 'wait'"
+ class="uv-steps-item__wrapper__circle__text"
+ :style="{
+ color: index == parentData.current ? '#ffffff' : parentData.inactiveColor
+ }">{{ index + 1}}</text>
+ <uv-icon
+ v-else
+ :color="statusClass === 'error' ? 'error' : parentData.activeColor"
+ size="12"
+ :name="statusClass === 'error' ? 'close' : 'checkmark'">
+ </uv-icon>
+ </view>
+ </slot>
+ </view>
+ <view
+ :class="[
+ 'uv-steps-item__content',
+ `uv-steps-item__content--${parentData.direction}`
+ ]"
+ :style="[contentStyle]"
+ >
+ <slot name="title">
+ <uv-text
+ :text="title"
+ :type="parentData.current == index ? 'main' : 'content'"
+ lineHeight="20px"
+ :size="parentData.current == index ? 14 : 13"
+ ></uv-text>
+ </slot>
+ <slot name="desc">
+ <uv-text
+ :text="desc"
+ type="tips"
+ size="12"
+ ></uv-text>
+ </slot>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * StepsItem 步骤条的子组件
+ * @description 本组件需要和uv-steps配合使用
+ * @tutorial https://www.uvui.cn/components/steps.html
+ * @property {String} title 标题文字
+ * @property {String} current 描述文本
+ * @property {String | Number} iconSize 图标大小 (默认 17 )
+ * @property {Boolean} error 当前步骤是否处于失败状态 (默认 false )
+ * @example <uv-steps current="0"><uv-steps-item title="已出库" desc="10:35" ></uv-steps-item></uv-steps>
+ */
+ export default {
+ name: 'uv-steps-item',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ index: 0,
+ childLength: 0,
+ showLine: false,
+ size: {
+ height: 0,
+ width: 0
+ },
+ parentData: {
+ direction: 'row',
+ current: 0,
+ activeColor: '',
+ inactiveColor: '',
+ activeIcon: '',
+ inactiveIcon: '',
+ dot: false
+ }
+ }
+ },
+ watch: {
+ 'parentData'(newValue, oldValue) {}
+ },
+ created() {
+ this.init()
+ },
+ computed: {
+ lineStyle() {
+ const style = {}
+ if (this.parentData.direction === 'row') {
+ style.width = this.size.width + 'px'
+ style.left = this.size.width / 2 + 'px'
+ } else {
+ style.height = this.size.height + 'px'
+ }
+ style.backgroundColor = this.parent.children?.[this.index + 1]?.error ? '#f56c6c' : this.index < this.parentData.current ? this.parentData.activeColor : this.parentData.inactiveColor
+ return style
+ },
+ statusClass() {
+ const {
+ index,
+ error
+ } = this
+ const {
+ current
+ } = this.parentData
+ if (current == index) {
+ return error === true ? 'error' : 'process'
+ } else if (error) {
+ return 'error'
+ } else if (current > index) {
+ return 'finish'
+ } else {
+ return 'wait'
+ }
+ },
+ statusColor() {
+ let color = ''
+ switch (this.statusClass) {
+ case 'finish':
+ color = this.parentData.activeColor
+ break
+ case 'error':
+ color = '#f56c6c'
+ break
+ case 'process':
+ color = this.parentData.dot ? this.parentData.activeColor : 'transparent'
+ break
+ default:
+ color = this.parentData.inactiveColor
+ break
+ }
+ return color
+ },
+ contentStyle() {
+ const style = {}
+ if (this.parentData.direction === 'column') {
+ style.marginLeft = this.parentData.dot ? '2px' : '6px'
+ style.marginTop = this.parentData.dot ? '0px' : '6px'
+ } else {
+ style.marginTop = this.parentData.dot ? '2px' : '6px'
+ style.marginLeft = this.parentData.dot ? '2px' : '6px'
+ }
+ return style
+ }
+ },
+ mounted() {
+ this.parent && this.parent.updateFromChild()
+ this.$uv.sleep().then(() => {
+ this.getStepsItemRect()
+ })
+ },
+ methods: {
+ init() {
+ // 初始化数据
+ this.updateParentData()
+ if (!this.parent) {
+ return this.$uv.error('uv-steps-item必须要搭配uv-steps组件使用')
+ }
+ this.index = this.parent.children.indexOf(this)
+ this.childLength = this.parent.children.length
+ },
+ updateParentData() {
+ // 此方法在mixin中
+ this.getParentData('uv-steps')
+ },
+ // 父组件数据发生变化
+ updateFromParent() {
+ this.init()
+ },
+ // 获取组件的尺寸,用于设置横线的位置
+ getStepsItemRect() {
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-steps-item').then(size => {
+ this.size = size
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ dom.getComponentRect(this.$refs['uv-steps-item'], res => {
+ const {
+ size
+ } = res
+ this.size = size
+ })
+ // #endif
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-steps-item {
+ flex: 1;
+ @include flex;
+
+ &--row {
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+ }
+
+ &--column {
+ position: relative;
+ flex-direction: row;
+ justify-content: flex-start;
+ padding-bottom: 5px;
+ }
+
+ &__wrapper {
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ background-color: #fff;
+
+ &--column {
+ width: 20px;
+ height: 32px;
+
+ &--dot {
+ height: 20px;
+ width: 20px;
+ }
+ }
+
+ &--row {
+ width: 32px;
+ height: 20px;
+
+ &--dot {
+ width: 20px;
+ height: 20px;
+ }
+ }
+
+ &__circle {
+ width: 20px;
+ height: 20px;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ flex-shrink: 0;
+ /* #endif */
+ border-radius: 100px;
+ border-width: 1px;
+ border-color: $uv-tips-color;
+ border-style: solid;
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.3s;
+
+ &__text {
+ color: $uv-tips-color;
+ font-size: 11px;
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ line-height: 11px;
+ }
+ }
+
+ &__dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 100px;
+ background-color: $uv-content-color;
+ }
+ }
+
+ &__content {
+ @include flex;
+ flex: 1;
+
+ &--row {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ &--column {
+ flex-direction: column;
+ margin-left: 6px;
+ }
+ }
+
+ &__line {
+ position: absolute;
+ background: $uv-tips-color;
+
+ &--row {
+ top: 10px;
+ height: 1px;
+ }
+
+ &--column {
+ width: 1px;
+ left: 10px;
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-steps/components/uv-steps/props.js b/uni_modules/uv-steps/components/uv-steps/props.js
new file mode 100644
index 0000000..61414ed
--- /dev/null
+++ b/uni_modules/uv-steps/components/uv-steps/props.js
@@ -0,0 +1,40 @@
+export default {
+ props: {
+ // 排列方向
+ direction: {
+ type: String,
+ default: 'row'
+ },
+ // 设置第几个步骤
+ current: {
+ type: [String, Number],
+ default: 0
+ },
+ // 激活状态颜色
+ activeColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 未激活状态颜色
+ inactiveColor: {
+ type: String,
+ default: '#969799'
+ },
+ // 激活状态的图标
+ activeIcon: {
+ type: String,
+ default: ''
+ },
+ // 未激活状态图标
+ inactiveIcon: {
+ type: String,
+ default: ''
+ },
+ // 是否显示点类型
+ dot: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.steps
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-steps/components/uv-steps/uv-steps.vue b/uni_modules/uv-steps/components/uv-steps/uv-steps.vue
new file mode 100644
index 0000000..c448f9e
--- /dev/null
+++ b/uni_modules/uv-steps/components/uv-steps/uv-steps.vue
@@ -0,0 +1,83 @@
+<template>
+ <view
+ :class="['uv-steps',`uv-steps--${direction}`]"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <slot></slot>
+ </view>
+</template>
+
+<script>
+ import { func } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Steps 步骤条
+ * @description 该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
+ * @tutorial https://www.uvui.cn/components/steps.html
+ * @property {String} direction row-横向,column-竖向 (默认 'row' )
+ * @property {String | Number} current 设置当前处于第几步 (默认 0 )
+ * @property {String} activeColor 激活状态颜色 (默认 '#3c9cff' )
+ * @property {String} inactiveColor 未激活状态颜色 (默认 '#969799' )
+ * @property {String} activeIcon 激活状态的图标
+ * @property {String} inactiveIcon 未激活状态图标
+ * @property {Boolean} dot 是否显示点类型 (默认 false )
+ * @example <uv-steps current="0"><uv-steps-item title="已出库" desc="10:35" ></uv-steps-item></uv-steps>
+ */
+ export default {
+ name: 'uv-steps',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ }
+ },
+ watch: {
+ children() {
+ this.updateChildData()
+ },
+ parentData() {
+ this.updateChildData()
+ }
+ },
+ computed: {
+ // 监听参数的变化,通过watch中,手动去更新子组件的数据,否则子组件不会自动变化
+ parentData() {
+ return [this.current, this.direction, this.activeColor, this.inactiveColor, this.activeIcon, this.inactiveIcon, this.dot]
+ }
+ },
+ methods: {
+ // 更新子组件的数据
+ updateChildData() {
+ this.children.map(child => {
+ // 先判断子组件是否存在对应的方法
+ func((child || {}).updateFromParent()) && child.updateFromParent()
+ })
+ },
+ // 接受子组件的通知,去修改其他子组件的数据
+ updateFromChild() {
+ this.updateChildData()
+ }
+ },
+ created() {
+ this.children = []
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-steps {
+ @include flex;
+
+ &--column {
+ flex-direction: column
+ }
+
+ &--row {
+ flex-direction: row;
+ flex: 1;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-steps/package.json b/uni_modules/uv-steps/package.json
new file mode 100644
index 0000000..a0a938a
--- /dev/null
+++ b/uni_modules/uv-steps/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-steps",
+ "displayName": "uv-steps 步骤条 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "步骤条组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。",
+ "keywords": [
+ "uv-steps",
+ "uvui",
+ "uv-ui",
+ "steps",
+ "步骤条"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-text"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "n"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-steps/readme.md b/uni_modules/uv-steps/readme.md
new file mode 100644
index 0000000..0f2f574
--- /dev/null
+++ b/uni_modules/uv-steps/readme.md
@@ -0,0 +1,11 @@
+## Steps 步骤条
+
+> **组件名:uv-steps**
+
+该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
+
+### <a href="https://www.uvui.cn/components/steps.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-sticky/changelog.md b/uni_modules/uv-sticky/changelog.md
new file mode 100644
index 0000000..c079cce
--- /dev/null
+++ b/uni_modules/uv-sticky/changelog.md
@@ -0,0 +1,11 @@
+## 1.0.4(2023-12-20)
+1. 优化
+## 1.0.3(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.2(2023-08-27)
+1. 优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-sticky 吸顶
diff --git a/uni_modules/uv-sticky/components/uv-sticky/props.js b/uni_modules/uv-sticky/components/uv-sticky/props.js
new file mode 100644
index 0000000..e7d2d2e
--- /dev/null
+++ b/uni_modules/uv-sticky/components/uv-sticky/props.js
@@ -0,0 +1,41 @@
+export default {
+ props: {
+ // 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px
+ offsetTop: {
+ type: [String, Number],
+ default: 0
+ },
+ // 自定义导航栏的高度
+ customNavHeight: {
+ type: [String, Number],
+ // #ifdef H5
+ // H5端的导航栏属于“自定义”导航栏的范畴,因为它是非原生的,与普通元素一致
+ default: 44,
+ // #endif
+ // #ifndef H5
+ default: 0
+ // #endif
+ },
+ // 是否禁用吸顶功能
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 吸顶区域的背景颜色
+ bgColor: {
+ type: String,
+ default: 'transparent'
+ },
+ // z-index值
+ zIndex: {
+ type: [String, Number],
+ default: ''
+ },
+ // 列表中的索引值
+ index: {
+ type: [String, Number],
+ default: ''
+ },
+ ...uni.$uv?.props?.sticky
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue b/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue
new file mode 100644
index 0000000..4ca7b45
--- /dev/null
+++ b/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue
@@ -0,0 +1,223 @@
+<template>
+ <view
+ class="uv-sticky"
+ :id="elId"
+ :style="[style]"
+ >
+ <view
+ :style="[stickyContent]"
+ class="uv-sticky__content"
+ >
+ <slot />
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';;
+ /**
+ * sticky 吸顶
+ * @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。
+ * @tutorial https://www.uvui.cn/components/sticky.html
+ * @property {String | Number} offsetTop 吸顶时与顶部的距离,单位px(默认 0 )
+ * @property {String | Number} customNavHeight 自定义导航栏的高度 (h5 默认44 其他默认 0 )
+ * @property {Boolean} disabled 是否开启吸顶功能 (默认 false )
+ * @property {String} bgColor 组件背景颜色(默认 '#ffffff' )
+ * @property {String | Number} zIndex 吸顶时的z-index值
+ * @property {String | Number} index 自定义标识,用于区分是哪一个组件
+ * @property {Object} customStyle 组件的样式,对象形式
+ * @example <uv-sticky offsetTop="200"><view>塞下秋来风景异,衡阳雁去无留意</view></uv-sticky>
+ */
+ export default {
+ name: 'uv-sticky',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ cssSticky: false, // 是否使用css的sticky实现
+ stickyTop: 0, // 吸顶的top值,因为可能受自定义导航栏影响,最终的吸顶值非offsetTop值
+ elId: '',
+ left: 0, // js模式时,吸顶的内容因为处于postition: fixed模式,为了和原来保持一致的样式,需要记录并重新设置它的left,height,width属性
+ width: 'auto',
+ height: 'auto',
+ fixed: false, // js模式时,是否处于吸顶模式
+ }
+ },
+ computed: {
+ style() {
+ const style = {}
+ if(!this.disabled) {
+ if (this.cssSticky) {
+ style.position = 'sticky'
+ style.zIndex = this.uZindex
+ style.top = this.$uv.addUnit(this.stickyTop)
+ } else {
+ style.height = this.fixed ? this.height + 'px' : 'auto'
+ }
+ } else {
+ // 无需吸顶时,设置会默认的relative(nvue)和非nvue的static静态模式即可
+ // #ifdef APP-NVUE
+ style.position = 'relative'
+ // #endif
+ // #ifndef APP-NVUE
+ style.position = 'static'
+ // #endif
+ }
+ style.backgroundColor = this.bgColor
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ },
+ // 吸顶内容的样式
+ stickyContent() {
+ const style = {}
+ if (!this.cssSticky) {
+ style.position = this.fixed ? 'fixed' : 'static'
+ style.top = this.stickyTop + 'px'
+ style.left = this.left + 'px'
+ style.width = this.width == 'auto' ? 'auto' : this.width + 'px'
+ style.zIndex = this.uZindex
+ }
+ return style
+ },
+ uZindex() {
+ return this.zIndex ? this.zIndex : 970
+ }
+ },
+ created() {
+ this.elId = this.$uv.guid();
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.getStickyTop()
+ // 判断使用的模式
+ this.checkSupportCssSticky()
+ // 如果不支持css sticky,则使用js方案,此方案性能比不上css方案
+ if (!this.cssSticky) {
+ !this.disabled && this.initObserveContent()
+ }
+ },
+ initObserveContent() {
+ // 获取吸顶内容的高度,用于在js吸顶模式时,给父元素一个填充高度,防止"塌陷"
+ this.$uvGetRect('#' + this.elId).then((res) => {
+ this.height = res.height
+ this.left = res.left
+ this.width = res.width
+ this.$nextTick(() => {
+ this.observeContent()
+ })
+ })
+ },
+ observeContent() {
+ // 先断掉之前的观察
+ this.disconnectObserver('contentObserver')
+ const contentObserver = uni.createIntersectionObserver({
+ // 检测的区间范围
+ thresholds: [0.95, 0.98, 1]
+ })
+ // 到屏幕顶部的高度时触发
+ contentObserver.relativeToViewport({
+ top: -this.stickyTop
+ })
+ // 绑定观察的元素
+ contentObserver.observe(`#${this.elId}`, res => {
+ this.setFixed(res.boundingClientRect.top)
+ })
+ this.contentObserver = contentObserver
+ },
+ setFixed(top) {
+ // 判断是否出于吸顶条件范围
+ const fixed = top <= this.stickyTop
+ this.fixed = fixed
+ },
+ disconnectObserver(observerName) {
+ // 断掉观察,释放资源
+ const observer = this[observerName]
+ observer && observer.disconnect()
+ },
+ getStickyTop() {
+ this.stickyTop = this.$uv.getPx(this.offsetTop) + this.$uv.getPx(this.customNavHeight)
+ },
+ async checkSupportCssSticky() {
+ // #ifdef H5
+ // H5,一般都是现代浏览器,是支持css sticky的,这里使用创建元素嗅探的形式判断
+ if (this.checkCssStickyForH5()) {
+ this.cssSticky = true
+ }
+ // #endif
+
+ // 如果安卓版本高于8.0,依然认为是支持css sticky的(因为安卓7在某些机型,可能不支持sticky)
+ if (this.$uv.os() === 'android' && Number(this.$uv.sys().system) > 8) {
+ this.cssSticky = true
+ }
+
+ // APP-Vue和微信平台,通过computedStyle判断是否支持css sticky
+ // #ifdef APP-VUE || MP-WEIXIN
+ this.cssSticky = await this.checkComputedStyle()
+ // #endif
+
+ // ios上,从ios6开始,都是支持css sticky的
+ if (this.$uv.os() === 'ios') {
+ this.cssSticky = true
+ }
+
+ // nvue,是支持css sticky的
+ // #ifdef APP-NVUE
+ this.cssSticky = true
+ // #endif
+ },
+ // 在APP和微信小程序上,通过uni.createSelectorQuery可以判断是否支持css sticky
+ checkComputedStyle() {
+ // 方法内进行判断,避免在其他平台生成无用代码
+ // #ifdef APP-VUE || MP-WEIXIN
+ return new Promise(resolve => {
+ uni.createSelectorQuery().in(this).select('.uv-sticky').fields({
+ computedStyle: ["position"]
+ }).exec(e => {
+ resolve('sticky' === e[0].position)
+ })
+ })
+ // #endif
+ },
+ // H5通过创建元素的形式嗅探是否支持css sticky
+ // 判断浏览器是否支持sticky属性
+ checkCssStickyForH5() {
+ // 方法内进行判断,避免在其他平台生成无用代码
+ // #ifdef H5
+ const vendorList = ['', '-webkit-', '-ms-', '-moz-', '-o-'],
+ vendorListLength = vendorList.length,
+ stickyElement = document.createElement('div')
+ for (let i = 0; i < vendorListLength; i++) {
+ stickyElement.style.position = vendorList[i] + 'sticky'
+ if (stickyElement.style.position !== '') {
+ return true
+ }
+ }
+ return false;
+ // #endif
+ }
+ },
+ // #ifdef VUE2
+ beforeDestroy() {
+ this.disconnectObserver('contentObserver')
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ this.disconnectObserver('contentObserver')
+ }
+ // #endif
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-sticky {
+ /* #ifdef APP-VUE || MP-WEIXIN */
+ // 此处默认写sticky属性,是为了给微信和APP通过uni.createSelectorQuery查询是否支持css sticky使用
+ position: sticky;
+ /* #endif */
+ }
+</style>
diff --git a/uni_modules/uv-sticky/package.json b/uni_modules/uv-sticky/package.json
new file mode 100644
index 0000000..036f922
--- /dev/null
+++ b/uni_modules/uv-sticky/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-sticky",
+ "displayName": "uv-sticky 吸顶 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.4",
+ "description": "吸顶组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。",
+ "keywords": [
+ "uv-sticky",
+ "uvui",
+ "uv-ui",
+ "sticky",
+ "吸顶"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-sticky/readme.md b/uni_modules/uv-sticky/readme.md
new file mode 100644
index 0000000..c9319a6
--- /dev/null
+++ b/uni_modules/uv-sticky/readme.md
@@ -0,0 +1,11 @@
+## Sticky 吸顶
+
+> **组件名:uv-sticky**
+
+该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。
+
+### <a href="https://www.uvui.cn/components/sticky.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-subsection/changelog.md b/uni_modules/uv-subsection/changelog.md
new file mode 100644
index 0000000..8a0fd65
--- /dev/null
+++ b/uni_modules/uv-subsection/changelog.md
@@ -0,0 +1,10 @@
+## 1.0.3(2023-12-20)
+1. 优化
+## 1.0.2(2023-11-27)
+1. 增加customItemStyle属性,方便修改bar样式
+2. 文档后续更新
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-subsection 分段器
diff --git a/uni_modules/uv-subsection/components/uv-subsection/props.js b/uni_modules/uv-subsection/components/uv-subsection/props.js
new file mode 100644
index 0000000..ef5568e
--- /dev/null
+++ b/uni_modules/uv-subsection/components/uv-subsection/props.js
@@ -0,0 +1,54 @@
+export default {
+ props: {
+ // tab的数据
+ list: {
+ type: Array,
+ default: () => []
+ },
+ // 当前活动的tab的index
+ current: {
+ type: [String, Number],
+ default: 0
+ },
+ // 激活的颜色
+ activeColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 未激活的颜色
+ inactiveColor: {
+ type: String,
+ default: '#303133'
+ },
+ // 模式选择,mode=button为按钮形式,mode=subsection时为分段模式
+ mode: {
+ type: String,
+ default: 'button'
+ },
+ // 字体大小
+ fontSize: {
+ type: [String, Number],
+ default: 12
+ },
+ // 激活tab的字体是否加粗
+ bold: {
+ type: Boolean,
+ default: true
+ },
+ // mode = button时,组件背景颜色
+ bgColor: {
+ type: String,
+ default: '#eeeeef'
+ },
+ // 从list元素对象中读取的键名
+ keyName: {
+ type: String,
+ default: 'name'
+ },
+ customItemStyle: {
+ type: [String, Object],
+ default: ''
+ },
+ ...uni.$uv?.props?.subsection
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue b/uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue
new file mode 100644
index 0000000..64338ae
--- /dev/null
+++ b/uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue
@@ -0,0 +1,269 @@
+<template>
+ <view
+ class="uv-subsection"
+ ref="uv-subsection"
+ :class="[`uv-subsection--${mode}`]"
+ :style="[$uv.addStyle(customStyle), wrapperStyle]">
+ <view
+ class="uv-subsection__bar"
+ ref="uv-subsection__bar"
+ :style="[barStyle]"
+ :class="[
+ mode === 'button' && 'uv-subsection--button__bar',
+ current === 0 && mode === 'subsection' && 'uv-subsection__bar--first',
+ current > 0 && current < list.length - 1 && mode === 'subsection' && 'uv-subsection__bar--center',
+ current === list.length - 1 && mode === 'subsection' && 'uv-subsection__bar--last'
+ ]"
+ ></view>
+ <view class="uv-subsection__item"
+ :class="[
+ `uv-subsection__item--${index}`,
+ index < list.length - 1 && 'uv-subsection__item--no-border-right',
+ index === 0 && 'uv-subsection__item--first',
+ index === list.length - 1 && 'uv-subsection__item--last'
+ ]"
+ :ref="`uv-subsection__item--${index}`"
+ :style="[itemStyle(index)]"
+ @tap="clickHandler(index)"
+ v-for="(item, index) in list"
+ :key="index"
+ >
+ <text
+ class="uv-subsection__item__text"
+ :style="[textStyle(index)]">{{ getText(item) }}
+ </text>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin("dom");
+ const animation = uni.requireNativePlugin("animation");
+ // #endif
+ import props from "./props.js";
+ /**
+ * Subsection 分段器
+ * @description 该分段器一般用于用户从几个选项中选择某一个的场景
+ * @tutorial https://www.uvui.cn/components/subsection.html
+ * @property {Array} list tab的数据
+ * @property {String | Number} current 当前活动的tab的index(默认 0 )
+ * @property {String} activeColor 激活时的颜色(默认 '#3c9cff' )
+ * @property {String} inactiveColor 未激活时的颜色(默认 '#303133' )
+ * @property {String} mode 模式选择,mode=button为按钮形式,mode=subsection时为分段模式(默认 'button' )
+ * @property {String | Number} fontSize 字体大小,单位px(默认 12 )
+ * @property {Boolean} bold 激活选项的字体是否加粗(默认 true )
+ * @property {String} bgColor 组件背景颜色,mode为button时有效(默认 '#eeeeef' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ * @property {String} keyName 从`list`元素对象中读取的键名(默认 'name' )
+ *
+ * @event {Function} change 分段器选项发生改变时触发 回调 index:选项的index索引值,从0开始
+ * @example <uv-subsection :list="list" :current="curNow" @change="sectionChange"></uv-subsection>
+ */
+ export default {
+ name: "uv-subsection",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 组件尺寸
+ itemRect: {
+ width: 0,
+ height: 0,
+ }
+ }
+ },
+ watch: {
+ list: {
+ deep: true,
+ handler(){
+ this.init();
+ }
+ },
+ current: {
+ immediate: true,
+ handler(n) {
+ // #ifdef APP-NVUE
+ // 在安卓nvue上,如果通过translateX进行位移,到最后一个时,会导致右侧无法绘制圆角
+ // 故用animation模块进行位移
+ const ref = this.$refs?.["uv-subsection__bar"]?.ref;
+ // 不存在ref的时候(理解为第一次初始化时,需要渲染dom,进行一定延时再获取ref),这里的100ms是经过测试得出的结果(某些安卓需要延时久一点),勿随意修改
+ this.$uv.sleep(ref ? 0 : 150).then(() => {
+ animation.transition(this.$refs["uv-subsection__bar"].ref, {
+ styles: {
+ transform: `translateX(${ n * this.itemRect.width }px)`,
+ transformOrigin: "center center"
+ },
+ duration: 300,
+ });
+ });
+ // #endif
+ }
+ }
+ },
+ computed: {
+ wrapperStyle() {
+ const style = {};
+ // button模式时,设置背景色
+ if (this.mode === "button") {
+ style.backgroundColor = this.bgColor;
+ }
+ return style;
+ },
+ // 滑块的样式
+ barStyle() {
+ const style = {};
+ style.width = `${this.itemRect.width}px`;
+ style.height = `${this.itemRect.height}px`;
+ // 通过translateX移动滑块,其移动的距离为索引*item的宽度
+ // #ifndef APP-NVUE
+ style.transform = `translateX(${ this.current * this.itemRect.width }px)`;
+ // #endif
+ if (this.mode === "subsection") {
+ // 在subsection模式下,需要动态设置滑块的圆角,因为移动滑块使用的是translateX,无法通过父元素设置overflow: hidden隐藏滑块的直角
+ style.backgroundColor = this.activeColor;
+ }
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customItemStyle));
+ },
+ // 分段器item的样式
+ itemStyle(index) {
+ return (index) => {
+ const style = {};
+ if (this.mode === "subsection") {
+ // 设置border的样式
+ style.borderColor = this.activeColor;
+ style.borderWidth = "1px";
+ style.borderStyle = "solid";
+ }
+ return style;
+ };
+ },
+ // 分段器文字颜色
+ textStyle(index) {
+ return (index) => {
+ const style = {};
+ style.fontWeight = this.bold && this.current === index ? "bold" : "normal";
+ style.fontSize = this.$uv.addUnit(this.fontSize);
+ // subsection模式下,激活时默认为白色的文字
+ if (this.mode === "subsection") {
+ style.color = this.current === index ? "#fff" : this.inactiveColor;
+ } else {
+ // button模式下,激活时文字颜色默认为activeColor
+ style.color = this.current === index ? this.activeColor : this.inactiveColor;
+ }
+ return style;
+ };
+ },
+ },
+ mounted() {
+ this.init();
+ },
+ methods: {
+ init() {
+ this.$uv.sleep().then(() => this.getRect());
+ },
+ // 判断展示文本
+ getText(item) {
+ return typeof item === 'object' ? item[this.keyName] : item
+ },
+ // 获取组件的尺寸
+ getRect() {
+ // #ifndef APP-NVUE
+ this.$uvGetRect(".uv-subsection__item--0").then((size) => {
+ this.itemRect = size;
+ });
+ // #endif
+
+ // #ifdef APP-NVUE
+ const ref = this.$refs["uv-subsection__item--0"][0];
+ ref && dom.getComponentRect(ref, (res) => {
+ this.itemRect = res.size;
+ });
+ // #endif
+ },
+ clickHandler(index) {
+ this.$emit("change", index);
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-subsection {
+ @include flex;
+ position: relative;
+ overflow: hidden;
+ /* #ifndef APP-NVUE */
+ width: 100%;
+ box-sizing: border-box;
+ /* #endif */
+ &--button {
+ height: 32px;
+ background-color: rgb(238, 238, 239);
+ padding: 3px;
+ border-radius: 3px;
+ align-items: stretch;
+ &__bar {
+ background-color: #ffffff;
+ }
+ }
+ &--subsection {
+ height: 30px;
+ }
+ &__bar {
+ position: absolute;
+ /* #ifndef APP-NVUE */
+ transition-property: transform, color;
+ transition-duration: 0.3s;
+ transition-timing-function: ease-in-out;
+ /* #endif */
+ &--first {
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+ }
+ &--center {
+ border-top-left-radius: 0px;
+ border-bottom-left-radius: 0px;
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+ }
+ &--last {
+ border-top-left-radius: 0px;
+ border-bottom-left-radius: 0px;
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+ }
+ }
+ &__item {
+ @include flex;
+ flex: 1;
+ justify-content: center;
+ align-items: center;
+ // vue环境下,需要设置相对定位,因为滑块为绝对定位,item需要在滑块的上面
+ position: relative;
+ &--no-border-right {
+ border-right-width: 0 !important;
+ }
+ &--first {
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+ }
+ &--last {
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+ }
+ &__text {
+ font-size: 12px;
+ line-height: 12px;
+ @include flex;
+ align-items: center;
+ transition-property: color;
+ transition-duration: 0.3s;
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-subsection/package.json b/uni_modules/uv-subsection/package.json
new file mode 100644
index 0000000..cd433eb
--- /dev/null
+++ b/uni_modules/uv-subsection/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-subsection",
+ "displayName": "uv-subsection 分段器 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "该分段器组件一般用于用户从几个选项中选择某一个的场景。",
+ "keywords": [
+ "uv-subsection",
+ "uvui",
+ "uv-ui",
+ "subsection",
+ "分段器"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-subsection/readme.md b/uni_modules/uv-subsection/readme.md
new file mode 100644
index 0000000..47de4e6
--- /dev/null
+++ b/uni_modules/uv-subsection/readme.md
@@ -0,0 +1,11 @@
+## Subsection 分段器
+
+> **组件名:uv-subsection**
+
+该分段器一般用于用户从几个选项中选择某一个的场景。
+
+### <a href="https://www.uvui.cn/components/subsection.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-swipe-action/changelog.md b/uni_modules/uv-swipe-action/changelog.md
new file mode 100644
index 0000000..508543c
--- /dev/null
+++ b/uni_modules/uv-swipe-action/changelog.md
@@ -0,0 +1,11 @@
+## 1.0.4(2023-10-13)
+1. 优化
+## 1.0.3(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.2(2023-05-27)
+1. 不支持抖音小程序说明
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-swipe-action 滑动单元格
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs
new file mode 100644
index 0000000..3d62784
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs
@@ -0,0 +1,256 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+ var state = instance.getState()
+ if (state.disable) return
+ var touches = event.touches
+ // 如果进行的是多指触控,不允许进行操作
+ if (touches && touches.length > 1) return
+ // 标识当前为滑动中状态
+ state.moving = true
+ // 记录触摸开始点的坐标值
+ state.startX = touches[0].pageX
+ state.startY = touches[0].pageY
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (state.disabled || !state.moving) return
+
+ var touches = event.touches
+ var pageX = touches[0].pageX
+ var pageY = touches[0].pageY
+ var moveX = pageX - state.startX
+ var moveY = pageY - state.startY
+ var buttonsWidth = state.buttonsWidth
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+ event.preventDefault()
+ event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (state.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
+ moveSwipeAction(moveX, instance, ownerInstance)
+ }
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (!state.moving || state.disabled) return
+ var touches = event.changedTouches ? event.changedTouches[0] : {}
+ var pageX = touches.pageX
+ var pageY = touches.pageY
+ var moveX = pageX - state.startX
+ if (state.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (moveX < 0) return
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (moveX === 0) {
+ return closeSwipeAction(instance, ownerInstance)
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(moveX) < state.threshold) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ closeSwipeAction(instance, ownerInstance)
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (moveX > 0) return
+ // 理由同上
+ if (Math.abs(moveX) < state.threshold) {
+ closeSwipeAction(instance, ownerInstance)
+ } else {
+ openSwipeAction(instance, ownerInstance)
+ }
+ }
+}
+
+// 获取过渡时间
+function getDuration(value) {
+ if (value.toString().indexOf('s') >= 0) return value
+ return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+ var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
+ var len = buttons.length
+ var previewButtonsMoveX = 0
+
+ // 设置菜单内容部分的偏移
+ instance.requestAnimationFrame(function() {
+ instance.setStyle({
+ // 设置translateX的值
+ 'transition': 'none',
+ transform: 'translateX(' + moveX + 'px)',
+ '-webkit-transform': 'translateX(' + moveX + 'px)'
+ })
+ // 折叠按钮动画
+ for (var i = len - 1; i >= 0; i--) {
+ // 通过比例,得出元素自身该移动的距离
+ var translateX = state.buttons[i].width / state.buttonsWidth * moveX
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ var realTranslateX = translateX + previewButtonsMoveX
+ buttons[i].setStyle({
+ // 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程
+ 'transition': 'none',
+ 'transform': 'translateX(' + realTranslateX + 'px)',
+ '-webkit-transform': 'translateX(' + realTranslateX + 'px)'
+ })
+ // 记录本按钮之前的所有按钮的移动距离之和
+ previewButtonsMoveX += translateX
+ }
+ })
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ const duration = getDuration(state.duration)
+ // 展开过程中,是向左移动,所以X的偏移应该为负值
+ var buttonsWidth = -state.buttonsWidth
+ var previewButtonsMoveX = 0
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + buttonsWidth + 'px)',
+ '-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+ })
+ // 设置各个隐藏的按钮为展开的状态
+ for (var i = len - 1; i >= 0; i--) {
+ // 通过比例,得出元素自身该移动的距离
+ var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ var realTranslateX = translateX + previewButtonsMoveX
+ buttons[i].setStyle({
+ // 在移动期间,需要加上动画效果
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + realTranslateX + 'px)',
+ '-webkit-transform': 'translateX(' + realTranslateX + 'px)'
+ })
+ // 记录本按钮之前的所有按钮的移动距离之和
+ previewButtonsMoveX += translateX
+ }
+ })
+ setStatus('open', instance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance) {
+ var state = instance.getState()
+ state.status = status
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ const duration = getDuration(state.duration)
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ // 设置各个隐藏的按钮为收起的状态
+ for (var i = len - 1; i >= 0; i--) {
+ buttons[i].setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ }
+ })
+ setStatus('close', instance)
+}
+
+// show的状态发生变化
+function showChange(newValue, oldValue, ownerInstance, instance) {
+ var state = instance.getState()
+ if (state.disabled) return
+ // 打开或关闭单元格
+ if (newValue) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ closeSwipeAction(instance, ownerInstance)
+ }
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ state.disabled = newValue.disabled
+ state.duration = newValue.duration
+ state.show = newValue.show
+ state.threshold = newValue.threshold
+ state.buttons = newValue.buttons
+
+ var len = state.buttons.length
+ if (len) {
+ var buttonsWidth = 0
+ var buttons = newValue.buttons
+ for (var i = 0; i < len; i++) {
+ buttonsWidth += buttons[i].width
+ }
+ }
+ state.buttonsWidth = buttonsWidth
+}
+
+module.exports = {
+ touchstart: touchstart,
+ touchmove: touchmove,
+ touchend: touchend,
+ sizeChange: sizeChange
+}
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs
new file mode 100644
index 0000000..16add56
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs
@@ -0,0 +1,225 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+ var state = instance.getState()
+ if (state.disabled) return
+ var touches = event.touches
+ // 如果进行的是多指触控,不允许进行操作
+ if (touches && touches.length > 1) return
+ // 标识当前为滑动中状态
+ state.moving = true
+ // 记录触摸开始点的坐标值
+ state.startX = touches[0].pageX
+ state.startY = touches[0].pageY
+
+ ownerInstance.callMethod('closeOther')
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (state.disabled || !state.moving) return
+ var touches = event.touches
+ var pageX = touches[0].pageX
+ var pageY = touches[0].pageY
+ var moveX = pageX - state.startX
+ var moveY = pageY - state.startY
+ var buttonsWidth = state.buttonsWidth
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+ event.preventDefault && event.preventDefault()
+ event.stopPropagation && event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (state.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来
+ moveSwipeAction(moveX, instance, ownerInstance)
+ }
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+ // 触发事件的组件的ComponentDescriptor实例
+ var instance = event.instance
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ if (!state.moving || state.disabled) return
+ var touches = event.changedTouches ? event.changedTouches[0] : {}
+ var pageX = touches.pageX
+ var pageY = touches.pageY
+ var moveX = pageX - state.startX
+ if (state.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (moveX < 0) return
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (moveX === 0) {
+ return closeSwipeAction(instance, ownerInstance)
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(moveX) < state.threshold) {
+ openSwipeAction(instance, ownerInstance)
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ closeSwipeAction(instance, ownerInstance)
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (moveX > 0) return
+ // 理由同上
+ if (Math.abs(moveX) < state.threshold) {
+ closeSwipeAction(instance, ownerInstance)
+ } else {
+ openSwipeAction(instance, ownerInstance)
+ }
+ }
+}
+
+// 获取过渡时间
+function getDuration(value) {
+ if (value.toString().indexOf('s') >= 0) return value
+ return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+ var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
+
+ // 设置菜单内容部分的偏移
+ instance.requestAnimationFrame(function() {
+ instance.setStyle({
+ // 设置translateX的值
+ 'transition': 'none',
+ transform: 'translateX(' + moveX + 'px)',
+ '-webkit-transform': 'translateX(' + moveX + 'px)'
+ })
+ })
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
+ // 处理duration单位问题
+ var duration = getDuration(state.duration)
+ // 展开过程中,是向左移动,所以X的偏移应该为负值
+ var buttonsWidth = -state.buttonsWidth
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(' + buttonsWidth + 'px)',
+ '-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+ })
+ })
+ setStatus('open', instance, ownerInstance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance, ownerInstance) {
+ var state = instance.getState()
+ state.status = status
+ ownerInstance.callMethod('setState', status)
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+ var state = instance.getState()
+ // 获取所有按钮的实例,需要通过它去设置按钮的位移
+ var buttons = ownerInstance.selectAllComponents('.uv-swipe-action-item__right__button')
+ var len = buttons.length
+ // 处理duration单位问题
+ var duration = getDuration(state.duration)
+ instance.requestAnimationFrame(function() {
+ // 设置菜单主体内容
+ instance.setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ // 设置各个隐藏的按钮为收起的状态
+ for (var i = len - 1; i >= 0; i--) {
+ buttons[i].setStyle({
+ 'transition': 'transform ' + duration,
+ 'transform': 'translateX(0px)',
+ '-webkit-transform': 'translateX(0px)'
+ })
+ }
+ })
+ setStatus('close', instance, ownerInstance)
+}
+
+// status的状态发生变化
+function statusChange(newValue, oldValue, ownerInstance, instance) {
+ var state = instance.getState()
+ if (state.disabled) return
+ // 打开或关闭单元格
+ if (newValue === 'close' && state.status === 'open') {
+ closeSwipeAction(instance, ownerInstance)
+ } else if(newValue === 'open' && state.status === 'close') {
+ openSwipeAction(instance, ownerInstance)
+ }
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+ // wxs内的局部变量快照
+ var state = instance.getState()
+ state.disabled = newValue.disabled
+ state.duration = newValue.duration
+ state.show = newValue.show
+ state.threshold = newValue.threshold
+ state.buttons = newValue.buttons
+
+ if (state.buttons) {
+ var len = state.buttons.length
+ var buttonsWidth = 0
+ var buttons = newValue.buttons
+ for (var i = 0; i < len; i++) {
+ buttonsWidth += buttons[i].width
+ }
+ }
+ state.buttonsWidth = buttonsWidth
+}
+
+module.exports = {
+ touchstart: touchstart,
+ touchmove: touchmove,
+ touchend: touchend,
+ sizeChange: sizeChange,
+ statusChange: statusChange
+}
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js
new file mode 100644
index 0000000..da259e8
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js
@@ -0,0 +1,264 @@
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom')
+// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue
+const animation = uni.requireNativePlugin('animation')
+import { sleep } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
+export default {
+ data() {
+ return {
+ // 是否滑动中
+ moving: false,
+ // 状态,open-打开状态,close-关闭状态
+ status: 'close',
+ // 开始触摸点的X和Y轴坐标
+ startX: 0,
+ startY: 0,
+ // 所有隐藏按钮的尺寸信息数组
+ buttons: [],
+ // 所有按钮的总宽度
+ buttonsWidth: 0,
+ // 记录上一次移动的位置值
+ moveX: 0,
+ // 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题
+ lastX: 0
+ }
+ },
+ computed: {
+ // 获取过渡时间
+ getDuratin() {
+ let duration = String(this.duration)
+ // 如果ms为单位,返回ms的数值部分
+ if (duration.indexOf('ms') >= 0) return parseInt(duration)
+ // 如果s为单位,为了得到ms的数值,需要乘以1000
+ if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
+ // 如果值传了数值,且小于30,认为是s单位
+ duration = Number(duration)
+ return duration < 30 ? duration * 1000 : duration
+ }
+ },
+ watch: {
+ show: {
+ immediate: true,
+ handler(n) {
+
+ }
+ }
+ },
+ mounted() {
+ sleep(20).then(() => {
+ this.queryRect()
+ })
+ },
+ methods: {
+ close() {
+ this.closeSwipeAction()
+ },
+ // 触摸单元格
+ touchstart(event) {
+ if (this.disabled) return
+ this.closeOther()
+ const { touches } = event
+ // 记录触摸开始点的坐标值
+ this.startX = touches[0].pageX
+ this.startY = touches[0].pageY
+ },
+ // // 触摸滑动
+ touchmove(event) {
+ if (this.disabled) return
+ const { touches } = event
+ const { pageX } = touches[0]
+ const { pageY } = touches[0]
+ let moveX = pageX - this.startX
+ const moveY = pageY - this.startY
+ const { buttonsWidth } = this
+ const len = this.buttons.length
+
+ // 判断前后两次的移动距离,如果小于一定值,则不进行移动处理
+ if (Math.abs(pageX - this.lastX) < 0.3) return
+ this.lastX = pageX
+
+ // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+ if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) {
+ event.stopPropagation()
+ }
+ // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+ if (Math.abs(moveX) < Math.abs(moveY)) return
+
+ // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+ // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+ // 在超出后,设置为0
+ if (this.status === 'open') {
+ // 在开启状态下,向左滑动,需忽略
+ if (moveX < 0) moveX = 0
+ // 想要收起菜单,最大能移动的距离为按钮的总宽度
+ if (moveX > buttonsWidth) moveX = buttonsWidth
+ // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+ this.moveSwipeAction(-buttonsWidth + moveX)
+ } else {
+ // 关闭状态下,右滑动需忽略
+ if (moveX > 0) moveX = 0
+ // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+ if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+ // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
+ this.moveSwipeAction(moveX)
+ }
+ },
+ // 单元格结束触摸
+ touchend(event) {
+ if (this.disabled) return
+ const touches = event.changedTouches ? event.changedTouches[0] : {}
+ const { pageX } = touches
+ const { pageY } = touches
+ const { buttonsWidth } = this
+ this.moveX = pageX - this.startX
+ if (this.status === 'open') {
+ // 在展开的状态下,继续左滑,无需操作
+ if (this.moveX < 0) this.moveX = 0
+ if (this.moveX > buttonsWidth) this.moveX = buttonsWidth
+ // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+ if (this.moveX === 0) {
+ return this.closeSwipeAction()
+ }
+ // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+ if (Math.abs(this.moveX) < this.threshold) {
+ this.openSwipeAction()
+ } else {
+ // 如果滑动距离大于阈值,则执行收起逻辑
+ this.closeSwipeAction()
+ }
+ } else {
+ // 在关闭的状态下,右滑,无需操作
+ if (this.moveX >= 0) this.moveX = 0
+ if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth
+ // 理由同上
+ if (Math.abs(this.moveX) < this.threshold) {
+ this.closeSwipeAction()
+ } else {
+ this.openSwipeAction()
+ }
+ }
+ },
+ // 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+ moveSwipeAction(moveX) {
+ if (this.moving) return
+ this.moving = true
+
+ let previewButtonsMoveX = 0
+ const len = this.buttons.length
+ animation.transition(this.$refs['uv-swipe-action-item__content'].ref, {
+ styles: {
+ transform: `translateX(${moveX}px)`
+ },
+ timingFunction: 'linear'
+ }, () => {
+ this.moving = false
+ })
+ // 按钮的组的长度
+ for (let i = len - 1; i >= 0; i--) {
+ const buttonRef = this.$refs[`uv-swipe-action-item__right__button-${i}`][0].ref
+ // 通过比例,得出元素自身该移动的距离
+ const translateX = this.buttons[i].width / this.buttonsWidth * moveX
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ const realTranslateX = translateX + previewButtonsMoveX
+ animation.transition(buttonRef, {
+ styles: {
+ transform: `translateX(${realTranslateX}px)`
+ },
+ duration: 0,
+ delay: 0,
+ timingFunction: 'linear'
+ }, () => {})
+ // 记录本按钮之前的所有按钮的移动距离之和
+ previewButtonsMoveX += translateX
+ }
+ },
+ // 关闭菜单
+ closeSwipeAction() {
+ if (this.status === 'close') return
+ this.moving = true
+ const { buttonsWidth } = this
+ animation.transition(this.$refs['uv-swipe-action-item__content'].ref, {
+ styles: {
+ transform: 'translateX(0px)'
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {
+ this.status = 'close'
+ this.moving = false
+ this.closeHandler()
+ })
+ // 按钮的组的长度
+ const len = this.buttons.length
+ for (let i = len - 1; i >= 0; i--) {
+ const buttonRef = this.$refs[`uv-swipe-action-item__right__button-${i}`][0].ref
+ // 如果不满足边界条件,返回
+ if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
+
+ animation.transition(buttonRef, {
+ styles: {
+ transform: 'translateX(0px)'
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {})
+ }
+ },
+ // 打开菜单
+ openSwipeAction() {
+ if (this.status === 'open') return
+ this.moving = true
+ const buttonsWidth = -this.buttonsWidth
+ let previewButtonsMoveX = 0
+ animation.transition(this.$refs['uv-swipe-action-item__content'].ref, {
+ styles: {
+ transform: `translateX(${buttonsWidth}px)`
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {
+ this.status = 'open'
+ this.moving = false
+ this.openHandler()
+ })
+ // 按钮的组的长度
+ const len = this.buttons.length
+ for (let i = len - 1; i >= 0; i--) {
+ const buttonRef = this.$refs[`uv-swipe-action-item__right__button-${i}`][0].ref
+ // 如果不满足边界条件,返回
+ if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
+ // 通过比例,得出元素自身该移动的距离
+ const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth
+ // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+ const realTranslateX = translateX + previewButtonsMoveX
+ animation.transition(buttonRef, {
+ styles: {
+ transform: `translateX(${realTranslateX}px)`
+ },
+ duration: this.getDuratin,
+ timingFunction: 'ease-in-out'
+ }, () => {})
+ previewButtonsMoveX += translateX
+ }
+ },
+ // 查询按钮节点信息
+ queryRect() {
+ // 历遍所有按钮数组,通过getRectByDom返回一个promise
+ const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`uv-swipe-action-item__right__button-${index}`][0]))
+ // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
+ Promise.all(promiseAll).then((sizes) => {
+ this.buttons = sizes
+ // 计算所有按钮总宽度
+ this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
+ })
+ },
+ // 通过nvue的dom模块,查询节点信息
+ getRectByDom(ref) {
+ return new Promise((resolve) => {
+ dom.getComponentRect(ref, (res) => {
+ resolve(res.size)
+ })
+ })
+ }
+ }
+}
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js
new file mode 100644
index 0000000..6eaf491
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js
@@ -0,0 +1,182 @@
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom');
+const bindingX = uni.requireNativePlugin('bindingx');
+const animation = uni.requireNativePlugin('animation');
+import { getDuration, getPx } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
+export default {
+ data() {
+ return {
+ // 所有按钮的总宽度
+ buttonsWidth: 0,
+ // 是否正在移动中
+ moving: false
+ }
+ },
+ computed: {
+ // 获取过渡时间
+ getDuratin() {
+ let duration = String(this.duration)
+ // 如果ms为单位,返回ms的数值部分
+ if (duration.indexOf('ms') >= 0) return parseInt(duration)
+ // 如果s为单位,为了得到ms的数值,需要乘以1000
+ if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
+ // 如果值传了数值,且小于30,认为是s单位
+ duration = Number(duration)
+ return duration < 30 ? duration * 1000 : duration
+ }
+ },
+ watch: {
+ show(n) {
+ if(n) {
+ this.moveCellByAnimation('open')
+ } else {
+ this.moveCellByAnimation('close')
+ }
+ }
+ },
+ mounted() {
+ setTimeout(()=>{
+ this.initialize()
+ },20)
+ },
+ methods: {
+ initialize() {
+ this.queryRect()
+ },
+ // 关闭单元格,用于打开一个,自动关闭其他单元格的场景
+ closeHandler() {
+ if(this.status === 'open') {
+ // 如果在打开状态下,进行点击的话,直接关闭单元格
+ return this.moveCellByAnimation('close') && this.unbindBindingX()
+ }
+ },
+ // 点击单元格
+ clickHandler() {
+ // 如果在移动中被点击,进行忽略
+ if(this.moving) return
+ // 尝试关闭其他打开的单元格
+ this.parent && this.parent.closeOther(this)
+ if(this.status === 'open') {
+ // 如果在打开状态下,进行点击的话,直接关闭单元格
+ return this.moveCellByAnimation('close') && this.unbindBindingX()
+ }
+ },
+ // 滑动单元格
+ onTouchstart(e) {
+ // 如果当前正在移动中,或者disabled状态,则返回
+ if(this.moving || this.disabled) {
+ return this.unbindBindingX()
+ }
+ if(this.status === 'open') {
+ // 如果在打开状态下,进行点击的话,直接关闭单元格
+ return this.moveCellByAnimation('close') && this.unbindBindingX()
+ }
+ // 特殊情况下,e可能不为一个对象
+ e?.stopPropagation && e.stopPropagation()
+ e?.preventDefault && e.preventDefault()
+ this.moving = true
+ // 获取元素ref
+ const content = this.getContentRef()
+ let expression = `min(max(${-this.buttonsWidth}, x), 0)`
+ // 尝试关闭其他打开的单元格
+ this.parent && this.parent.closeOther(this)
+ // 阿里为了KPI而开源的BindingX
+ this.panEvent = bindingX.bind({
+ anchor: content,
+ eventType: 'pan',
+ props: [{
+ element: content,
+ // 绑定width属性,设置其宽度值
+ property: 'transform.translateX',
+ expression
+ }]
+ }, (res) => {
+ this.moving = false
+ if (res.state === 'end' || res.state === 'exit') {
+ const deltaX = res.deltaX
+ if(deltaX <= -this.buttonsWidth || deltaX >= 0) {
+ // 如果触摸滑动的过程中,大于单元格的总宽度,或者大于0,意味着已经动过滑动达到了打开或者关闭的状态
+ // 这里直接进行状态的标记
+ this.$nextTick(() => {
+ this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close'
+ })
+ } else if(Math.abs(deltaX) > getPx(this.threshold)) {
+ // 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭
+ // 移动距离大于0时,意味着需要关闭状态
+ if(Math.abs(deltaX) < this.buttonsWidth) {
+ this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open')
+ }
+ } else {
+ // 在小于阈值时,进行关闭操作(如果在打开状态下,将不会执行bindingX)
+ this.moveCellByAnimation('close')
+ }
+ }
+ })
+ },
+ // 释放bindingX
+ unbindBindingX() {
+ // 释放上一次的资源
+ if (this?.panEvent?.token != 0) {
+ bindingX.unbind({
+ token: this.panEvent?.token,
+ // pan为手势事件
+ eventType: 'pan'
+ })
+ }
+ },
+ // 查询按钮节点信息
+ queryRect() {
+ // 历遍所有按钮数组,通过getRectByDom返回一个promise
+ const promiseAll = this.options.map(async(item, index) => {
+ return await this.getRectByDom(this.$refs[`uv-swipe-action-item__right__button-${index}`][0])
+ })
+ // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
+ Promise.all(promiseAll).then(sizes => {
+ this.buttons = sizes
+ // 计算所有按钮总宽度
+ this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
+ })
+ },
+ // 通过nvue的dom模块,查询节点信息
+ getRectByDom(ref) {
+ return new Promise(resolve => {
+ dom.getComponentRect(ref, res => {
+ resolve(res.size)
+ })
+ })
+ },
+ // 移动单元格到左边或者右边尽头
+ moveCellByAnimation(status = 'open') {
+ if(this.moving) return
+ // 标识当前状态
+ this.moveing = true
+ const content = this.getContentRef()
+ const x = status === 'open' ? -this.buttonsWidth : 0
+ animation.transition(content, {
+ styles: {
+ transform: `translateX(${x}px)`,
+ },
+ duration: getDuration(this.duration, false),
+ timingFunction: 'ease-in-out'
+ }, () => {
+ this.moving = false
+ this.status = status
+ this.unbindBindingX()
+ })
+ },
+ // 获取元素ref
+ getContentRef() {
+ return this.$refs['uv-swipe-action-item__content'].ref
+ }
+ },
+ // #ifdef VUE2
+ beforeDestroy() {
+ this.unbindBindingX()
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ this.unbindBindingX()
+ }
+ // #endif
+}
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js
new file mode 100644
index 0000000..5b321e2
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js
@@ -0,0 +1,40 @@
+export default {
+ props: {
+ // 控制打开或者关闭
+ show: {
+ type: Boolean,
+ default: false
+ },
+ // 标识符,如果是v-for,可用index索引值
+ name: {
+ type: [String, Number],
+ default: ''
+ },
+ // 是否禁用
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 是否自动关闭其他swipe按钮组
+ autoClose: {
+ type: Boolean,
+ default: true
+ },
+ // 滑动距离阈值,只有大于此值,才被认为是要打开菜单
+ threshold: {
+ type: Number,
+ default: 20
+ },
+ // 右侧按钮内容
+ options: {
+ type: Array,
+ default: () => []
+ },
+ // 动画过渡时间,单位ms
+ duration: {
+ type: [String, Number],
+ default: 300
+ },
+ ...uni.$uv?.props?.swipeActionItem
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue
new file mode 100644
index 0000000..8fd503a
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue
@@ -0,0 +1,200 @@
+<template>
+ <view class="uv-swipe-action-item" ref="uv-swipe-action-item">
+ <view class="uv-swipe-action-item__right">
+ <slot name="button">
+ <view v-for="(item,index) in options" :key="index" class="uv-swipe-action-item__right__button"
+ :ref="`uv-swipe-action-item__right__button-${index}`" :style="[{
+ alignItems: item.style && item.style.borderRadius ? 'center' : 'stretch'
+ }]" @tap="buttonClickHandler(item, index)">
+ <view class="uv-swipe-action-item__right__button__wrapper" :style="[{
+ backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+ borderRadius: item.style && item.style.borderRadius ? item.style.borderRadius : '0',
+ padding: item.style && item.style.borderRadius ? '0' : '0 15px',
+ }, item.style]">
+ <uv-icon v-if="item.icon" :name="item.icon"
+ :color="item.style && item.style.color ? item.style.color : '#ffffff'"
+ :size="item.iconSize ? $uv.addUnit(item.iconSize) : item.style && item.style.fontSize ? $uv.getPx(item.style.fontSize) * 1.2 : 17"
+ :customStyle="{
+ marginRight: item.text ? '2px' : 0
+ }"></uv-icon>
+ <text v-if="item.text" class="uv-swipe-action-item__right__button__wrapper__text uv-line-1"
+ :style="[{
+ color: item.style && item.style.color ? item.style.color : '#ffffff',
+ fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px',
+ lineHeight: item.style && item.style.fontSize ? item.style.fontSize : '16px',
+ }]">{{ item.text }}</text>
+ </view>
+ </view>
+ </slot>
+ </view>
+ <!-- #ifndef APP-NVUE -->
+ <view class="uv-swipe-action-item__content" @touchstart="wxs.touchstart" @touchmove="wxs.touchmove"
+ @touchend="wxs.touchend" :status="status" :change:status="wxs.statusChange" :size="size"
+ :change:size="wxs.sizeChange">
+ <!-- #endif -->
+ <!-- #ifdef APP-NVUE -->
+ <view class="uv-swipe-action-item__content" ref="uv-swipe-action-item__content" @panstart="onTouchstart"
+ @tap="clickHandler">
+ <!-- #endif -->
+ <slot />
+ </view>
+ </view>
+</template>
+<!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ -->
+<script src="./index.wxs" module="wxs" lang="wxs"></script>
+<!-- #endif -->
+<script>
+ import touch from '@/uni_modules/uv-ui-tools/libs/mixin/touch.js'
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ import nvue from './nvue.js';
+ // #endif
+ // #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ
+ import wxs from './wxs.js';
+ // #endif
+ /**
+ * SwipeActionItem 滑动单元格子组件
+ * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作
+ * @tutorial https://www.uvui.cn/components/swipeAction.html
+ * @property {Boolean} show 控制打开或者关闭(默认 false )
+ * @property {String | Number} index 标识符,如果是v-for,可用index索引
+ * @property {Boolean} disabled 是否禁用(默认 false )
+ * @property {Boolean} autoClose 是否自动关闭其他swipe按钮组(默认 true )
+ * @property {Number} threshold 滑动距离阈值,只有大于此值,才被认为是要打开菜单(默认 30 )
+ * @property {Array} options 右侧按钮内容
+ * @property {String | Number} duration 动画过渡时间,单位ms(默认 350 )
+ * @event {Function(index)} open 组件打开时触发
+ * @event {Function(index)} close 组件关闭时触发
+ * @example <uv-swipe-action><uv-swipe-action-item :options="options1" ></uv-swipe-action-item></uv-swipe-action>
+ */
+ export default {
+ name: 'uv-swipe-action-item',
+ emits: ['click'],
+ // #ifndef APP-NVUE
+ mixins: [mpMixin, mixin, props, touch],
+ // #endif
+ // #ifdef APP-NVUE
+ mixins: [mpMixin, mixin, props, nvue , touch],
+ // #endif
+ // #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ
+ mixins: [mpMixin, mixin, props, touch, wxs],
+ // #endif
+ data() {
+ return {
+ // 按钮的尺寸信息
+ size: {},
+ // 父组件uv-swipe-action的参数
+ parentData: {
+ autoClose: true,
+ },
+ // 当前状态,open-打开,close-关闭
+ status: 'close',
+ }
+ },
+ watch: {
+ // 由于wxs无法直接读取外部的值,需要在外部值变化时,重新执行赋值逻辑
+ wxsInit(newValue, oldValue) {
+ this.queryRect()
+ }
+ },
+ computed: {
+ wxsInit() {
+ return [this.disabled, this.autoClose, this.threshold, this.options, this.duration]
+ }
+ },
+ mounted() {
+ // #ifdef MP-TOUTIAO
+ this.$uv.error('抖音小程序暂不支持wxs,故该组件暂不支持抖音小程序');
+ // #endif
+ this.init()
+ },
+ methods: {
+ init() {
+ // 初始化父组件数据
+ this.updateParentData()
+ // #ifndef APP-NVUE
+ this.$uv.sleep().then(() => {
+ this.queryRect()
+ })
+ // #endif
+ },
+ updateParentData() {
+ // 此方法在mixin中
+ this.getParentData('uv-swipe-action')
+ },
+ // #ifndef APP-NVUE
+ // 查询节点
+ queryRect() {
+ this.$uvGetRect('.uv-swipe-action-item__right__button', true).then(buttons => {
+ this.size = {
+ buttons,
+ show: this.show,
+ disabled: this.disabled,
+ threshold: this.threshold,
+ duration: this.duration
+ }
+ })
+ },
+ // #endif
+ // 按钮被点击
+ buttonClickHandler(item, index) {
+ this.$emit('click', {
+ index,
+ name: this.name
+ })
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-lines: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-swipe-action-item {
+ position: relative;
+ overflow: hidden;
+ /* #ifndef APP-NVUE || MP-WEIXIN */
+ touch-action: pan-y;
+ /* #endif */
+
+ &__content {
+ background-color: #FFFFFF;
+ z-index: 10;
+ }
+
+ &__right {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ @include flex;
+
+ &__button {
+ @include flex;
+ justify-content: center;
+ overflow: hidden;
+ align-items: center;
+
+ &__wrapper {
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0 15px;
+
+ &__text {
+ @include flex;
+ align-items: center;
+ color: #FFFFFF;
+ font-size: 15px;
+ text-align: center;
+ justify-content: center;
+ }
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js
new file mode 100644
index 0000000..ee49c10
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js
@@ -0,0 +1,15 @@
+export default {
+ methods: {
+ // 关闭时执行
+ closeHandler() {
+ this.status = 'close'
+ },
+ setState(status) {
+ this.status = status
+ },
+ closeOther() {
+ // 尝试关闭其他打开的单元格
+ this.parent && this.parent.closeOther(this)
+ }
+ }
+}
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action/props.js b/uni_modules/uv-swipe-action/components/uv-swipe-action/props.js
new file mode 100644
index 0000000..7d06765
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action/props.js
@@ -0,0 +1,10 @@
+export default {
+ props: {
+ // 是否自动关闭其他swipe按钮组
+ autoClose: {
+ type: Boolean,
+ default: true
+ },
+ ...uni.$uv?.props?.swipeAction
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue b/uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue
new file mode 100644
index 0000000..ed3e9f9
--- /dev/null
+++ b/uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue
@@ -0,0 +1,65 @@
+<template>
+ <view class="uv-swipe-action">
+ <slot></slot>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * SwipeAction 滑动单元格
+ * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作
+ * @tutorial https://www.uvui.cn/components/swipeAction.html
+ * @property {Boolean} autoClose 是否自动关闭其他swipe按钮组
+ * @event {Function(index)} click 点击组件时触发
+ * @example <uv-swipe-action><uv-swipe-action-item :rightOptions="options1" ></uv-swipe-action-item></uv-swipe-action>
+ */
+ export default {
+ name: 'uv-swipe-action',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {}
+ },
+ provide() {
+ return {
+ swipeAction: this
+ }
+ },
+ computed: {
+ // 这里computed的变量,都是子组件uv-swipe-action-item需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化
+ // 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(uv-swipe-action-item)
+ // 拉取父组件新的变化后的参数
+ parentData() {
+ return [this.autoClose]
+ }
+ },
+ watch: {
+ // 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+ parentData() {
+ if (this.children.length) {
+ this.children.map(child => {
+ // 判断子组件(uv-swipe-action-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+ typeof(child.updateParentData) === 'function' && child.updateParentData()
+ })
+ }
+ },
+ },
+ created() {
+ this.children = []
+ },
+ methods: {
+ closeOther(child) {
+ if (this.autoClose) {
+ // 历遍所有的单元格,找出非当前操作中的单元格,进行关闭
+ this.children.map((item, index) => {
+ if (child !== item) {
+ item.closeHandler()
+ }
+ })
+ }
+ }
+ }
+ }
+</script>
diff --git a/uni_modules/uv-swipe-action/package.json b/uni_modules/uv-swipe-action/package.json
new file mode 100644
index 0000000..9eef69b
--- /dev/null
+++ b/uni_modules/uv-swipe-action/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-swipe-action",
+ "displayName": "uv-swipe-action 滑动单元格 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.4",
+ "description": "滑动单元格组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作。",
+ "keywords": [
+ "uv-swipe-action",
+ "uvui",
+ "uv-ui",
+ "swipe-action",
+ "滑动单元格"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-swipe-action/readme.md b/uni_modules/uv-swipe-action/readme.md
new file mode 100644
index 0000000..3180696
--- /dev/null
+++ b/uni_modules/uv-swipe-action/readme.md
@@ -0,0 +1,11 @@
+## SwipeAction 滑动单元格
+
+> **组件名:uv-swipe-action**
+
+该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作。
+
+### <a href="https://www.uvui.cn/components/swipeAction.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-swiper/changelog.md b/uni_modules/uv-swiper/changelog.md
new file mode 100644
index 0000000..30f26c6
--- /dev/null
+++ b/uni_modules/uv-swiper/changelog.md
@@ -0,0 +1,16 @@
+## 1.0.6(2023-12-20)
+1. 优化
+## 1.0.5(2023-08-24)
+1. 修复标题文字过多未隐藏掉的BUG
+## 1.0.4(2023-07-24)
+1. 增加 滑动方向是否为纵向 属性vertical
+## 1.0.3(2023-06-27)
+1. 增加titleStyle属性,方便修改标题样式
+2. 标题上去掉是否是图片的判断,避免无后缀的图片不显示
+## 1.0.2(2023-06-01)
+1. 修复点击触发两次事件的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-swiper 轮播图,走马灯
diff --git a/uni_modules/uv-swiper/components/uv-swiper-indicator/props.js b/uni_modules/uv-swiper/components/uv-swiper-indicator/props.js
new file mode 100644
index 0000000..8f305a0
--- /dev/null
+++ b/uni_modules/uv-swiper/components/uv-swiper-indicator/props.js
@@ -0,0 +1,30 @@
+export default {
+ props: {
+ // 轮播的长度
+ length: {
+ type: [String, Number],
+ default: 0
+ },
+ // 当前处于活动状态的轮播的索引
+ current: {
+ type: [String, Number],
+ default: 0
+ },
+ // 指示器非激活颜色
+ indicatorActiveColor: {
+ type: String,
+ default: ''
+ },
+ // 指示器的激活颜色
+ indicatorInactiveColor: {
+ type: String,
+ default: ''
+ },
+ // 指示器模式,line-线型,dot-点型
+ indicatorMode: {
+ type: String,
+ default: ''
+ },
+ ...uni.$uv?.props?.swiperIndicator
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue b/uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue
new file mode 100644
index 0000000..b326f43
--- /dev/null
+++ b/uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue
@@ -0,0 +1,112 @@
+<template>
+ <view class="uv-swiper-indicator">
+ <view
+ class="uv-swiper-indicator__wrapper"
+ v-if="indicatorMode === 'line'"
+ :class="[`uv-swiper-indicator__wrapper--${indicatorMode}`]"
+ :style="{
+ width: $uv.addUnit(lineWidth * length),
+ backgroundColor: indicatorInactiveColor
+ }"
+ >
+ <view
+ class="uv-swiper-indicator__wrapper--line__bar"
+ :style="[lineStyle]"
+ ></view>
+ </view>
+ <view
+ class="uv-swiper-indicator__wrapper"
+ v-if="indicatorMode === 'dot'"
+ >
+ <view
+ class="uv-swiper-indicator__wrapper__dot"
+ v-for="(item, index) in length"
+ :key="index"
+ :class="[index === current && 'uv-swiper-indicator__wrapper__dot--active']"
+ :style="[dotStyle(index)]"
+ >
+
+ </view>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * SwiperIndicator 轮播图指示器
+ * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
+ * @tutorial https://www.uvui.cn/components/swiper.html
+ * @property {String | Number} length 轮播的长度(默认 0 )
+ * @property {String | Number} current 当前处于活动状态的轮播的索引(默认 0 )
+ * @property {String} indicatorActiveColor 指示器非激活颜色
+ * @property {String} indicatorInactiveColor 指示器的激活颜色
+ * @property {String} indicatorMode 指示器模式(默认 'line' )
+ * @example <uv-swiper :list="list4" indicator keyName="url" :autoplay="false"></uv-swiper>
+ */
+ export default {
+ name: 'uv-swiper-indicator',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ lineWidth: 22
+ }
+ },
+ computed: {
+ // 指示器为线型的样式
+ lineStyle() {
+ let style = {}
+ style.width = this.$uv.addUnit(this.lineWidth)
+ style.transform = `translateX(${ this.$uv.addUnit(this.current * this.lineWidth) })`
+ style.backgroundColor = this.indicatorActiveColor
+ return style
+ },
+ // 指示器为点型的样式
+ dotStyle() {
+ return index => {
+ let style = {}
+ style.backgroundColor = index === this.current ? this.indicatorActiveColor : this.indicatorInactiveColor
+ return style
+ }
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-swiper-indicator {
+
+ &__wrapper {
+ @include flex;
+
+ &--line {
+ border-radius: 100px;
+ height: 4px;
+
+ &__bar {
+ width: 22px;
+ height: 4px;
+ border-radius: 100px;
+ background-color: #FFFFFF;
+ transition: transform 0.3s;
+ }
+ }
+
+ &__dot {
+ width: 5px;
+ height: 5px;
+ border-radius: 100px;
+ margin: 0 4px;
+
+ &--active {
+ width: 12px;
+ }
+ }
+
+ }
+ }
+</style>
diff --git a/uni_modules/uv-swiper/components/uv-swiper/props.js b/uni_modules/uv-swiper/components/uv-swiper/props.js
new file mode 100644
index 0000000..66e4787
--- /dev/null
+++ b/uni_modules/uv-swiper/components/uv-swiper/props.js
@@ -0,0 +1,136 @@
+export default {
+ props: {
+ // 列表数组,元素可为字符串,如为对象可通过keyName指定目标属性名
+ list: {
+ type: Array,
+ default: () => []
+ },
+ // 是否显示面板指示器
+ indicator: {
+ type: Boolean,
+ default: false
+ },
+ // 指示器非激活颜色
+ indicatorActiveColor: {
+ type: String,
+ default: '#fff'
+ },
+ // 指示器的激活颜色
+ indicatorInactiveColor: {
+ type: String,
+ default: 'rgba(255, 255, 255, 0.35)'
+ },
+ // 指示器样式,可通过bottom,left,right进行定位
+ indicatorStyle: {
+ type: [String, Object],
+ default: ''
+ },
+ // 指示器模式,line-线型,dot-点型
+ indicatorMode: {
+ type: String,
+ default: 'line'
+ },
+ // 是否自动切换
+ autoplay: {
+ type: Boolean,
+ default: true
+ },
+ // 当前所在滑块的 index
+ current: {
+ type: [String, Number],
+ default: 0
+ },
+ // 当前所在滑块的 item-id ,不能与 current 被同时指定
+ currentItemId: {
+ type: String,
+ default: ''
+ },
+ // 滑块自动切换时间间隔
+ interval: {
+ type: [String, Number],
+ default: 3000
+ },
+ // 滑块切换过程所需时间
+ duration: {
+ type: [String, Number],
+ default: 300
+ },
+ // 播放到末尾后是否重新回到开头
+ circular: {
+ type: Boolean,
+ default: false
+ },
+ // 滑动方向是否为纵向
+ vertical: {
+ type: Boolean,
+ default: false
+ },
+ // 前边距,可用于露出前一项的一小部分,nvue和支付宝不支持
+ previousMargin: {
+ type: [String, Number],
+ default: 0
+ },
+ // 后边距,可用于露出后一项的一小部分,nvue和支付宝不支持
+ nextMargin: {
+ type: [String, Number],
+ default: 0
+ },
+ // 当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持
+ acceleration: {
+ type: Boolean,
+ default: false
+ },
+ // 同时显示的滑块数量,nvue、支付宝小程序不支持
+ displayMultipleItems: {
+ type: Number,
+ default: 1
+ },
+ // 指定swiper切换缓动动画类型,有效值:default、linear、easeInCubic、easeOutCubic、easeInOutCubic
+ // 只对微信小程序有效
+ easingFunction: {
+ type: String,
+ default: 'default'
+ },
+ // list数组中指定对象的目标属性名
+ keyName: {
+ type: String,
+ default: 'url'
+ },
+ // 图片的裁剪模式
+ imgMode: {
+ type: String,
+ default: 'aspectFill'
+ },
+ // 组件高度
+ height: {
+ type: [String, Number],
+ default: 130
+ },
+ // 背景颜色
+ bgColor: {
+ type: String,
+ default: '#f3f4f6'
+ },
+ // 组件圆角,数值或带单位的字符串
+ radius: {
+ type: [String, Number],
+ default: 4
+ },
+ // 是否加载中
+ loading: {
+ type: Boolean,
+ default: false
+ },
+ // 是否显示标题,要求数组对象中有title属性
+ showTitle: {
+ type: Boolean,
+ default: false
+ },
+ // 显示的标题样式
+ titleStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ ...uni.$uv?.props?.swiper
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue b/uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue
new file mode 100644
index 0000000..f897ff9
--- /dev/null
+++ b/uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue
@@ -0,0 +1,263 @@
+<template>
+ <view
+ class="uv-swiper"
+ :style="{
+ backgroundColor: bgColor,
+ height: $uv.addUnit(height),
+ borderRadius: $uv.addUnit(radius)
+ }"
+ >
+ <view
+ class="uv-swiper__loading"
+ v-if="loading"
+ >
+ <uv-loading-icon mode="circle"></uv-loading-icon>
+ </view>
+ <swiper
+ v-else
+ class="uv-swiper__wrapper"
+ :style="{
+ height: $uv.addUnit(height),
+ flex: 1
+ }"
+ @change="change"
+ :circular="circular"
+ :vertical="vertical"
+ :interval="interval"
+ :duration="duration"
+ :autoplay="autoplay"
+ :current="current"
+ :currentItemId="currentItemId"
+ :previousMargin="$uv.addUnit(previousMargin)"
+ :nextMargin="$uv.addUnit(nextMargin)"
+ :acceleration="acceleration"
+ :displayMultipleItems="displayMultipleItems"
+ :easingFunction="easingFunction"
+ >
+ <swiper-item
+ class="uv-swiper__wrapper__item"
+ v-for="(item, index) in list"
+ :key="index"
+ >
+ <view
+ class="uv-swiper__wrapper__item__wrapper"
+ :style="[itemStyle(index)]"
+ >
+ <!-- 在nvue中,image图片的宽度默认为屏幕宽度,需要通过flex:1撑开,另外必须设置高度才能显示图片 -->
+ <image
+ class="uv-swiper__wrapper__item__wrapper__image"
+ v-if="getItemType(item) === 'image'"
+ :src="getSource(item)"
+ :mode="imgMode"
+ @tap="clickHandler(index)"
+ :style="{
+ height: $uv.addUnit(height),
+ borderRadius: $uv.addUnit(radius)
+ }"
+ ></image>
+ <video
+ class="uv-swiper__wrapper__item__wrapper__video"
+ v-if="getItemType(item) === 'video'"
+ :id="`video-${index}`"
+ :enable-progress-gesture="false"
+ :src="getSource(item)"
+ :poster="getPoster(item)"
+ :title="showTitle && $uv.test.object(item) && item.title ? item.title : ''"
+ :style="{
+ height: $uv.addUnit(height)
+ }"
+ controls
+ @tap="clickHandler(index)"
+ ></video>
+ <text
+ v-if="showTitle && $uv.test.object(item) && item.title"
+ class="uv-swiper__wrapper__item__wrapper__title uv-line-1"
+ :style="[$uv.addStyle(titleStyle)]"
+ >{{ item.title }}</text>
+ </view>
+ </swiper-item>
+ </swiper>
+ <view class="uv-swiper__indicator" :style="[$uv.addStyle(indicatorStyle)]">
+ <slot name="indicator">
+ <uv-swiper-indicator
+ v-if="!loading && indicator && !showTitle"
+ :indicatorActiveColor="indicatorActiveColor"
+ :indicatorInactiveColor="indicatorInactiveColor"
+ :length="list.length"
+ :current="currentIndex"
+ :indicatorMode="indicatorMode"
+ ></uv-swiper-indicator>
+ </slot>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Swiper 轮播图
+ * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
+ * @tutorial https://www.uvui.cn/components/swiper.html
+ * @property {Array} list 轮播图数据
+ * @property {Boolean} indicator 是否显示面板指示器(默认 false )
+ * @property {String} indicatorActiveColor 指示器非激活颜色(默认 '#FFFFFF' )
+ * @property {String} indicatorInactiveColor 指示器的激活颜色(默认 'rgba(255, 255, 255, 0.35)' )
+ * @property {String | Object} indicatorStyle 指示器样式,可通过bottom,left,right进行定位
+ * @property {String} indicatorMode 指示器模式(默认 'line' )
+ * @property {Boolean} autoplay 是否自动切换(默认 true )
+ * @property {String | Number} current 当前所在滑块的 index(默认 0 )
+ * @property {String} currentItemId 当前所在滑块的 item-id ,不能与 current 被同时指定
+ * @property {String | Number} interval 滑块自动切换时间间隔(ms)(默认 3000 )
+ * @property {String | Number} duration 滑块切换过程所需时间(ms)(默认 300 )
+ * @property {Boolean} circular 播放到末尾后是否重新回到开头(默认 false )
+ * @property {String | Number} previousMargin 前边距,可用于露出前一项的一小部分,nvue和支付宝不支持(默认 0 )
+ * @property {String | Number} nextMargin 后边距,可用于露出后一项的一小部分,nvue和支付宝不支持(默认 0 )
+ * @property {Boolean} acceleration 当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持(默认 false )
+ * @property {Number} displayMultipleItems 同时显示的滑块数量,nvue、支付宝小程序不支持(默认 1 )
+ * @property {String} easingFunction 指定swiper切换缓动动画类型, 只对微信小程序有效(默认 'default' )
+ * @property {String} keyName list数组中指定对象的目标属性名(默认 'url' )
+ * @property {String} imgMode 图片的裁剪模式(默认 'aspectFill' )
+ * @property {String | Number} height 组件高度(默认 130 )
+ * @property {String} bgColor 背景颜色(默认 '#f3f4f6' )
+ * @property {String | Number} radius 组件圆角,数值或带单位的字符串(默认 4 )
+ * @property {Boolean} loading 是否加载中(默认 false )
+ * @property {Boolean} showTitle 是否显示标题,要求数组对象中有title属性(默认 false )
+ * @event {Function(index)} click 点击轮播图时触发 index:点击了第几张图片,从0开始
+ * @event {Function(index)} change 轮播图切换时触发(自动或者手动切换) index:切换到了第几张图片,从0开始
+ * @example <uv-swiper :list="list4" keyName="url" :autoplay="false"></uv-swiper>
+ */
+ export default {
+ name: 'uv-swiper',
+ mixins: [mpMixin, mixin, props],
+ emits: ['click','change'],
+ data() {
+ return {
+ currentIndex: 0
+ }
+ },
+ watch: {
+ current(val, preVal) {
+ if(val === preVal) return;
+ this.currentIndex = val; // 和上游数据关联上
+ }
+ },
+ computed: {
+ itemStyle() {
+ return index => {
+ const style = {}
+ // #ifndef APP-NVUE || MP-TOUTIAO
+ // 左右流出空间的写法不支持nvue和头条
+ // 只有配置了此二值,才加上对应的圆角,以及缩放
+ if (this.nextMargin && this.previousMargin) {
+ style.borderRadius = this.$uv.addUnit(this.radius)
+ if (index !== this.currentIndex) style.transform = 'scale(0.92)'
+ }
+ // #endif
+ return style
+ }
+ }
+ },
+ methods: {
+ getItemType(item) {
+ if (typeof item === 'string') return this.$uv.test.video(this.getSource(item)) ? 'video' : 'image'
+ if (typeof item === 'object' && this.keyName) {
+ if (!item.type) return this.$uv.test.video(this.getSource(item)) ? 'video' : 'image'
+ if (item.type === 'image') return 'image'
+ if (item.type === 'video') return 'video'
+ return 'image'
+ }
+ },
+ // 获取目标路径,可能数组中为字符串,对象的形式,额外可指定对象的目标属性名keyName
+ getSource(item) {
+ if (typeof item === 'string') return item
+ if (typeof item === 'object' && this.keyName) return item[this.keyName]
+ else this.$uv.error('请按格式传递列表参数')
+ return ''
+ },
+ // 轮播切换事件
+ change(e) {
+ // 当前的激活索引
+ const {
+ current
+ } = e.detail
+ this.pauseVideo(this.currentIndex)
+ this.currentIndex = current
+ this.$emit('change', e.detail)
+ },
+ // 切换轮播时,暂停视频播放
+ pauseVideo(index) {
+ const lastItem = this.getSource(this.list[index])
+ if (this.$uv.test.video(lastItem)) {
+ // 当视频隐藏时,暂停播放
+ const video = uni.createVideoContext(`video-${index}`, this)
+ video.pause()
+ }
+ },
+ // 当一个轮播item为视频时,获取它的视频海报
+ getPoster(item) {
+ return typeof item === 'object' && item.poster ? item.poster : ''
+ },
+ // 点击某个item
+ clickHandler(index) {
+ this.$emit('click', index)
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-lines: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-swiper {
+ @include flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+ overflow: hidden;
+
+ &__wrapper {
+ flex: 1;
+
+ &__item {
+ flex: 1;
+
+ &__wrapper {
+ @include flex;
+ position: relative;
+ overflow: hidden;
+ transition: transform 0.3s;
+ flex: 1;
+
+ &__image {
+ flex: 1;
+ }
+
+ &__video {
+ flex: 1;
+ }
+
+ &__title {
+ position: absolute;
+ background-color: rgba(0, 0, 0, 0.3);
+ bottom: 0;
+ left: 0;
+ right: 0;
+ font-size: 28rpx;
+ height: 60rpx;
+ line-height: 60rpx;
+ color: #FFFFFF;
+ flex: 1;
+ }
+ }
+ }
+ }
+
+ &__indicator {
+ position: absolute;
+ bottom: 10px;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-swiper/package.json b/uni_modules/uv-swiper/package.json
new file mode 100644
index 0000000..f561f10
--- /dev/null
+++ b/uni_modules/uv-swiper/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-swiper",
+ "displayName": "uv-swiper 轮播图 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.6",
+ "description": "该组件为轮播、跑马灯,支持卡片式。一般用于导航轮播,广告展示等场景,开箱即用",
+ "keywords": [
+ "swiper",
+ "uvui",
+ "uv-ui",
+ "轮播图",
+ "走马灯"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-loading-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-swiper/readme.md b/uni_modules/uv-swiper/readme.md
new file mode 100644
index 0000000..cae255f
--- /dev/null
+++ b/uni_modules/uv-swiper/readme.md
@@ -0,0 +1,19 @@
+## Swiper 轮播图
+
+> **组件名:uv-swiper**
+
+该组件为轮播、跑马灯,支持卡片式。一般用于导航轮播,广告展示等场景,开箱即用。
+
+# <a href="https://www.uvui.cn/components/swiper.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-switch/changelog.md b/uni_modules/uv-switch/changelog.md
new file mode 100644
index 0000000..655c0ae
--- /dev/null
+++ b/uni_modules/uv-switch/changelog.md
@@ -0,0 +1,14 @@
+## 1.0.5(2023-09-21)
+1. 优化细节
+## 1.0.4(2023-08-23)
+1. 取消value传值,只能使用v-model传值,避免异步操作不生效的BUG
+## 1.0.3(2023-07-13)
+1. 修复 uv-switch设置value属性不生效的BUG
+## 1.0.2(2023-06-19)
+1. size属性兼容和单位一起使用
+
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-switch 开关选择器
diff --git a/uni_modules/uv-switch/components/uv-switch/props.js b/uni_modules/uv-switch/components/uv-switch/props.js
new file mode 100644
index 0000000..b3737dd
--- /dev/null
+++ b/uni_modules/uv-switch/components/uv-switch/props.js
@@ -0,0 +1,58 @@
+export default {
+ props: {
+ value: {
+ type: [Boolean, String, Number],
+ default: false
+ },
+ modelValue: {
+ type: [Boolean, String, Number],
+ default: false
+ },
+ // 是否为加载中状态
+ loading: {
+ type: Boolean,
+ default: false
+ },
+ // 是否为禁用装填
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 开关尺寸,单位px
+ size: {
+ type: [String, Number],
+ default: 25
+ },
+ // 打开时的背景颜色
+ activeColor: {
+ type: String,
+ default: '#2979ff'
+ },
+ // 关闭时的背景颜色
+ inactiveColor: {
+ type: String,
+ default: '#fff'
+ },
+ // switch打开时的值
+ activeValue: {
+ type: [String, Number, Boolean],
+ default: true
+ },
+ // switch关闭时的值
+ inactiveValue: {
+ type: [String, Number, Boolean],
+ default: false
+ },
+ // 是否开启异步变更,开启后需要手动控制输入值
+ asyncChange: {
+ type: Boolean,
+ default: false
+ },
+ // 圆点与外边框的距离
+ space: {
+ type: [String, Number],
+ default: 0
+ },
+ ...uni.$uv?.props?.switch
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-switch/components/uv-switch/uv-switch.vue b/uni_modules/uv-switch/components/uv-switch/uv-switch.vue
new file mode 100644
index 0000000..884963a
--- /dev/null
+++ b/uni_modules/uv-switch/components/uv-switch/uv-switch.vue
@@ -0,0 +1,192 @@
+<template>
+ <view
+ class="uv-switch"
+ :class="[disabled && 'uv-switch--disabled']"
+ :style="[switchStyle, $uv.addStyle(customStyle)]"
+ @tap="clickHandler"
+ >
+ <view
+ class="uv-switch__bg"
+ :style="[bgStyle]"
+ >
+ </view>
+ <view
+ class="uv-switch__node"
+ :class="[innerValue && 'uv-switch__node--on']"
+ :style="[nodeStyle]"
+ ref="uv-switch__node"
+ >
+ <uv-loading-icon
+ :show="loading"
+ mode="circle"
+ timingFunction='linear'
+ :color="innerValue ? activeColor : '#AAABAD'"
+ :size="size * 0.6"
+ />
+ </view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * switch 开关选择器
+ * @description 选择开关一般用于只有两个选择,且只能选其一的场景。
+ * @tutorial https://www.uvui.cn/components/switch.html
+ * @property {Boolean} loading 是否处于加载中(默认 false )
+ * @property {Boolean} disabled 是否禁用(默认 false )
+ * @property {String | Number} size 开关尺寸,单位px (默认 25 )
+ * @property {String} activeColor 打开时的背景色 (默认 '#2979ff' )
+ * @property {String} inactiveColor 关闭时的背景色 (默认 '#ffffff' )
+ * @property {Boolean | String | Number} value 通过v-model双向绑定的值 (默认 false )
+ * @property {Boolean | String | Number} activeValue 打开选择器时通过change事件发出的值 (默认 true )
+ * @property {Boolean | String | Number} inactiveValue 关闭选择器时通过change事件发出的值 (默认 false )
+ * @property {Boolean} asyncChange 是否开启异步变更,开启后需要手动控制输入值 (默认 false )
+ * @property {String | Number} space 圆点与外边框的距离 (默认 0 )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function} change 在switch打开或关闭时触发
+ * @example <uv-switch v-model="checked" active-color="red" inactive-color="#eee"></uv-switch>
+ */
+ export default {
+ name: "uv-switch",
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ bgColor: '#ffffff',
+ innerValue: false
+ }
+ },
+ watch: {
+ // #ifdef VUE2
+ value: {
+ immediate: true,
+ handler(newVal) {
+ if (newVal !== this.inactiveValue && newVal !== this.activeValue) {
+ return this.$uv.error('v-model绑定的值必须为inactiveValue、activeValue二者之一')
+ }
+ this.innerValue = newVal;
+ }
+ },
+ // #endif
+ // #ifndef VUE2
+ modelValue: {
+ immediate: true,
+ handler(newVal) {
+ if (newVal !== this.inactiveValue && newVal !== this.activeValue) {
+ return this.$uv.error('v-model绑定的值必须为inactiveValue、activeValue二者之一')
+ }
+ this.innerValue = newVal;
+ }
+ }
+ // #endif
+ },
+ created() {
+ this.innerValue = this.value || this.modelValue;
+ },
+ computed: {
+ isActive() {
+ return this.innerValue === this.activeValue;
+ },
+ switchStyle() {
+ let style = {}
+ // 这里需要加2,是为了腾出边框的距离,否则圆点node会和外边框紧贴在一起
+ style.width = this.$uv.addUnit(this.$uv.getPx(this.size) * 2 + 2)
+ style.height = this.$uv.addUnit(this.$uv.getPx(this.size) + 2)
+ // 如果自定义了“非激活”演示,name边框颜色设置为透明(跟非激活颜色一致)
+ // 这里不能简单的设置为非激活的颜色,否则打开状态时,会有边框,所以需要透明
+ if (this.customInactiveColor) {
+ style.borderColor = 'rgba(0, 0, 0, 0)'
+ }
+ style.backgroundColor = this.isActive ? this.activeColor : this.inactiveColor
+ return style;
+ },
+ nodeStyle() {
+ let style = {}
+ // 如果自定义非激活颜色,将node圆点的尺寸减少两个像素,让其与外边框距离更大一点
+ style.width = this.$uv.addUnit(this.$uv.getPx(this.size) - this.space)
+ style.height = this.$uv.addUnit(this.$uv.getPx(this.size) - this.space)
+ const translateX = this.isActive ? this.$uv.addUnit(this.space) : this.$uv.addUnit(this.$uv.getPx(this.size));
+ style.transform = `translateX(-${translateX})`
+ return style
+ },
+ bgStyle() {
+ let style = {}
+ // 这里配置一个多余的元素在HTML中,是为了让switch切换时,有更良好的背景色扩充体验(见实际效果)
+ style.width = this.$uv.addUnit(this.$uv.getPx(this.size) * 2 - this.$uv.getPx(this.size) / 2)
+ style.height = this.$uv.addUnit(this.$uv.getPx(this.size))
+ style.backgroundColor = this.inactiveColor
+ // 打开时,让此元素收缩,否则反之
+ style.transform = `scale(${this.isActive ? 0 : 1})`
+ return style
+ },
+ customInactiveColor() {
+ // 之所以需要判断是否自定义了“非激活”颜色,是为了让node圆点离外边框更宽一点的距离
+ return this.inactiveColor !== '#fff' && this.inactiveColor !== '#ffffff'
+ }
+ },
+ methods: {
+ clickHandler() {
+ if (!this.disabled && !this.loading) {
+ const oldValue = this.isActive ? this.inactiveValue : this.activeValue
+ if (!this.asyncChange) {
+ this.$emit('input', oldValue)
+ this.$emit('update:modelValue', oldValue)
+ }
+ // 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
+ this.$nextTick(() => {
+ this.$emit('change', oldValue)
+ })
+ }
+ }
+ }
+ };
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-switch {
+ @include flex(row);
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+ position: relative;
+ background-color: #fff;
+ border-width: 1px;
+ border-radius: 100px;
+ transition: background-color 0.4s;
+ border-color: rgba(0, 0, 0, 0.12);
+ border-style: solid;
+ justify-content: flex-end;
+ align-items: center;
+ // 由于weex为阿里逗着玩的KPI项目,导致bug奇多,这必须要写这一行,
+ // 否则在iOS上,点击页面任意地方,都会触发switch的点击事件
+ overflow: hidden;
+ &__node {
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+ border-radius: 100px;
+ background-color: #fff;
+ border-radius: 100px;
+ box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, 0.25);
+ transition-property: transform;
+ transition-duration: 0.4s;
+ transition-timing-function: cubic-bezier(0.3, 1.05, 0.4, 1.05);
+ }
+ &__bg {
+ position: absolute;
+ border-radius: 100px;
+ background-color: #FFFFFF;
+ transition-property: transform;
+ transition-duration: 0.4s;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ transition-timing-function: ease;
+ }
+ &--disabled {
+ opacity: 0.6;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-switch/package.json b/uni_modules/uv-switch/package.json
new file mode 100644
index 0000000..2b9c1dd
--- /dev/null
+++ b/uni_modules/uv-switch/package.json
@@ -0,0 +1,88 @@
+{
+ "id": "uv-switch",
+ "displayName": "uv-switch 开关选择器 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "选择开关用于在打开和关闭状态之间进行切换,支持异步操作。",
+ "keywords": [
+ "uv-switch",
+ "uvui",
+ "uv-ui",
+ "switch",
+ "开关选择器"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-loading-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-switch/readme.md b/uni_modules/uv-switch/readme.md
new file mode 100644
index 0000000..7dbe8ed
--- /dev/null
+++ b/uni_modules/uv-switch/readme.md
@@ -0,0 +1,19 @@
+## Switch 开关选择器
+
+> **组件名:uv-switch**
+
+选择开关用于在打开和关闭状态之间进行切换,支持异步操作。
+
+# <a href="https://www.uvui.cn/components/switch.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-tabbar/changelog.md b/uni_modules/uv-tabbar/changelog.md
new file mode 100644
index 0000000..a3ed25c
--- /dev/null
+++ b/uni_modules/uv-tabbar/changelog.md
@@ -0,0 +1,14 @@
+## 1.0.5(2023-07-17)
+1. 优化文档
+2. 优化其他
+## 1.0.4(2023-06-13)
+1. 底部安全距离依赖添加
+## 1.0.3(2023-06-09)
+1. 增加iconSize参数
+## 1.0.2(2023-06-01)
+1. 修复点击触发两次事件的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-tabbar 底部导航栏
diff --git a/uni_modules/uv-tabbar/components/uv-tabbar-item/props.js b/uni_modules/uv-tabbar/components/uv-tabbar-item/props.js
new file mode 100644
index 0000000..e67922a
--- /dev/null
+++ b/uni_modules/uv-tabbar/components/uv-tabbar-item/props.js
@@ -0,0 +1,40 @@
+export default {
+ props: {
+ // item标签的名称,作为与uv-tabbar的value参数匹配的标识符
+ name: {
+ type: [String, Number, null],
+ default: null
+ },
+ // uv-ui内置图标或者绝对路径的图片
+ icon: {
+ icon: String,
+ default: ''
+ },
+ // 图标大小,默认uv-tabbar的iconSize=20
+ iconSize: {
+ type: [String, Number],
+ default: ''
+ },
+ // 右上角的角标提示信息
+ badge: {
+ type: [String, Number, null],
+ default: null
+ },
+ // 是否显示圆点,将会覆盖badge参数
+ dot: {
+ type: Boolean,
+ default: false
+ },
+ // 描述文本
+ text: {
+ type: String,
+ default: ''
+ },
+ // 控制徽标的位置,对象或者字符串形式,可以设置top和right属性
+ badgeStyle: {
+ type: [Object, String],
+ default: 'top: 6px;right:2px;'
+ },
+ ...uni.$uv?.props?.tabbarItem
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue b/uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue
new file mode 100644
index 0000000..c6fa65e
--- /dev/null
+++ b/uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue
@@ -0,0 +1,146 @@
+<template>
+ <view
+ class="uv-tabbar-item"
+ :style="[$uv.addStyle(customStyle)]"
+ @tap="clickHandler"
+ >
+ <view class="uv-tabbar-item__icon">
+ <uv-icon
+ v-if="icon"
+ :name="icon"
+ :color="isActive? parentData.activeColor : parentData.inactiveColor"
+ :size="iconSize? iconSize: parentData.iconSize"
+ ></uv-icon>
+ <template v-else>
+ <slot
+ v-if="isActive"
+ name="active-icon"
+ />
+ <slot
+ v-else
+ name="inactive-icon"
+ />
+ </template>
+ <uv-badge
+ absolute
+ :offset="[0, dot ? '34rpx' : badge > 9 ? '14rpx' : '20rpx']"
+ :customStyle="badgeStyle"
+ :isDot="dot"
+ :value="badge || (dot ? 1 : null)"
+ :show="dot || badge > 0"
+ ></uv-badge>
+ </view>
+
+ <slot name="text">
+ <text
+ class="uv-tabbar-item__text"
+ :style="{
+ color: isActive? parentData.activeColor : parentData.inactiveColor
+ }"
+ >{{ text }}</text>
+ </slot>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * TabbarItem 底部导航栏子组件
+ * @description 此组件提供了自定义tabbar的能力。
+ * @tutorial https://www.uvui.cn/components/tabbar.html
+ * @property {String | Number} name item标签的名称,作为与uv-tabbar的value参数匹配的标识符
+ * @property {String} icon uvui内置图标或者绝对路径的图片
+ * @property {String | Number} badge 右上角的角标提示信息
+ * @property {Boolean} dot 是否显示圆点,将会覆盖badge参数(默认 false )
+ * @property {String} text 描述文本
+ * @property {Object | String} badgeStyle 控制徽标的位置,对象或者字符串形式,可以设置top和right属性(默认 'top: 6px;right:2px;' )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @example <uv-tabbar :value="value2" :placeholder="false" @change="name => value2 = name" :fixed="false" :safeAreaInsetBottom="false"><uv-tabbar-item text="首页" icon="home" dot ></uv-tabbar-item></uv-tabbar>
+ */
+ export default {
+ name: 'uv-tabbar-item',
+ mixins: [mpMixin, mixin, props],
+ emits: ['click','change'],
+ data() {
+ return {
+ isActive: false, // 是否处于激活状态
+ parentData: {
+ value: null,
+ activeColor: '',
+ inactiveColor: '',
+ iconSize: 20
+ }
+ }
+ },
+ created() {
+ this.init()
+ },
+ methods: {
+ init() {
+ // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
+ this.updateParentData()
+ if (!this.parent) {
+ this.$uv.error('uv-tabbar-item必须搭配uv-tabbar组件使用')
+ }
+ // 本子组件在uv-tabbar的children数组中的索引
+ const index = this.parent.children.indexOf(this)
+ // 判断本组件的name(如果没有定义name,就用index索引)是否等于父组件的value参数
+ this.isActive = (this.name || index) === this.parentData.value
+ },
+ updateParentData() {
+ // 此方法在mixin中
+ this.getParentData('uv-tabbar')
+ },
+ // 此方法将会被父组件uv-tabbar调用
+ updateFromParent() {
+ // 重新初始化
+ this.init()
+ },
+ clickHandler() {
+ this.$nextTick(() => {
+ const index = this.parent.children.indexOf(this)
+ const name = this.name || index
+ // 点击的item为非激活的item才发出change事件
+ if (name !== this.parent.value) {
+ this.parent.$emit('change', name)
+ }
+ this.$emit('click', name)
+ })
+ }
+ },
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-tabbar-item {
+ @include flex(column);
+ align-items: center;
+ justify-content: center;
+ flex: 1;
+
+ &__icon {
+ @include flex;
+ position: relative;
+ width: 150rpx;
+ justify-content: center;
+ }
+
+ &__text {
+ margin-top: 2px;
+ font-size: 12px;
+ color: $uv-content-color;
+ }
+ }
+
+ /* #ifdef MP */
+ // 由于小程序都使用shadow DOM形式实现,需要给影子宿主设置flex: 1才能让其撑开
+ :host {
+ flex: 1
+ }
+ /* #endif */
+</style>
diff --git a/uni_modules/uv-tabbar/components/uv-tabbar/props.js b/uni_modules/uv-tabbar/components/uv-tabbar/props.js
new file mode 100644
index 0000000..91621aa
--- /dev/null
+++ b/uni_modules/uv-tabbar/components/uv-tabbar/props.js
@@ -0,0 +1,50 @@
+export default {
+ props: {
+ // 当前匹配项的name
+ value: {
+ type: [String, Number, null],
+ default: null
+ },
+ // 是否为iPhoneX留出底部安全距离
+ safeAreaInsetBottom: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示上方边框
+ border: {
+ type: Boolean,
+ default: true
+ },
+ // 元素层级z-index
+ zIndex: {
+ type: [String, Number],
+ default: 9
+ },
+ // 选中标签的颜色
+ activeColor: {
+ type: String,
+ default: '#1989fa'
+ },
+ // 未选中标签的颜色
+ inactiveColor: {
+ type: String,
+ default: '#7d7e80'
+ },
+ // 是否固定在底部
+ fixed: {
+ type: Boolean,
+ default: true
+ },
+ // fixed定位固定在底部时,是否生成一个等高元素防止塌陷
+ placeholder: {
+ type: Boolean,
+ default: true
+ },
+ // 图标大小
+ iconSize: {
+ type: [String, Number],
+ default: 20
+ },
+ ...uni.$uv?.props?.tabbar
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue b/uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue
new file mode 100644
index 0000000..694ed13
--- /dev/null
+++ b/uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue
@@ -0,0 +1,146 @@
+<template>
+ <view class="uv-tabbar">
+ <view
+ class="uv-tabbar__content"
+ ref="uv-tabbar__content"
+ @touchmove.stop.prevent="noop"
+ :class="[border && 'uv-border-top', fixed && 'uv-tabbar--fixed']"
+ :style="[tabbarStyle]"
+ >
+ <view class="uv-tabbar__content__item-wrapper">
+ <slot />
+ </view>
+ <uv-safe-bottom v-if="safeAreaInsetBottom"></uv-safe-bottom>
+ </view>
+ <view
+ class="uv-tabbar__placeholder"
+ v-if="placeholder"
+ :style="{
+ height: placeholderHeight + 'px',
+ }"
+ ></view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * Tabbar 底部导航栏
+ * @description 此组件提供了自定义tabbar的能力。
+ * @tutorial https://www.uvui.cn/components/tabbar.html
+ * @property {String | Number} value 当前匹配项的name
+ * @property {Boolean} safeAreaInsetBottom 是否为iPhoneX留出底部安全距离(默认 true )
+ * @property {Boolean} border 是否显示上方边框(默认 true )
+ * @property {String | Number} zIndex 元素层级z-index(默认 1 )
+ * @property {String} activeColor 选中标签的颜色(默认 '#1989fa' )
+ * @property {String} inactiveColor 未选中标签的颜色(默认 '#7d7e80' )
+ * @property {Boolean} fixed 是否固定在底部(默认 true )
+ * @property {Boolean} placeholder fixed定位固定在底部时,是否生成一个等高元素防止塌陷(默认 true )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @example <uv-tabbar :value="value2" :placeholder="false" @change="name => value2 = name" :fixed="false" :safeAreaInsetBottom="false"><uv-tabbar-item text="首页" icon="home" dot ></uv-tabbar-item></uv-tabbar>
+ */
+ export default {
+ name: 'uv-tabbar',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ placeholderHeight: 0
+ }
+ },
+ computed: {
+ tabbarStyle() {
+ const style = {
+ zIndex: this.zIndex
+ }
+ // 合并来自父组件的customStyle样式
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ },
+ // 监听多个参数的变化,通过在computed执行对应的操作
+ updateChild() {
+ return [this.value, this.activeColor, this.inactiveColor]
+ },
+ updatePlaceholder() {
+ return [this.fixed, this.placeholder]
+ }
+ },
+ watch: {
+ updateChild() {
+ // 如果updateChildren中的元素发生了变化,则执行子元素初始化操作
+ this.updateChildren()
+ },
+ updatePlaceholder() {
+ // 如果fixed,placeholder等参数发生变化,重新计算占位元素的高度
+ this.setPlaceholderHeight()
+ }
+ },
+ created() {
+ this.children = []
+ },
+ mounted() {
+ this.setPlaceholderHeight()
+ },
+ methods: {
+ updateChildren() {
+ // 如果存在子元素,则执行子元素的updateFromParent进行更新数据
+ this.children.length && this.children.map(child => child.updateFromParent())
+ },
+ // 设置用于防止塌陷元素的高度
+ async setPlaceholderHeight() {
+ if (!this.fixed || !this.placeholder) return
+ // 延时一定时间
+ await this.$uv.sleep(20)
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-tabbar__content').then(({height = 50}) => {
+ // 修复IOS safearea bottom 未填充高度
+ this.placeholderHeight = height
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ dom.getComponentRect(this.$refs['uv-tabbar__content'], (res) => {
+ const {
+ size
+ } = res
+ this.placeholderHeight = size.height
+ })
+ // #endif
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ $show-border: 1;
+ $show-border-top: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-tabbar {
+ @include flex(column);
+ flex: 1;
+ justify-content: center;
+
+ &__content {
+ @include flex(column);
+ background-color: #fff;
+
+ &__item-wrapper {
+ height: 50px;
+ @include flex(row);
+ }
+ }
+
+ &--fixed {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
+ }
+</style>
diff --git a/uni_modules/uv-tabbar/package.json b/uni_modules/uv-tabbar/package.json
new file mode 100644
index 0000000..41c3257
--- /dev/null
+++ b/uni_modules/uv-tabbar/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-tabbar",
+ "displayName": "uv-tabbar 底部导航栏 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.5",
+ "description": "底部导航栏组件提供了自定义tabbar的能力,可以满足某些需要鉴权跳转等场景。",
+ "keywords": [
+ "uv-tabbar",
+ "uvui",
+ "uv-ui",
+ "tabbar",
+ "底部导航栏"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-badge",
+ "uv-safe-bottom"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tabbar/readme.md b/uni_modules/uv-tabbar/readme.md
new file mode 100644
index 0000000..fece546
--- /dev/null
+++ b/uni_modules/uv-tabbar/readme.md
@@ -0,0 +1,21 @@
+## Tabbar 底部导航栏
+
+> **组件名:uv-tabbar**
+
+此组件提供了自定义`tabbar`的能力。
+
+一般固定在底部,可以满足某些需要鉴权跳转等场景。一旦自定义底部导航栏后,为了达到不闪烁的效果,建议在`tabbar`页面进行组件切换。
+
+# <a href="https://www.uvui.cn/components/tabbar.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <span style="font-size:14px;font-weight:700;">(请不要 下载插件ZIP)</span>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-tabs/changelog.md b/uni_modules/uv-tabs/changelog.md
new file mode 100644
index 0000000..a0924ba
--- /dev/null
+++ b/uni_modules/uv-tabs/changelog.md
@@ -0,0 +1,22 @@
+## 1.0.9(2023-12-01)
+1. 修复current为字符串activeStyle不生效的BUG
+2. 优化文档
+## 1.0.8(2023-10-13)
+1. 优化点击一个选项,change事件重复派发的问题
+## 1.0.7(2023-09-14)
+1. 优化首次加载时,处理下划线会有左到右的过渡效果
+## 1.0.6(2023-09-13)
+1. 修复设置lineWidth未带单位产生的误差BUG
+## 1.0.5(2023-06-23)
+添加uv-icon依赖
+## 1.0.4(2023-06-16)
+1. 增加customStyle参数
+## 1.0.3(2023-06-12)
+1. activeStyle设置字体大小,导致下划线位置不对的BUG,增加this.$nextTick机制
+## 1.0.2(2023-05-23)
+1. 修复nvue中不滚动的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-tabs 标签选项卡
diff --git a/uni_modules/uv-tabs/components/uv-tabs/props.js b/uni_modules/uv-tabs/components/uv-tabs/props.js
new file mode 100644
index 0000000..acab29e
--- /dev/null
+++ b/uni_modules/uv-tabs/components/uv-tabs/props.js
@@ -0,0 +1,71 @@
+export default {
+ props: {
+ // 滑块的移动过渡时间,单位ms
+ duration: {
+ type: Number,
+ default: 300
+ },
+ // tabs标签数组
+ list: {
+ type: Array,
+ default: () => []
+ },
+ // 滑块颜色
+ lineColor: {
+ type: String,
+ default: '#3c9cff'
+ },
+ // 菜单选择中时的样式
+ activeStyle: {
+ type: [String, Object],
+ default: () => ({
+ color: '#303133'
+ })
+ },
+ // 菜单非选中时的样式
+ inactiveStyle: {
+ type: [String, Object],
+ default: () => ({
+ color: '#606266'
+ })
+ },
+ // 滑块长度
+ lineWidth: {
+ type: [String, Number],
+ default: 20
+ },
+ // 滑块高度
+ lineHeight: {
+ type: [String, Number],
+ default: 3
+ },
+ // 滑块背景显示大小,当滑块背景设置为图片时使用
+ lineBgSize: {
+ type: String,
+ default: 'cover'
+ },
+ // 菜单item的样式
+ itemStyle: {
+ type: [String, Object],
+ default: () => ({
+ height: '44px'
+ })
+ },
+ // 菜单是否可滚动
+ scrollable: {
+ type: Boolean,
+ default: true
+ },
+ // 当前选中标签的索引
+ current: {
+ type: [Number, String],
+ default: 0
+ },
+ // 默认读取的键名
+ keyName: {
+ type: String,
+ default: 'name'
+ },
+ ...uni.$uv?.props?.tabs
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue b/uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue
new file mode 100644
index 0000000..cfbc3b7
--- /dev/null
+++ b/uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue
@@ -0,0 +1,390 @@
+<template>
+ <view class="uv-tabs" :style="[$uv.addStyle(customStyle)]">
+ <view class="uv-tabs__wrapper">
+ <slot name="left" />
+ <view class="uv-tabs__wrapper__scroll-view-wrapper">
+ <scroll-view
+ :scroll-x="scrollable"
+ :scroll-left="scrollLeft"
+ scroll-with-animation
+ class="uv-tabs__wrapper__scroll-view"
+ :show-scrollbar="false"
+ ref="uv-tabs__wrapper__scroll-view"
+ >
+ <view
+ class="uv-tabs__wrapper__nav"
+ ref="uv-tabs__wrapper__nav"
+ :style="{
+ flex: scrollable ? '' : 1
+ }"
+ >
+ <view
+ class="uv-tabs__wrapper__nav__item"
+ v-for="(item, index) in list"
+ :key="index"
+ @tap="clickHandler(item, index)"
+ :ref="`uv-tabs__wrapper__nav__item-${index}`"
+ :style="[{flex: scrollable ? '' : 1},$uv.addStyle(itemStyle)]"
+ :class="[`uv-tabs__wrapper__nav__item-${index}`, item.disabled && 'uv-tabs__wrapper__nav__item--disabled']"
+ >
+ <text
+ :class="[item.disabled && 'uv-tabs__wrapper__nav__item__text--disabled']"
+ class="uv-tabs__wrapper__nav__item__text"
+ :style="[textStyle(index)]"
+ >{{ item[keyName] }}</text>
+ <uv-badge
+ :show="!!(item.badge && (item.badge.show || item.badge.isDot || item.badge.value))"
+ :isDot="item.badge && item.badge.isDot || propsBadge.isDot"
+ :value="item.badge && item.badge.value || propsBadge.value"
+ :max="item.badge && item.badge.max || propsBadge.max"
+ :type="item.badge && item.badge.type || propsBadge.type"
+ :showZero="item.badge && item.badge.showZero || propsBadge.showZero"
+ :bgColor="item.badge && item.badge.bgColor || propsBadge.bgColor"
+ :color="item.badge && item.badge.color || propsBadge.color"
+ :shape="item.badge && item.badge.shape || propsBadge.shape"
+ :numberType="item.badge && item.badge.numberType || propsBadge.numberType"
+ :inverted="item.badge && item.badge.inverted || propsBadge.inverted"
+ customStyle="margin-left: 4px;"
+ ></uv-badge>
+ </view>
+ <!-- #ifdef APP-NVUE -->
+ <view
+ class="uv-tabs__wrapper__nav__line"
+ ref="uv-tabs__wrapper__nav__line"
+ :style="[{
+ width: $uv.addUnit(lineWidth),
+ height: firstTime?0:$uv.addUnit(lineHeight),
+ background: lineColor,
+ backgroundSize: lineBgSize
+ }]"
+ >
+ <!-- #endif -->
+ <!-- #ifndef APP-NVUE -->
+ <view
+ class="uv-tabs__wrapper__nav__line"
+ ref="uv-tabs__wrapper__nav__line"
+ :style="[{
+ width: $uv.addUnit(lineWidth),
+ transform: `translate(${lineOffsetLeft}px)`,
+ transitionDuration: `${firstTime ? 0 : duration}ms`,
+ height: firstTime?0:$uv.addUnit(lineHeight),
+ background: lineColor,
+ backgroundSize: lineBgSize,
+ }]"
+ >
+ <!-- #endif -->
+ </view>
+ </view>
+ </scroll-view>
+ </view>
+ <slot name="right" />
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import uvBadgeProps from '@/uni_modules/uv-badge/components/uv-badge/props.js'
+ // #ifdef APP-NVUE
+ const animation = uni.requireNativePlugin('animation')
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ import props from './props.js';
+ /**
+ * Tabs 标签
+ * @description tabs标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的tab会自动移动到组件的中间位置。
+ * @tutorial https://www.uvui.cn/components/tabs.html
+ * @property {Array} list 标签数组,元素为对象,如[{name: '推荐'}]
+ * @property {String | Number} duration 滑块移动一次所需的时间,单位秒(默认 200 )
+ * @property {String | Object} activeStyle 菜单选择中时的样式(默认{ color: '#303133' })
+ * @property {String | Object} inactiveStyle 菜单非选择中时的样式(默认{ color: '#606266' })
+ * @property {String | Number} lineWidth 滑块长度(默认 20)
+ * @property {String | Number} lineHeight 滑块高度(默认 3)
+ * @property {String} lineColor 滑块颜色(默认:'#3c9cff')
+ * @property {String} lineBgSize 滑块背景显示大小,当滑块背景设置为图片时使用(默认 cover)
+ * @property {String | Number} itemStyle 菜单item的样式(默认 { height: '44px' })
+ * @property {String} scrollable 菜单是否可滚动,选项很少的时候设置为false整个tabs自动居中显示(默认:true)
+ * @property {String | Number} current 当前选中标签的索引(默认 0 )
+ * @property {String} keyName 从list元素对象中读取的键名(默认 'name' )
+ * @property {String | Number} swierWidth swiper的宽度(默认 '750rpx' )
+ * @property {String | Object} customStyle 自定义外部样式
+ *
+ * @event {Function(index)} change 标签改变时触发 index: 点击了第几个tab,索引从0开始
+ * @event {Function(index)} click 点击标签时触发 index: 点击了第几个tab,索引从0开始
+ * @example <uv-tabs :list="list" :is-scroll="false" :current="current" @change="change"></uv-tabs>
+ */
+ export default {
+ name: 'uv-tabs',
+ emits: ['click','change'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ firstTime: true,
+ scrollLeft: 0,
+ scrollViewWidth: 0,
+ lineOffsetLeft: 0,
+ tabsRect: {
+ left: 0
+ },
+ innerCurrent: 0,
+ moving: false,
+ }
+ },
+ watch: {
+ current: {
+ immediate: true,
+ handler (newValue, oldValue) {
+ // 内外部值不相等时,才尝试移动滑块
+ if (newValue !== this.innerCurrent) {
+ this.innerCurrent = newValue
+ this.$nextTick(() => {
+ this.resize()
+ })
+ }
+ }
+ },
+ // list变化时,重新渲染list各项信息
+ list() {
+ this.$nextTick(() => {
+ this.resize()
+ })
+ }
+ },
+ computed: {
+ textStyle() {
+ return index => {
+ const style = {}
+ // 取当期是否激活的样式
+ const customeStyle = index == this.innerCurrent ? this.$uv.addStyle(this.activeStyle) : this.$uv
+ .addStyle(
+ this.inactiveStyle)
+ // 如果当前菜单被禁用,则加上对应颜色,需要在此做处理,是因为nvue下,无法在style样式中通过!import覆盖标签的内联样式
+ if (this.list[index].disabled) {
+ style.color = '#c8c9cc'
+ }
+ return this.$uv.deepMerge(customeStyle, style)
+ }
+ },
+ propsBadge() {
+ return uvBadgeProps
+ }
+ },
+ async mounted() {
+ this.init()
+ },
+ methods: {
+ setLineLeft() {
+ const tabItem = this.list[this.innerCurrent];
+ if (!tabItem) {
+ return;
+ }
+ // 获取滑块该移动的位置
+ let lineOffsetLeft = this.list
+ .slice(0, this.innerCurrent)
+ .reduce((total, curr) => total + curr.rect.width, 0);
+ // 获取下划线的数值px表示法
+ let lineWidth = this.$uv.getPx(this.lineWidth);
+ // 如果传的值未带单位+设置了全局单位,则带上单位计算,这样才没有误差
+ if (this.$uv.test.number(this.lineWidth) && this.$uv.unit) {
+ lineWidth = this.$uv.getPx(`${this.lineWidth}${this.$uv.unit}`);
+ }
+ this.lineOffsetLeft = lineOffsetLeft + (tabItem.rect.width - lineWidth) / 2
+ // #ifdef APP-NVUE
+ // 第一次移动滑块,无需过渡时间
+ this.animation(this.lineOffsetLeft, this.firstTime ? 0 : parseInt(this.duration))
+ // #endif
+
+ // 如果是第一次执行此方法,让滑块在初始化时,瞬间滑动到第一个tab item的中间
+ // 这里需要一个定时器,因为在非nvue下,是直接通过style绑定过渡时间,需要等其过渡完成后,再设置为false(非第一次移动滑块)
+ if (this.firstTime) {
+ setTimeout(() => {
+ this.firstTime = false
+ }, 20);
+ }
+ },
+ // nvue下设置滑块的位置
+ animation(x, duration = 0) {
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-tabs__wrapper__nav__line']
+ animation.transition(ref, {
+ styles: {
+ transform: `translateX(${x}px)`
+ },
+ duration
+ })
+ // #endif
+ },
+ // 点击某一个标签
+ clickHandler(item, index) {
+ // 因为标签可能为disabled状态,所以click是一定会发出的,但是change事件是需要可用的状态才发出
+ this.$emit('click', {
+ ...item,
+ index
+ })
+ // 如果disabled状态,返回
+ if (item.disabled) return
+ if(this.innerCurrent != index) {
+ this.$emit('change', {
+ ...item,
+ index
+ })
+ }
+ this.innerCurrent = index
+ // #ifndef APP-NVUE
+ this.$nextTick(()=>{
+ this.resize()
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ this.$nextTick(()=>{
+ // nvue模式下再给点延时,确保万无一失
+ this.$uv.sleep(30).then(res=>{
+ this.resize()
+ });
+ })
+ // #endif
+ },
+ init() {
+ this.$uv.sleep().then(() => {
+ this.resize()
+ })
+ },
+ setScrollLeft() {
+ // 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
+ const tabRect = this.list[this.innerCurrent]
+ // 累加得到当前item到左边的距离
+ const offsetLeft = this.list
+ .slice(0, this.innerCurrent)
+ .reduce((total, curr) => {
+ return total + curr.rect.width
+ }, 0)
+ // 此处为屏幕宽度
+ const windowWidth = this.$uv.sys().windowWidth
+ // 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动
+ let scrollLeft = offsetLeft - (this.tabsRect.width - tabRect.rect.width) / 2 - (windowWidth - this.tabsRect
+ .right) / 2 + this.tabsRect.left / 2
+ // 这里做一个限制,限制scrollLeft的最大值为整个scroll-view宽度减去tabs组件的宽度
+ scrollLeft = Math.min(scrollLeft, this.scrollViewWidth - this.tabsRect.width)
+ this.scrollLeft = Math.max(0, scrollLeft)
+ },
+ // 获取所有标签的尺寸
+ resize() {
+ // 如果不存在list,则不处理
+ if(this.list.length === 0) {
+ return
+ }
+ Promise.all([this.getTabsRect(), this.getAllItemRect()]).then(([tabsRect, itemRect = []]) => {
+ this.tabsRect = tabsRect
+ this.scrollViewWidth = 0
+ itemRect.map((item, index) => {
+ // 计算scroll-view的宽度,这里
+ this.scrollViewWidth += item.width
+ // 另外计算每一个item的中心点X轴坐标
+ this.list[index].rect = item
+ })
+ // 获取了tabs的尺寸之后,设置滑块的位置
+ this.setLineLeft()
+ this.setScrollLeft()
+ })
+ },
+ // 获取导航菜单的尺寸
+ getTabsRect() {
+ return new Promise(resolve => {
+ this.queryRect('uv-tabs__wrapper__scroll-view').then(size => resolve(size))
+ })
+ },
+ // 获取所有标签的尺寸
+ getAllItemRect() {
+ return new Promise(resolve => {
+ const promiseAllArr = this.list.map((item, index) => this.queryRect(
+ `uv-tabs__wrapper__nav__item-${index}`, true))
+ Promise.all(promiseAllArr).then(sizes => resolve(sizes))
+ })
+ },
+ // 获取各个标签的尺寸
+ queryRect(el, item) {
+ // #ifndef APP-NVUE
+ // $uvGetRect为uni-ui自带的节点查询简化方法,详见文档介绍:https://www.uvui.cn/js/getRect.html
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`.${el}`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(item ? this.$refs[el][0] : this.$refs[el], res => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ },
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-tabs {
+
+ &__wrapper {
+ @include flex;
+ align-items: center;
+
+ &__scroll-view-wrapper {
+ flex: 1;
+ /* #ifndef APP-NVUE */
+ overflow: auto hidden;
+ /* #endif */
+ }
+
+ &__scroll-view {
+ @include flex;
+ flex: 1;
+ }
+
+ &__nav {
+ @include flex;
+ position: relative;
+
+ &__item {
+ padding: 0 11px;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+
+ &--disabled {
+ /* #ifndef APP-NVUE */
+ cursor: not-allowed;
+ /* #endif */
+ }
+
+ &__text {
+ font-size: 15px;
+ color: $uv-content-color;
+
+ &--disabled {
+ color: $uv-disabled-color !important;
+ }
+ }
+ }
+
+ &__line {
+ height: 3px;
+ background: $uv-primary;
+ width: 30px;
+ position: absolute;
+ bottom: 2px;
+ border-radius: 100px;
+ transition-property: transform;
+ transition-duration: 300ms;
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-tabs/package.json b/uni_modules/uv-tabs/package.json
new file mode 100644
index 0000000..b56f108
--- /dev/null
+++ b/uni_modules/uv-tabs/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-tabs",
+ "displayName": "uv-tabs 标签选项卡 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.9",
+ "description": "标签选项卡组件,是一个tabs标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的tab会自动移动到组件的中间位置。",
+ "keywords": [
+ "选项卡",
+ "uvui",
+ "uv-ui",
+ "tab",
+ "标签"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-badge",
+ "uv-sticky",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tabs/readme.md b/uni_modules/uv-tabs/readme.md
new file mode 100644
index 0000000..88b74aa
--- /dev/null
+++ b/uni_modules/uv-tabs/readme.md
@@ -0,0 +1,19 @@
+## Tabs 标签选项卡
+
+> **组件名:uv-tabs**
+
+标签选项卡组件,是一个`tabs`标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的`tab`会自动移动到组件的中间位置。
+
+# <a href="https://www.uvui.cn/components/tabs.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-tags/changelog.md b/uni_modules/uv-tags/changelog.md
new file mode 100644
index 0000000..96450ba
--- /dev/null
+++ b/uni_modules/uv-tags/changelog.md
@@ -0,0 +1,10 @@
+## 1.0.3(2023-10-02)
+1. 兼容customStyle参数
+2. 优化
+## 1.0.2(2023-07-31)
+1. nvue模式下增加cell-child参数,避免在list中使用出现回收后不显示的BUG
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-tags 标签
diff --git a/uni_modules/uv-tags/components/uv-tags/props.js b/uni_modules/uv-tags/components/uv-tags/props.js
new file mode 100644
index 0000000..82b4bf7
--- /dev/null
+++ b/uni_modules/uv-tags/components/uv-tags/props.js
@@ -0,0 +1,95 @@
+export default {
+ props: {
+ // 标签类型info、primary、success、warning、error
+ type: {
+ type: String,
+ default: 'primary'
+ },
+ // 不可用
+ disabled: {
+ type: [Boolean, String],
+ default: false
+ },
+ // 标签的大小,large,medium,mini
+ size: {
+ type: String,
+ default: 'medium'
+ },
+ // tag的形状,circle(两边半圆形), square(方形,带圆角)
+ shape: {
+ type: String,
+ default: 'square'
+ },
+ // 标签文字
+ text: {
+ type: [String, Number],
+ default: ''
+ },
+ // 背景颜色,默认为空字符串,即不处理
+ bgColor: {
+ type: String,
+ default: ''
+ },
+ // 标签字体颜色,默认为空字符串,即不处理
+ color: {
+ type: String,
+ default: ''
+ },
+ // 标签的边框颜色
+ borderColor: {
+ type: String,
+ default: ''
+ },
+ // 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了
+ name: {
+ type: [String, Number],
+ default: ''
+ },
+ // 镂空时是否填充背景色
+ plainFill: {
+ type: Boolean,
+ default: false
+ },
+ // 是否镂空
+ plain: {
+ type: Boolean,
+ default: false
+ },
+ // 是否可关闭
+ closable: {
+ type: Boolean,
+ default: false
+ },
+ // 关闭按钮图标的颜色
+ closeColor: {
+ type: String,
+ default: '#C6C7CB'
+ },
+ // 关闭按钮图标的位置 right(右边)right-top(右上) 默认right-top
+ closePlace: {
+ type: String,
+ default: 'right-top'
+ },
+ // 是否显示
+ show: {
+ type: Boolean,
+ default: true
+ },
+ // 内置图标,或绝对路径的图片
+ icon: {
+ type: String,
+ default: ''
+ },
+ // 图标颜色
+ iconColor: {
+ type: String,
+ default: ''
+ },
+ // nvue模式下 是否直接显示,在uv-list等cell下面使用就需要设置
+ cellChild: {
+ type: Boolean,
+ default: false
+ },
+ ...uni.$uv?.props?.tags
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tags/components/uv-tags/uv-tags.vue b/uni_modules/uv-tags/components/uv-tags/uv-tags.vue
new file mode 100644
index 0000000..baeb0fd
--- /dev/null
+++ b/uni_modules/uv-tags/components/uv-tags/uv-tags.vue
@@ -0,0 +1,379 @@
+<template>
+ <uv-transition
+ mode="fade"
+ :show="show"
+ :cell-child="cellChild"
+ >
+ <view class="uv-tags-wrapper">
+ <view
+ class="uv-tags"
+ :class="[`uv-tags--${shape}`, !plain && `uv-tags--${type}`, plain && `uv-tags--${type}--plain`, `uv-tags--${size}`,`uv-tags--${size}--${closePlace}`, plain && plainFill && `uv-tags--${type}--plain--fill`]"
+ @tap.stop="clickHandler"
+ :style="[{
+ marginRight: closable&& closePlace=='right-top' ? '10px' : 0,
+ marginTop: closable && closePlace=='right-top' ? '10px' : 0,
+ }, style, $uv.addStyle(customStyle)]"
+ >
+ <slot name="icon">
+ <view
+ class="uv-tags__icon"
+ v-if="icon"
+ >
+ <image
+ v-if="$uv.test.image(icon)"
+ :src="icon"
+ :style="[imgStyle]"
+ ></image>
+ <uv-icon
+ v-else
+ :color="elIconColor"
+ :name="icon"
+ :size="iconSize"
+ ></uv-icon>
+ </view>
+ </slot>
+ <text
+ class="uv-tags__text"
+ :style="[textColor]"
+ :class="[`uv-tags__text--${type}`, plain && `uv-tags__text--${type}--plain`, `uv-tags__text--${size}`]"
+ >{{ text }}</text>
+ <view
+ class="uv-tags__close"
+ :class="[`uv-tags__close--${size}`,`uv-tags__close--${closePlace}`]"
+ v-if="closable && closePlace=='right'"
+ @tap.stop="closeHandler"
+ :style="{backgroundColor: closeColor}"
+ >
+ <uv-icon
+ name="close"
+ :size="closeSize"
+ color="#ffffff"
+ ></uv-icon>
+ </view>
+ </view>
+ <view
+ class="uv-tags__close"
+ :class="[`uv-tags__close--${size}`,`uv-tags__close--${closePlace}`]"
+ v-if="closable && closePlace=='right-top'"
+ @tap.stop="closeHandler"
+ :style="{backgroundColor: closeColor}"
+ >
+ <uv-icon
+ name="close"
+ :size="closeSize"
+ color="#ffffff"
+ ></uv-icon>
+ </view>
+ </view>
+ </uv-transition>
+</template>
+
+<script>
+ import { image } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * Tag 标签
+ * @description tag组件一般用于标记和选择,我们提供了更加丰富的表现形式,能够较全面的涵盖您的使用场景
+ * @tutorial https://www.uvui.cn/components/tag.html
+ * @property {String} type 标签类型info、primary、success、warning、error (默认 'primary' )
+ * @property {Boolean | String} disabled 不可用(默认 false )
+ * @property {String} size 标签的大小,large,medium,mini (默认 'medium' )
+ * @property {String} shape tag的形状,circle(两边半圆形), square(方形,带圆角)(默认 'square' )
+ * @property {String | Number} text 标签的文字内容
+ * @property {String} bgColor 背景颜色,默认为空字符串,即不处理
+ * @property {String} color 标签字体颜色,默认为空字符串,即不处理
+ * @property {String} borderColor 镂空形式标签的边框颜色
+ * @property {String} closeColor 关闭按钮图标的颜色(默认 #C6C7CB)
+ * @property {String | Number} name 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了
+ * @property {Boolean} plainFill 镂空时是否填充背景色(默认 false )
+ * @property {Boolean} plain 是否镂空(默认 false )
+ * @property {Boolean} closable 是否可关闭,设置为true,文字右边会出现一个关闭图标(默认 false )
+ * @property {Boolean} show 标签显示与否(默认 true )
+ * @property {String} icon 内置图标,或绝对路径的图片
+ * @event {Function(index)} click 点击标签时触发 index: 传递的index参数值
+ * @event {Function(index)} close closable为true时,点击标签关闭按钮触发 index: 传递的index参数值
+ * @example <uv-tags text="标签" type="error" plain plainFill></uv-tags>
+ */
+ export default {
+ name: 'uv-tags',
+ emits: ['click','close'],
+ mixins: [mpMixin, mixin, props],
+ computed: {
+ style() {
+ const style = {}
+ if (this.bgColor) {
+ style.backgroundColor = this.bgColor
+ }
+ if (this.color) {
+ style.color = this.color
+ }
+ if(this.borderColor) {
+ style.borderColor = this.borderColor
+ }
+ return style
+ },
+ // nvue下,文本颜色无法继承父元素
+ textColor() {
+ const style = {}
+ if (this.color) {
+ style.color = this.color
+ }
+ return style
+ },
+ imgStyle() {
+ const width = this.size === 'large' ? '17px' : this.size === 'medium' ? '15px' : '13px'
+ return {
+ width,
+ height: width
+ }
+ },
+ // 文本的样式
+ closeSize() {
+ const size = this.size === 'large' ? 15 : this.size === 'medium' ? 13 : 12
+ return size
+ },
+ // 图标大小
+ iconSize() {
+ const size = this.size === 'large' ? 21 : this.size === 'medium' ? 19 : 16
+ return size
+ },
+ // 图标颜色
+ elIconColor() {
+ return this.iconColor ? this.iconColor : this.plain ? this.type : '#ffffff'
+ }
+ },
+ methods: {
+ // 点击关闭按钮
+ closeHandler() {
+ this.$emit('close', this.name)
+ },
+ // 点击标签
+ clickHandler() {
+ this.$emit('click', this.name)
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped >
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-tags-wrapper {
+ position: relative;
+ }
+
+ .uv-tags {
+ @include flex;
+ align-items: center;
+ border-style: solid;
+
+ &--circle {
+ border-radius: 100px;
+ }
+
+ &--square {
+ border-radius: 3px;
+ }
+
+ &__icon {
+ margin-right: 4px;
+ }
+
+ &__text {
+ &--mini {
+ font-size: 12px;
+ line-height: 12px;
+ }
+
+ &--medium {
+ font-size: 13px;
+ line-height: 13px;
+ }
+
+ &--large {
+ font-size: 15px;
+ line-height: 15px;
+ }
+ }
+
+ &--mini {
+ height: 22px;
+ line-height: 22px;
+ padding: 0 5px;
+ &--right {
+ padding-right: 2px;
+ }
+ }
+
+ &--medium {
+ height: 26px;
+ line-height: 22px;
+ padding: 0 10px;
+ &--right {
+ padding: 0 4px 0 8px;
+ }
+ }
+
+ &--large {
+ height: 32px;
+ line-height: 32px;
+ padding: 0 15px;
+ &--right {
+ padding: 0 4px 0 8px;
+ }
+ }
+
+ &--primary {
+ background-color: $uv-primary;
+ border-width: 1px;
+ border-color: $uv-primary;
+ }
+
+ &--primary--plain {
+ border-width: 1px;
+ border-color: $uv-primary;
+ }
+
+ &--primary--plain--fill {
+ background-color: #ecf5ff;
+ }
+
+ &__text--primary {
+ color: #FFFFFF;
+ }
+
+ &__text--primary--plain {
+ color: $uv-primary;
+ }
+
+ &--error {
+ background-color: $uv-error;
+ border-width: 1px;
+ border-color: $uv-error;
+ }
+
+ &--error--plain {
+ border-width: 1px;
+ border-color: $uv-error;
+ }
+
+ &--error--plain--fill {
+ background-color: #fef0f0;
+ }
+
+ &__text--error {
+ color: #FFFFFF;
+ }
+
+ &__text--error--plain {
+ color: $uv-error;
+ }
+
+ &--warning {
+ background-color: $uv-warning;
+ border-width: 1px;
+ border-color: $uv-warning;
+ }
+
+ &--warning--plain {
+ border-width: 1px;
+ border-color: $uv-warning;
+ }
+
+ &--warning--plain--fill {
+ background-color: #fdf6ec;
+ }
+
+ &__text--warning {
+ color: #FFFFFF;
+ }
+
+ &__text--warning--plain {
+ color: $uv-warning;
+ }
+
+ &--success {
+ background-color: $uv-success;
+ border-width: 1px;
+ border-color: $uv-success;
+ }
+
+ &--success--plain {
+ border-width: 1px;
+ border-color: $uv-success;
+ }
+
+ &--success--plain--fill {
+ background-color: #f5fff0;
+ }
+
+ &__text--success {
+ color: #FFFFFF;
+ }
+
+ &__text--success--plain {
+ color: $uv-success;
+ }
+
+ &--info {
+ background-color: $uv-info;
+ border-width: 1px;
+ border-color: $uv-info;
+ }
+
+ &--info--plain {
+ border-width: 1px;
+ border-color: $uv-info;
+ }
+
+ &--info--plain--fill {
+ background-color: #f4f4f5;
+ }
+
+ &__text--info {
+ color: #FFFFFF;
+ }
+
+ &__text--info--plain {
+ color: $uv-info;
+ }
+
+ &__close {
+ border-radius: 100px;
+ background-color: #C6C7CB;
+ @include flex(row);
+ align-items: center;
+ justify-content: center;
+ transform: scale(0.6);
+ &--right-top {
+ position: absolute;
+ z-index: 999;
+ top: 10px;
+ right: 10px;
+ /* #ifndef APP-NVUE */
+ transform: scale(0.6) translate(80%,-80%);
+ /* #endif */
+ /* #ifdef APP-NVUE */
+ transform: scale(0.6) translate(50%, -50%);
+ /* #endif */
+ }
+ &--mini {
+ width: 18px;
+ height: 18px;
+ }
+
+ &--medium {
+ width: 22px;
+ height: 22px;
+ }
+
+ &--large {
+ width: 25px;
+ height: 25px;
+ }
+ }
+
+ }
+</style>
diff --git a/uni_modules/uv-tags/package.json b/uni_modules/uv-tags/package.json
new file mode 100644
index 0000000..6ad55da
--- /dev/null
+++ b/uni_modules/uv-tags/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-tags",
+ "displayName": "uv-tags 标签 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "tag组件一般用于标记和选择,我们提供了更加丰富的表现形式,能够较全面的涵盖您的使用场景。",
+ "keywords": [
+ "uv-tags",
+ "uvui",
+ "uv-ui",
+ "tag",
+ "标签"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-transition",
+ "uv-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tags/readme.md b/uni_modules/uv-tags/readme.md
new file mode 100644
index 0000000..6170a2f
--- /dev/null
+++ b/uni_modules/uv-tags/readme.md
@@ -0,0 +1,11 @@
+## Tag 标签
+
+> **组件名:uv-tags**
+
+tag组件一般用于标记和选择,我们提供了更加丰富的表现形式,能够较全面的涵盖您的使用场景。
+
+### <a href="https://www.uvui.cn/components/tag.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-text/changelog.md b/uni_modules/uv-text/changelog.md
new file mode 100644
index 0000000..a3f748e
--- /dev/null
+++ b/uni_modules/uv-text/changelog.md
@@ -0,0 +1,11 @@
+## 1.0.4(2023-08-29)
+1. nvue修复设置align不生效的BUG
+## 1.0.3(2023-08-24)
+1. 修复在nvue不能换行的问题
+## 1.0.2(2023-05-24)
+1. 去掉多余的data-index属性,避免警告
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-text 文本组件
diff --git a/uni_modules/uv-text/components/uv-text/props.js b/uni_modules/uv-text/components/uv-text/props.js
new file mode 100644
index 0000000..e533c66
--- /dev/null
+++ b/uni_modules/uv-text/components/uv-text/props.js
@@ -0,0 +1,113 @@
+export default {
+ props: {
+ // 主题颜色
+ type: {
+ type: String,
+ default: ''
+ },
+ // 是否显示
+ show: {
+ type: Boolean,
+ default: true
+ },
+ // 显示的值
+ text: {
+ type: [String, Number],
+ default: ''
+ },
+ // 前置图标
+ prefixIcon: {
+ type: String,
+ default: ''
+ },
+ // 后置图标
+ suffixIcon: {
+ type: String,
+ default: ''
+ },
+ // 文本处理的匹配模式
+ // text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
+ mode: {
+ type: String,
+ default: ''
+ },
+ // mode=link下,配置的链接
+ href: {
+ type: String,
+ default: ''
+ },
+ // 格式化规则
+ format: {
+ type: [String, Function],
+ default: ''
+ },
+ // mode=phone时,点击文本是否拨打电话
+ call: {
+ type: Boolean,
+ default: true
+ },
+ // 小程序的打开方式
+ openType: {
+ type: String,
+ default: ''
+ },
+ // 是否粗体,默认normal
+ bold: {
+ type: Boolean,
+ default: false
+ },
+ // 是否块状
+ block: {
+ type: Boolean,
+ default: false
+ },
+ // 文本显示的行数,如果设置,超出此行数,将会显示省略号
+ lines: {
+ type: [String, Number],
+ default: ''
+ },
+ // 文本颜色
+ color: {
+ type: String,
+ default: '#303133'
+ },
+ // 字体大小
+ size: {
+ type: [String, Number],
+ default: 15
+ },
+ // 图标的样式
+ iconStyle: {
+ type: [Object, String],
+ default: () => ({
+ fontSize: '15px'
+ })
+ },
+ // 文字装饰,下划线,中划线等,可选值 none|underline|line-through
+ decoration: {
+ type: String,
+ default: 'none'
+ },
+ // 外边距,对象、字符串,数值形式均可
+ margin: {
+ type: [Object, String, Number],
+ default: 0
+ },
+ // 文本行高
+ lineHeight: {
+ type: [String, Number],
+ default: ''
+ },
+ // 文本对齐方式,可选值left|center|right
+ align: {
+ type: String,
+ default: 'left'
+ },
+ // 文字换行,可选值break-word|normal|anywhere
+ wordWrap: {
+ type: String,
+ default: 'normal'
+ },
+ ...uni.$uv?.props?.text
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-text/components/uv-text/uv-text.vue b/uni_modules/uv-text/components/uv-text/uv-text.vue
new file mode 100644
index 0000000..973eebe
--- /dev/null
+++ b/uni_modules/uv-text/components/uv-text/uv-text.vue
@@ -0,0 +1,218 @@
+<template>
+ <view
+ class="uv-text"
+ :class="[]"
+ v-if="show"
+ :style="{
+ margin: margin,
+ justifyContent: align === 'left' ? 'flex-start' : align === 'center' ? 'center' : 'flex-end'
+ }"
+ @tap="clickHandler"
+ >
+ <text
+ :class="['uv-text__price', type && `uv-text__value--${type}`]"
+ v-if="mode === 'price'"
+ :style="[valueStyle]"
+ >¥</text
+ >
+ <view class="uv-text__prefix-icon" v-if="prefixIcon">
+ <uv-icon
+ :name="prefixIcon"
+ :customStyle="$uv.addStyle(iconStyle)"
+ ></uv-icon>
+ </view>
+ <uv-link
+ v-if="mode === 'link'"
+ :text="value"
+ :href="href"
+ underLine
+ ></uv-link>
+ <template v-else-if="openType && isMp">
+ <button
+ class="uv-reset-button uv-text__value"
+ :style="[valueStyle]"
+ :openType="openType"
+ @getuserinfo="onGetUserInfo"
+ @contact="onContact"
+ @getphonenumber="onGetPhoneNumber"
+ @error="onError"
+ @launchapp="onLaunchApp"
+ @opensetting="onOpenSetting"
+ :lang="lang"
+ :session-from="sessionFrom"
+ :send-message-title="sendMessageTitle"
+ :send-message-path="sendMessagePath"
+ :send-message-img="sendMessageImg"
+ :show-message-card="showMessageCard"
+ :app-parameter="appParameter"
+ >
+ {{ value }}
+ </button>
+ </template>
+ <text
+ v-else
+ class="uv-text__value"
+ :style="[valueStyle]"
+ :class="[
+ type && `uv-text__value--${type}`,
+ lines && `uv-line-${lines}`
+ ]"
+ >{{ value }}</text
+ >
+ <view class="uv-text__suffix-icon" v-if="suffixIcon">
+ <uv-icon
+ :name="suffixIcon"
+ :customStyle="$uv.addStyle(iconStyle)"
+ ></uv-icon>
+ </view>
+ </view>
+</template>
+<script>
+ import value from './value.js'
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
+ import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
+ import props from './props.js'
+ /**
+ * Text 文本
+ * @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。
+ * @tutorial https://www.uvui.cn/components/loading.html
+ * @property {String} type 主题颜色
+ * @property {Boolean} show 是否显示(默认 true )
+ * @property {String | Number} text 显示的值
+ * @property {String} prefixIcon 前置图标
+ * @property {String} suffixIcon 后置图标
+ * @property {String} mode 文本处理的匹配模式 text-普通文本,price-价格,phone-手机号,name-姓名,date-日期,link-超链接
+ * @property {String} href mode=link下,配置的链接
+ * @property {String | Function} format 格式化规则
+ * @property {Boolean} call mode=phone时,点击文本是否拨打电话(默认 false )
+ * @property {String} openType 小程序的打开方式
+ * @property {Boolean} bold 是否粗体,默认normal(默认 false )
+ * @property {Boolean} block 是否块状(默认 false )
+ * @property {String | Number} lines 文本显示的行数,如果设置,超出此行数,将会显示省略号
+ * @property {String} color 文本颜色(默认 '#303133' )
+ * @property {String | Number} size 字体大小(默认 15 )
+ * @property {Object | String} iconStyle 图标的样式 (默认 {fontSize: '15px'} )
+ * @property {String} decoration 文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' )
+ * @property {Object | String | Number} margin 外边距,对象、字符串,数值形式均可(默认 0 )
+ * @property {String | Number} lineHeight 文本行高
+ * @property {String} align 文本对齐方式,可选值left|center|right(默认 'left' )
+ * @property {String} wordWrap 文字换行,可选值break-word|normal|anywhere(默认 'normal' )
+ * @event {Function} click 点击触发事件
+ * @example <uv-text text="我用十年青春,赴你最后之约"></uv-text>
+ */
+ export default {
+ name: 'uv-text',
+ emits: ['click'],
+ // #ifdef MP
+ mixins: [mpMixin, mixin, value, button, openType, props],
+ // #endif
+ // #ifndef MP
+ mixins: [mpMixin, mixin, value, props],
+ // #endif
+ computed: {
+ valueStyle() {
+ const style = {
+ textDecoration: this.decoration,
+ fontWeight: this.bold ? 'bold' : 'normal',
+ wordWrap: this.wordWrap,
+ fontSize: this.$uv.addUnit(this.size)
+ };
+ !this.type && (style.color = this.color);
+ this.isNvue && this.lines && (style.lines = this.lines);
+ if(this.isNvue && this.mode != 'price' && !this.prefixIcon && !this.suffixIcon) {
+ style.flex = 1;
+ style.textAlign = this.align === 'left' ? 'flex-start' : this.align === 'center' ? 'center' : 'right';
+ }
+ this.lineHeight && (style.lineHeight = this.$uv.addUnit(this.lineHeight));
+ !this.isNvue && this.block && (style.display = 'block');
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+ },
+ isNvue() {
+ let nvue = false
+ // #ifdef APP-NVUE
+ nvue = true
+ // #endif
+ return nvue
+ },
+ isMp() {
+ let mp = false
+ // #ifdef MP
+ mp = true
+ // #endif
+ return mp
+ }
+ },
+ data() {
+ return {}
+ },
+ methods: {
+ clickHandler() {
+ // 如果为手机号模式,拨打电话
+ if (this.call && this.mode === 'phone') {
+ uni.makePhoneCall({
+ phoneNumber: this.text
+ })
+ }
+ this.$emit('click')
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ $show-lines: 1;
+ $show-reset-button: 1;
+ @import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-text {
+ @include flex(row);
+ align-items: center;
+ flex-wrap: nowrap;
+ flex: 1;
+ /* #ifndef APP-NVUE */
+ width: 100%;
+ /* #endif */
+ &__price {
+ font-size: 14px;
+ color: $uv-content-color;
+ }
+ &__value {
+ font-size: 14px;
+ @include flex;
+ color: $uv-content-color;
+ flex-wrap: wrap;
+ // flex: 1;
+ text-overflow: ellipsis;
+ align-items: center;
+ &--primary {
+ color: $uv-primary;
+ }
+ &--warning {
+ color: $uv-warning;
+ }
+ &--success {
+ color: $uv-success;
+ }
+ &--info {
+ color: $uv-info;
+ }
+ &--error {
+ color: $uv-error;
+ }
+ &--main {
+ color: $uv-main-color;
+ }
+ &--content {
+ color: $uv-content-color;
+ }
+ &--tips {
+ color: $uv-tips-color;
+ }
+ &--light {
+ color: $uv-light-color;
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-text/components/uv-text/value.js b/uni_modules/uv-text/components/uv-text/value.js
new file mode 100644
index 0000000..d0a5f17
--- /dev/null
+++ b/uni_modules/uv-text/components/uv-text/value.js
@@ -0,0 +1,87 @@
+import { func, date, url } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
+import { error, timeFormat, priceFormat } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
+export default {
+ computed: {
+ // 经处理后需要显示的值
+ value() {
+ const {
+ text,
+ mode,
+ format,
+ href
+ } = this
+ // 价格类型
+ if (mode === 'price') {
+ // 如果text不为金额进行提示
+ if (!/^\d+(\.\d+)?$/.test(text)) {
+ error('金额模式下,text参数需要为金额格式');
+ }
+ // 进行格式化,判断用户传入的format参数为正则,或者函数,如果没有传入format,则使用默认的金额格式化处理
+ if (func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ }
+ // 如果format非正则,非函数,则使用默认的金额格式化方法进行操作
+ return priceFormat(text, 2)
+ } if (mode === 'date') {
+ // 判断是否合法的日期或者时间戳
+ !date(text) && error('日期模式下,text参数需要为日期或时间戳格式')
+ // 进行格式化,判断用户传入的format参数为正则,或者函数,如果没有传入format,则使用默认的格式化处理
+ if (func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ } if (format) {
+ // 如果format非正则,非函数,则使用默认的时间格式化方法进行操作
+ return timeFormat(text, format)
+ }
+ // 如果没有设置format,则设置为默认的时间格式化形式
+ return timeFormat(text, 'yyyy-mm-dd')
+ } if (mode === 'phone') {
+ // 判断是否合法的手机号
+ // !mobile(text) && error('手机号模式下,text参数需要为手机号码格式')
+ if (func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ } if (format === 'encrypt') {
+ // 如果format为encrypt,则将手机号进行星号加密处理
+ return `${text.substr(0, 3)}****${text.substr(7)}`
+ }
+ return text
+ } if (mode === 'name') {
+ // 判断是否合法的字符粗
+ !(typeof (text) === 'string') && error('姓名模式下,text参数需要为字符串格式')
+ if (func(format)) {
+ // 如果用户传入的是函数,使用函数格式化
+ return format(text)
+ } if (format === 'encrypt') {
+ // 如果format为encrypt,则将姓名进行星号加密处理
+ return this.formatName(text)
+ }
+ return text
+ } if (mode === 'link') {
+ // 判断是否合法的字符粗
+ !url(href) && error('超链接模式下,href参数需要为URL格式')
+ return text
+ }
+ return text
+ }
+ },
+ methods: {
+ // 默认的姓名脱敏规则
+ formatName(name) {
+ let value = ''
+ if (name.length === 2) {
+ value = name.substr(0, 1) + '*'
+ } else if (name.length > 2) {
+ let char = ''
+ for (let i = 0, len = name.length - 2; i < len; i++) {
+ char += '*'
+ }
+ value = name.substr(0, 1) + char + name.substr(-1, 1)
+ } else {
+ value = name
+ }
+ return value
+ }
+ }
+}
diff --git a/uni_modules/uv-text/package.json b/uni_modules/uv-text/package.json
new file mode 100644
index 0000000..80b8491
--- /dev/null
+++ b/uni_modules/uv-text/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-text",
+ "displayName": "uv-text 文本 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.4",
+ "description": "此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件涵盖您能使用的大部分场景。",
+ "keywords": [
+ "uv-text",
+ "uvui",
+ "uv-ui",
+ "text",
+ "文本"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-link"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-text/readme.md b/uni_modules/uv-text/readme.md
new file mode 100644
index 0000000..9956ffa
--- /dev/null
+++ b/uni_modules/uv-text/readme.md
@@ -0,0 +1,17 @@
+## Text 文本
+
+> **组件名:uv-text**
+
+此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件涵盖您能使用的大部分场景。
+
+# <a href="https://www.uvui.cn/components/text.html" target="_blank">查看文档</a>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-toast/changelog.md b/uni_modules/uv-toast/changelog.md
new file mode 100644
index 0000000..7858c41
--- /dev/null
+++ b/uni_modules/uv-toast/changelog.md
@@ -0,0 +1,10 @@
+## 1.0.3(2023-12-06)
+1. 增加z-index参数
+2. 增加overlay参数
+## 1.0.2(2023-10-13)
+1. unmounted兼容vue3
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-toast 消息提示
diff --git a/uni_modules/uv-toast/components/uv-toast/uv-toast.vue b/uni_modules/uv-toast/components/uv-toast/uv-toast.vue
new file mode 100644
index 0000000..5458a5e
--- /dev/null
+++ b/uni_modules/uv-toast/components/uv-toast/uv-toast.vue
@@ -0,0 +1,333 @@
+<template>
+ <view class="uv-toast">
+ <uv-overlay :show="isShow && tmpConfig.overlay" :custom-style="overlayStyle"></uv-overlay>
+ <uv-transition :show="isShow" mode="fade" :custom-style="aniStyle">
+ <view
+ class="uv-toast__content"
+ ref="uvToastContent"
+ :style="[contentStyle]"
+ :class="['uv-type-' + tmpConfig.type, (tmpConfig.type === 'loading' || tmpConfig.loading) ? 'uv-toast__content--loading' : '']"
+ >
+ <uv-loading-icon
+ v-if="tmpConfig.type === 'loading'"
+ mode="circle"
+ color="rgb(255, 255, 255)"
+ inactiveColor="rgb(120, 120, 120)"
+ size="25"
+ ></uv-loading-icon>
+ <uv-icon
+ v-else-if="tmpConfig.type !== 'defalut' && iconName"
+ :name="iconName"
+ size="17"
+ :color="tmpConfig.type"
+ :customStyle="iconStyle"
+ ></uv-icon>
+ <uv-gap
+ v-if="tmpConfig.type === 'loading' || tmpConfig.loading"
+ height="12"
+ bgColor="transparent"
+ ></uv-gap>
+ <text
+ class="uv-toast__content__text"
+ :class="['uv-toast__content__text--' + tmpConfig.type]"
+ style="max-width: 400rpx;"
+ >{{ tmpConfig.message }}</text>
+ </view>
+ </uv-transition>
+ </view>
+</template>
+<script>
+ import { hexToRgb } from '@/uni_modules/uv-ui-tools/libs/function/colorGradient.js'
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * toast 消息提示
+ * @description 此组件表现形式类似uni的uni.showToastAPI,但也有不同的地方。
+ * @tutorial https://www.uvui.cn/components/toast.html
+ * @property {Boolean} loading 是否加载中 (默认 false )
+ * @property {String | Number} zIndex toast展示时的zIndex值 (默认 10090 )
+ * @property {String | Number} message 显示的文字内容
+ * @property {String} icon 图标,或者绝对路径的图片
+ * @property {String} type 主题类型 (默认 default)
+ * @property {Boolean} overlay 是否显示透明遮罩,防止点击穿透 (默认 false )
+ * @property {String} position 位置 (默认 'center' )
+ * @property {Object} params 跳转的参数
+ * @property {String | Number} duration 展示时间,单位ms (默认 2000 )
+ * @property {Function} complete 执行完后的回调函数
+ *
+ * @event {Function} show 显示toast,如需一进入页面就显示toast,请在onReady生命周期调用
+ * @example <uv-toast ref="uvToastRef" />
+ */
+ export default {
+ name: 'uv-toast',
+ mixins: [mpMixin, mixin],
+ data() {
+ return {
+ isShow: false,
+ timer: null, // 定时器
+ config: {
+ message: '', // 显示文本
+ type: '', // 主题类型,primary,success,error,warning,black
+ duration: 2000, // 显示的时间,毫秒
+ icon: true, // 显示的图标
+ position: 'center', // toast出现的位置
+ complete: null, // 执行完后的回调函数
+ overlay: true, // 是否防止触摸穿透
+ loading: false ,// 是否加载中状态
+ zIndex: 10090 //弹出的层级
+ },
+ tmpConfig: {}, // 将用户配置和内置配置合并后的临时配置变量
+ rect: {},
+ opacity: 0
+ }
+ },
+ computed: {
+ iconName() {
+ // 只有不为none,并且type为error|warning|succes|info时候,才显示图标
+ if (!this.tmpConfig.icon || this.tmpConfig.icon == 'none') {
+ return '';
+ }
+ if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) {
+ return this.$uv.type2icon(this.tmpConfig.type)
+ } else {
+ return ''
+ }
+ },
+ overlayStyle() {
+ const style = {
+ justifyContent: 'center',
+ alignItems: 'center',
+ display: 'flex',
+ zIndex: this.tmpConfig.zIndex
+ }
+ // 将遮罩设置为100%透明度,避免出现灰色背景
+ style.backgroundColor = 'rgba(0, 0, 0, 0)'
+ return style
+ },
+ iconStyle() {
+ const style = {}
+ // 图标需要一个右边距,以跟右边的文字有隔开的距离
+ style.marginRight = '4px'
+ // #ifdef APP-NVUE
+ // iOSAPP下,图标有1px的向下偏移,这里进行修正
+ if (this.$uv.os() === 'ios') {
+ style.marginTop = '-1px'
+ }
+ // #endif
+ return style
+ },
+ aniStyle() {
+ const style = {
+ position: 'fixed',
+ zIndex: this.tmpConfig.zIndex
+ }
+ return style
+ },
+ // 内容盒子的样式
+ contentStyle() {
+ const { windowWidth, windowHeight } = this.$uv.sys();
+ const style = {
+ position: 'fixed',
+ top: '50%',
+ left: '50%',
+ // #ifdef APP-NVUE
+ opacity: this.opacity
+ // #endif
+ };
+ let value = 0
+ // #ifndef APP-NVUE
+ // 根据top和bottom,对Y轴进行窗体高度的百分比偏移
+ if (this.tmpConfig.position === 'top') {
+ style.top = '25%'
+ } else if (this.tmpConfig.position === 'bottom') {
+ style.top = '75%'
+ } else {
+ value = '-50%'
+ }
+ style.transform = `translate(-50%,${value})`
+ // #endif
+ // #ifdef APP-NVUE
+ const { width = 0, height = 0 } = this.rect ? this.rect : {};
+ if (width && height) {
+ style.left = `${windowWidth*0.5 - width/2}px`
+ if (this.tmpConfig.position === 'top') {
+ style.top = `${windowHeight*0.5- height/2 - windowHeight*0.25}px`
+ } else if (this.tmpConfig.position === 'bottom') {
+ style.top = `${windowHeight*0.5- height/2 + windowHeight*0.25}px`
+ } else {
+ style.top = `${windowHeight*0.5- height/2}px`
+ }
+ }
+ // #endif
+ return style
+ }
+ },
+ created() {
+ // 通过主题的形式调用toast,批量生成方法函数
+ ['primary', 'success', 'error', 'warning', 'default', 'loading'].map(item => {
+ this[item] = message => this.show({
+ type: item,
+ message
+ })
+ })
+ },
+ methods: {
+ // 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用
+ show(options) {
+ // 不将结果合并到this.config变量,避免多次调用uv-toast,前后的配置造成混乱
+ this.tmpConfig = this.$uv.deepMerge(this.config, options)
+ // 清除定时器
+ this.clearTimer()
+ this.isShow = true
+ //#ifdef APP-NVUE
+ this.$nextTick(() => {
+ this.$uv.sleep(100).then(async res => {
+ this.rect = await this.queryRect();
+ this.$nextTick(() => {
+ this.opacity = 1;
+ })
+ })
+ })
+ // #endif
+ this.timer = setTimeout(() => {
+ // 倒计时结束,清除定时器,隐藏toast组件
+ this.clearTimer()
+ // 判断是否存在callback方法,如果存在就执行
+ typeof(this.tmpConfig.complete) === 'function' && this.tmpConfig.complete()
+ }, this.tmpConfig.duration)
+ },
+ // 查询内容高度
+ queryRect() {
+ return new Promise(resolve => {
+ const ref = this.$refs['uvToastContent'];
+ dom.getComponentRect(ref, res => {
+ resolve(res.size)
+ })
+ })
+ },
+ // 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用
+ hide() {
+ this.clearTimer()
+ },
+ clearTimer() {
+ // #ifdef APP-NVUE
+ this.opacity = 0;
+ // #endif
+ this.isShow = false
+ // 清除定时器
+ clearTimeout(this.timer)
+ this.timer = null
+ }
+ },
+ // #ifdef VUE2
+ beforeDestroy() {
+ this.clearTimer()
+ },
+ // #endif
+ // #ifdef VUE3
+ unmounted() {
+ this.clearTimer()
+ }
+ // #endif
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-toast-color: #fff !default;
+ $uv-toast-border-radius: 4px !default;
+ $uv-toast-border-background-color: #585858 !default;
+ $uv-toast-border-font-size: 14px !default;
+ $uv-toast-border-padding: 12px 20px !default;
+ $uv-toast-loading-border-padding: 20px 20px !default;
+ $uv-toast-content-text-color: #fff !default;
+ $uv-toast-content-text-font-size: 15px !default;
+ $uv-toast-uv-icon: 10rpx !default;
+ $uv-toast-uv-type-primary-color: $uv-primary !default;
+ $uv-toast-uv-type-primary-background-color: #ecf5ff !default;
+ $uv-toast-uv-type-primary-border-color: rgb(215, 234, 254) !default;
+ $uv-toast-uv-type-primary-border-width: 1px !default;
+ $uv-toast-uv-type-success-color: $uv-success !default;
+ $uv-toast-uv-type-success-background-color: #dbf1e1 !default;
+ $uv-toast-uv-type-success-border-color: #BEF5C8 !default;
+ $uv-toast-uv-type-success-border-width: 1px !default;
+ $uv-toast-uv-type-error-color: $uv-error !default;
+ $uv-toast-uv-type-error-background-color: #fef0f0 !default;
+ $uv-toast-uv-type-error-border-color: #fde2e2 !default;
+ $uv-toast-uv-type-error-border-width: 1px !default;
+ $uv-toast-uv-type-warning-color: $uv-warning !default;
+ $uv-toast-uv-type-warning-background-color: #fdf6ec !default;
+ $uv-toast-uv-type-warning-border-color: #faecd8 !default;
+ $uv-toast-uv-type-warning-border-width: 1px !default;
+ $uv-toast-uv-type-default-color: #fff !default;
+ $uv-toast-uv-type-default-background-color: #585858 !default;
+ .uv-toast {
+ &__content {
+ @include flex;
+ padding: $uv-toast-border-padding;
+ border-radius: $uv-toast-border-radius;
+ background-color: $uv-toast-border-background-color;
+ color: $uv-toast-color;
+ align-items: center;
+ /* #ifndef APP-NVUE */
+ max-width: 600rpx;
+ /* #endif */
+ position: relative;
+ &--loading {
+ flex-direction: column;
+ padding: $uv-toast-loading-border-padding;
+ }
+ &__text {
+ color: $uv-toast-content-text-color;
+ font-size: $uv-toast-content-text-font-size;
+ line-height: $uv-toast-content-text-font-size;
+ &--default {
+ color: $uv-toast-content-text-color;
+ }
+ &--error {
+ color: $uv-error;
+ }
+ &--primary {
+ color: $uv-primary;
+ }
+ &--success {
+ color: $uv-success;
+ }
+ &--warning {
+ color: $uv-warning;
+ }
+ }
+ }
+ }
+ .uv-type-primary {
+ color: $uv-toast-uv-type-primary-color;
+ background-color: $uv-toast-uv-type-primary-background-color;
+ border-color: $uv-toast-uv-type-primary-border-color;
+ border-width: $uv-toast-uv-type-primary-border-width;
+ }
+ .uv-type-success {
+ color: $uv-toast-uv-type-success-color;
+ background-color: $uv-toast-uv-type-success-background-color;
+ border-color: $uv-toast-uv-type-success-border-color;
+ border-width: 1px;
+ }
+ .uv-type-error {
+ color: $uv-toast-uv-type-error-color;
+ background-color: $uv-toast-uv-type-error-background-color;
+ border-color: $uv-toast-uv-type-error-border-color;
+ border-width: $uv-toast-uv-type-error-border-width;
+ }
+ .uv-type-warning {
+ color: $uv-toast-uv-type-warning-color;
+ background-color: $uv-toast-uv-type-warning-background-color;
+ border-color: $uv-toast-uv-type-warning-border-color;
+ border-width: 1px;
+ }
+ .uv-type-default {
+ color: $uv-toast-uv-type-default-color;
+ background-color: $uv-toast-uv-type-default-background-color;
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-toast/package.json b/uni_modules/uv-toast/package.json
new file mode 100644
index 0000000..c77987b
--- /dev/null
+++ b/uni_modules/uv-toast/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-toast",
+ "displayName": "uv-toast 消息提示 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.3",
+ "description": "Toast 组件主要用于消息通知、加载提示、操作结果提示等醒目提示效果,我们为其提供了多种丰富的API。",
+ "keywords": [
+ "uv-toast",
+ "uvui",
+ "uv-ui",
+ "toast",
+ "消息提示"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-overlay",
+ "uv-loading-icon",
+ "uv-gap"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-toast/readme.md b/uni_modules/uv-toast/readme.md
new file mode 100644
index 0000000..a95a5ae
--- /dev/null
+++ b/uni_modules/uv-toast/readme.md
@@ -0,0 +1,11 @@
+## Toast 消息提示
+
+> **组件名:uv-toast**
+
+Toast 组件主要用于消息通知、加载提示、操作结果提示等醒目提示效果,我们为其提供了多种丰富的API。
+
+### <a href="https://www.uvui.cn/components/toast.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-toolbar/components/uv-toolbar/props.js b/uni_modules/uv-toolbar/components/uv-toolbar/props.js
index 64a17df..e332412 100644
--- a/uni_modules/uv-toolbar/components/uv-toolbar/props.js
+++ b/uni_modules/uv-toolbar/components/uv-toolbar/props.js
@@ -28,7 +28,7 @@
// 确认按钮的颜色
confirmColor: {
type: String,
- default: '#EE2626'
+ default: '#3c9cff'
},
// 标题文字
title: {
diff --git a/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue b/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue
index 39ba4cd..0b3fc9e 100644
--- a/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue
+++ b/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue
@@ -47,7 +47,7 @@
* @property {String} cancelText 取消按钮的文字(默认 '取消' )
* @property {String} confirmText 确认按钮的文字(默认 '确认' )
* @property {String} cancelColor 取消按钮的颜色(默认 '#909193' )
- * @property {String} confirmColor 确认按钮的颜色(默认 '#EE2626' )
+ * @property {String} confirmColor 确认按钮的颜色(默认 '#3c9cff' )
* @property {String} title 标题文字
* @event {Function}
* @example
@@ -78,7 +78,7 @@
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
$uv-tips-color: #909193 !default;
$uv-main-color: #303133 !default;
- $uv-primary: #EE2626 !default;
+ $uv-primary: #3c9cff !default;
.uv-toolbar {
height: 42px;
@include flex;
diff --git a/uni_modules/uv-toolbar/readme.md b/uni_modules/uv-toolbar/readme.md
index 9e04f39..b33643f 100644
--- a/uni_modules/uv-toolbar/readme.md
+++ b/uni_modules/uv-toolbar/readme.md
@@ -19,7 +19,7 @@
| cancelText | String | '取消' | 取消按钮的文字 |
| confirmText | String | '确定' | 确定按钮的文字 |
| cancelColor | String | '#909193' | 取消按钮的颜色 |
-| confirmColor | String | '#EE2626' | 确认按钮的颜色 |
+| confirmColor | String | '#3c9cff' | 确认按钮的颜色 |
| title | String | - | 标题文字 |
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
diff --git a/uni_modules/uv-tooltip/changelog.md b/uni_modules/uv-tooltip/changelog.md
new file mode 100644
index 0000000..5009f54
--- /dev/null
+++ b/uni_modules/uv-tooltip/changelog.md
@@ -0,0 +1,13 @@
+## 1.0.5(2023-07-02)
+修改VUE3模式不显示的BUG
+## 1.0.4(2023-07-02)
+uv-tooltip 由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。
+## 1.0.3(2023-05-17)
+1. 修复报错的BUG
+## 1.0.2(2023-05-17)
+1. vue2模式下报错的BUG修复
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-tooltip 长按提示
diff --git a/uni_modules/uv-tooltip/components/uv-tooltip/props.js b/uni_modules/uv-tooltip/components/uv-tooltip/props.js
new file mode 100644
index 0000000..4130df5
--- /dev/null
+++ b/uni_modules/uv-tooltip/components/uv-tooltip/props.js
@@ -0,0 +1,60 @@
+export default {
+ props: {
+ // 需要显示的提示文字
+ text: {
+ type: [String, Number],
+ default: ''
+ },
+ // 点击复制按钮时,复制的文本,为空则使用text值
+ copyText: {
+ type: [String, Number],
+ default: ''
+ },
+ // 文本大小
+ size: {
+ type: [String, Number],
+ default: 14
+ },
+ // 字体颜色
+ color: {
+ type: String,
+ default: '#606266'
+ },
+ // 弹出提示框时,文本的背景色
+ bgColor: {
+ type: String,
+ default: 'transparent'
+ },
+ // 弹出提示的方向,top-上方,bottom-下方
+ direction: {
+ type: String,
+ default: 'top'
+ },
+ // 弹出提示的z-index,nvue无效
+ zIndex: {
+ type: [String, Number],
+ default: 10071
+ },
+ // 是否显示复制按钮
+ showCopy: {
+ type: Boolean,
+ default: true
+ },
+ // 扩展的按钮组
+ buttons: {
+ type: Array,
+ default: () => []
+ },
+ // 是否显示透明遮罩以防止触摸穿透
+ overlay: {
+ type: Boolean,
+ default: true
+ },
+ // 是否显示复制成功或者失败的toast
+ showToast: {
+ type: Boolean,
+ default: true
+ },
+ ...uni.$uv?.props?.tooltip
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue b/uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue
new file mode 100644
index 0000000..6e762d7
--- /dev/null
+++ b/uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue
@@ -0,0 +1,372 @@
+<template>
+ <view
+ class="uv-tooltip"
+ :style="[$uv.addStyle(customStyle)]"
+ >
+ <uv-overlay
+ :show="showTooltip && tooltipTop !== -10000 && overlay && timeout>0"
+ customStyle="backgroundColor: rgba(0, 0, 0, 0)"
+ @click="overlayClickHandler"
+ ></uv-overlay>
+ <view class="uv-tooltip__wrapper">
+ <template v-if="isShow">
+ <text
+ class="uv-tooltip__wrapper__text"
+ :id="textId"
+ :ref="textId"
+ :userSelect="false"
+ :selectable="false"
+ @click.stop="longpressHandler"
+ :style="{
+ color: color,
+ backgroundColor: bgColor && showTooltip && tooltipTop !== -10000 ? bgColor : 'transparent'
+ }"
+ >{{ text }}</text>
+ </template>
+ <template v-else>
+ <text
+ class="uv-tooltip__wrapper__text"
+ :id="textId"
+ :ref="textId"
+ :userSelect="false"
+ :selectable="false"
+ @longpress.stop="longpressHandler"
+ :style="{
+ color: color,
+ backgroundColor: bgColor && showTooltip && tooltipTop !== -10000 ? bgColor : 'transparent'
+ }"
+ >{{ text }}</text>
+ </template>
+ <uv-transition
+ mode="fade"
+ :show="showTooltip"
+ :duration="300"
+ :customStyle="{
+ position: 'absolute',
+ top: $uv.addUnit(tooltipTop),
+ zIndex: zIndex,
+ ...tooltipStyle
+ }"
+ >
+ <view
+ class="uv-tooltip__wrapper__popup"
+ :id="tooltipId"
+ :ref="tooltipId"
+ >
+ <view
+ class="uv-tooltip__wrapper__popup__indicator"
+ hover-class="uv-tooltip__wrapper__popup__indicator--hover"
+ v-if="showCopy || buttons.length"
+ :style="[indicatorStyle, {
+ width: $uv.addUnit(indicatorWidth),
+ height: $uv.addUnit(indicatorWidth),
+ }]"
+ >
+ <!-- 由于nvue不支持三角形绘制,这里就做一个四方形,再旋转45deg,得到露出的一个三角 -->
+ </view>
+ <view class="uv-tooltip__wrapper__popup__list">
+ <view
+ v-if="showCopy"
+ class="uv-tooltip__wrapper__popup__list__btn"
+ hover-class="uv-tooltip__wrapper__popup__list__btn--hover"
+ @tap="setClipboardData"
+ >
+ <text
+ class="uv-tooltip__wrapper__popup__list__btn__text"
+ >复制</text>
+ </view>
+ <uv-line
+ direction="column"
+ color="#8d8e90"
+ v-if="showCopy && buttons.length > 0"
+ length="18"
+ ></uv-line>
+ <block v-for="(item , index) in buttons" :key="index">
+ <view
+ class="uv-tooltip__wrapper__popup__list__btn"
+ hover-class="uv-tooltip__wrapper__popup__list__btn--hover"
+ >
+ <text
+ class="uv-tooltip__wrapper__popup__list__btn__text"
+ @tap="btnClickHandler(index)"
+ >{{ item }}</text>
+ </view>
+ <uv-line
+ direction="column"
+ color="#8d8e90"
+ v-if="index < buttons.length - 1"
+ length="18"
+ ></uv-line>
+ </block>
+ </view>
+ </view>
+ </uv-transition>
+ </view>
+ </view>
+</template>
+
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * Tooltip
+ * @description
+ * @tutorial https://www.uvui.cn/components/tooltip.html
+ * @property {String | Number} text 需要显示的提示文字
+ * @property {String | Number} copyText 点击复制按钮时,复制的文本,为空则使用text值
+ * @property {String | Number} size 文本大小(默认 14 )
+ * @property {String} color 字体颜色(默认 '#606266' )
+ * @property {String} bgColor 弹出提示框时,文本的背景色(默认 'transparent' )
+ * @property {String} direction 弹出提示的方向,top-上方,bottom-下方(默认 'top' )
+ * @property {String | Number} zIndex 弹出提示的z-index,nvue无效(默认 10071 )
+ * @property {Boolean} showCopy 是否显示复制按钮(默认 true )
+ * @property {Array} buttons 扩展的按钮组
+ * @property {Boolean} overlay 是否显示透明遮罩以防止触摸穿透(默认 true )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @event {Function}
+ * @example
+ */
+ export default {
+ name: 'uv-tooltip',
+ emits: ['click'],
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ // 是否展示气泡
+ showTooltip: true,
+ // 生成唯一id,防止一个页面多个组件,造成干扰
+ textId: '',
+ tooltipId: '',
+ // 初始时甚至为很大的值,让其移到屏幕外面,为了计算元素的尺寸
+ tooltipTop: -10000,
+ // 气泡的位置信息
+ tooltipInfo: {
+ width: 0,
+ left: 0
+ },
+ // 文本的位置信息
+ textInfo: {
+ width: 0,
+ left: 0
+ },
+ // 三角形指示器的样式
+ indicatorStyle: {},
+ // 气泡在可能超出屏幕边沿范围时,重新定位后,距离屏幕边沿的距离
+ screenGap: 12,
+ // 三角形指示器的宽高,由于对元素进行了角度旋转,精确计算指示器位置时,需要用到其尺寸信息
+ indicatorWidth: 14,
+ timeout: 0
+ }
+ },
+ watch: {
+ propsChange() {
+ this.getElRect()
+ },
+ showTooltip(newVal){
+ if(!newVal) this.timeout = 0;
+ }
+ },
+ computed: {
+ isShow(){
+ const isPC = this.$uv.sys()?.model == 'PC';
+ if(isPC) {
+ return true;
+ }
+ // #ifdef MP-WEIXIN || H5
+ return false;
+ // #endif
+ return true;
+ },
+ // 特别处理H5的复制,因为H5浏览器是自带系统复制功能的,在H5环境
+ // 当一些依赖参数变化时,需要重新计算气泡和指示器的位置信息
+ propsChange() {
+ return [this.text, this.buttons]
+ },
+ // 计算气泡和指示器的位置信息
+ tooltipStyle() {
+ const style = {};
+ if (this.tooltipInfo.width / 2 > this.textInfo.left + this.textInfo.width / 2 - this.screenGap) {
+ this.indicatorStyle = {}
+ style.left = `-${this.$uv.addUnit(this.textInfo.left - this.screenGap)}`
+ this.indicatorStyle.left = this.$uv.addUnit(this.textInfo.width / 2 - this.$uv.getPx(style.left) - this.indicatorWidth /
+ 2)
+ } else if (this.tooltipInfo.width / 2 > this.$uv.sys().windowWidth - this.textInfo.right + this.textInfo.width / 2 -
+ this.screenGap) {
+ this.indicatorStyle = {}
+ style.right = `-${this.$uv.addUnit(this.$uv.sys().windowWidth - this.textInfo.right - this.screenGap)}`
+ this.indicatorStyle.right = this.$uv.addUnit(this.textInfo.width / 2 - this.$uv.getPx(style.right) - this
+ .indicatorWidth / 2)
+ } else {
+ const left = Math.abs(this.textInfo.width / 2 - this.tooltipInfo.width / 2)
+ style.left = this.textInfo.width > this.tooltipInfo.width ? this.$uv.addUnit(left) : -this.$uv.addUnit(left)
+ this.indicatorStyle = {}
+ }
+ if (this.direction === 'top') {
+ style.marginTop = `-${10 + this.tooltipInfo.height}px`
+ this.indicatorStyle.bottom = '-4px'
+ } else {
+ style.marginTop = `${this.tooltipInfo.height-10}px`
+ this.indicatorStyle.top = '-4px'
+ }
+ return style
+ }
+ },
+ created() {
+ this.textId = this.$uv.guid();
+ this.tooltipId = this.$uv.guid();
+ },
+ mounted() {
+ this.init()
+ },
+ methods: {
+ init() {
+ this.getElRect()
+ },
+ // 长按触发事件
+ async longpressHandler(e) {
+ this.tooltipTop = 0
+ this.showTooltip = true
+ setTimeout(()=>{
+ this.timeout = 1;
+ },300)
+ },
+ // 点击透明遮罩
+ overlayClickHandler() {
+ this.showTooltip = false
+ },
+ // 点击弹出按钮
+ btnClickHandler(index) {
+ this.showTooltip = false
+ // 如果需要展示复制按钮,此处index需要加1,因为复制按钮在第一个位置
+ this.$emit('click', this.showCopy ? index + 1 : index)
+ },
+ // 查询内容高度
+ queryRect(ref) {
+ // #ifndef APP-NVUE
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`#${ref}`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(this.$refs[ref], res => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ },
+ // 元素尺寸
+ getElRect() {
+ // 调用之前,先将指示器调整到屏幕外,方便获取尺寸
+ this.showTooltip = true
+ this.tooltipTop = -10000
+ this.$uv.sleep(500).then(() => {
+ this.queryRect(this.tooltipId).then(size => {
+ this.tooltipInfo = size
+ // 获取气泡尺寸之后,将其隐藏,为了让下次切换气泡显示与隐藏时,有淡入淡出的效果
+ this.showTooltip = false
+ })
+ this.queryRect(this.textId).then(size => {
+ this.textInfo = size
+ })
+ })
+ },
+ // 复制文本到粘贴板
+ setClipboardData() {
+ // 关闭组件
+ this.showTooltip = false
+ this.$emit('click', 0)
+ uni.setClipboardData({
+ // 优先使用copyText字段,如果没有,则默认使用text字段当做复制的内容
+ data: this.copyText || this.text,
+ success: () => {
+ this.showToast && this.$uv.toast('复制成功')
+ },
+ fail: () => {
+ this.showToast && this.$uv.toast('复制失败')
+ },
+ complete: () => {
+ this.showTooltip = false
+ }
+ })
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+
+ .uv-tooltip {
+ position: relative;
+ @include flex;
+
+ &__wrapper {
+ @include flex;
+ justify-content: center;
+ /* #ifndef APP-NVUE */
+ white-space: nowrap;
+ /* #endif */
+
+ &__text {
+ font-size: 14px;
+ }
+
+ &__popup {
+ @include flex;
+ justify-content: center;
+
+ &__list {
+ background-color: #060607;
+ position: relative;
+ flex: 1;
+ border-radius: 5px;
+ padding: 0px 0;
+ @include flex(row);
+ align-items: center;
+ overflow: hidden;
+
+ &__btn {
+ padding: 11px 13px;
+
+ &--hover {
+ background-color: #58595B;
+ }
+
+ &__text {
+ line-height: 12px;
+ font-size: 13px;
+ color: #FFFFFF;
+ }
+ }
+ }
+
+ &__indicator {
+ position: absolute;
+ background-color: #060607;
+ width: 14px;
+ height: 14px;
+ bottom: -4px;
+ transform: rotate(45deg);
+ border-radius: 2px;
+ z-index: -1;
+
+ &--hover {
+ background-color: #58595B;
+ }
+ }
+ }
+ }
+ }
+</style>
diff --git a/uni_modules/uv-tooltip/package.json b/uni_modules/uv-tooltip/package.json
new file mode 100644
index 0000000..092dd03
--- /dev/null
+++ b/uni_modules/uv-tooltip/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-tooltip",
+ "displayName": "uv-tooltip 长按提示",
+ "version": "1.0.5",
+ "description": "Tooltip组件主要用于长按操作,类似微信的长按气泡。",
+ "keywords": [
+ "uv-tooltip",
+ "uvui",
+ "uv-ui",
+ "tooltip",
+ "长按提示"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-overlay",
+ "uv-transition",
+ "uv-line"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "n"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-tooltip/readme.md b/uni_modules/uv-tooltip/readme.md
new file mode 100644
index 0000000..9a97fcb
--- /dev/null
+++ b/uni_modules/uv-tooltip/readme.md
@@ -0,0 +1,11 @@
+## Tooltip 长按提示
+
+> **组件名:uv-tooltip**
+
+Tooltip组件主要用于长按操作,类似微信的长按气泡。
+
+### <a href="https://www.uvui.cn/components/tooltip.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-ui/changelog.md b/uni_modules/uv-ui/changelog.md
new file mode 100644
index 0000000..fce7860
--- /dev/null
+++ b/uni_modules/uv-ui/changelog.md
@@ -0,0 +1,353 @@
+## 1.1.20(2024-01-20)
+1. 优化 uv-loading-page重构,避免初始加载的时候先显示页面的问题,小程序平台由于性能问题,可能还是会短暂出现,请注意优化处理
+2. 优化 uv-textarea confirmType默认值改成return,支持换行
+3. 优化 uv-ui-tools luch-request更新
+4. 修复 uv-drop-down parentData不变的BUG
+5. 修复 uv-picker 上个版本引出的BUG
+6. 修复 uv-image 设置show-menu-by-longpress不生效的BUG
+7. 修复 uv-upload 动态设置deletable为false不生效的BUG
+## 1.1.19(2023-12-12)
+1. 优化 uv-toast部分重构,支持取消遮罩不影响点击其他元素 a. 增加z-index参数;b. 增加overlay参数
+2. 优化 关于组件如何阻止事件冒泡,增加相关说明:[组件怎么阻止事件冒泡](https://www.uvui.cn/components/feature.html#组件怎么阻止事件冒泡)
+3. 优化 持续优化文档
+4. 修复 uv-tabs current为字符串activeStyle不生效的BUG
+5. 修复 uv-vtabs 在change回调中设置current,快速点击菜单,导致死循环的BUG
+6. 修复 uv-cell right-icon插槽编译到APP端不显示的BUG,问题来源:[https://gitee.com/climblee/uv-ui/issues/I8LXZI](https://gitee.com/climblee/uv-ui/issues/I8LXZI)
+## 1.1.18(2023-11-28)
+0. 优化 uv-datetime-picker 增加round属性,设置弹窗圆角
+1. 优化 uv-subsection 增加customItemStyle属性,方便修改bar样式,比如设置圆角
+2. 修复 uv-picker issues反馈的问题uv-picker在组合式API的自定义组件中,columns动态赋值无法显示选项:[https://gitee.com/climblee/uv-ui/issues/I8H0GQ](https://gitee.com/climblee/uv-ui/issues/I8H0GQ)
+3. 修复 uv-popup issues问题:[https://gitee.com/climblee/uv-ui/issues/I8HDLO](https://gitee.com/climblee/uv-ui/issues/I8HDLO)
+4. 修复 uv-skeletons 支付宝小程序报错的BUG
+5. 修复 uv-image webp之前未使用的BUG & 微信报错的BUG
+6. 修复 uv-modal 上版本引出的确认和取消按钮均不显示,还有高度的BUG
+## 1.1.17(2023-11-10)
+0. 交流反馈 欢迎加入uv-ui官方群1交流反馈: 549833913(1000+)
+1. 交流反馈 欢迎加入uv-ui官方群2交流反馈: 206060892
+2. 新增 新增其他小组件下载入口:其他小组件
+3. 优化 uv-calendars 增加readonly属性,是否为只读状态,只读状态下禁止选择日期
+4. 修复 uv-search 禁用时,不能触发click的BUG
+5. 修复 uv-checkbox and uv-radio label文字过长,不换行的相关说明及处理
+6. 修复 uv-input and uv-search 点击clear按钮在微信小程序不好触发的BUG
+7. 修复 uv-list 修复设置ellipsis不生效的BUG
+8. 修复 uv-index-list 修复sticky属性不生效的BUG
+## 1.1.16(2023-10-30)
+1. 交流反馈 欢迎加入uv-ui官方群1交流反馈: 549833913(940+/1000)
+2. 交流反馈 欢迎加入uv-ui官方群2交流反馈: 206060892
+3. 新增 uv-skeletons 骨架屏,全新升级骨架屏,更加灵活,体验更加,强烈推荐使用新版骨架屏。一般用于页面在请求远程数据尚未完成时,在内容加载出来前展示与内容布局结构一致的灰白块,提升用户视觉体验。
+4. 优化 uv-button 增加后置插槽suffix,方便在按钮文字后面增加图标等
+5. 优化 uv-popup vue模式内容有背景色,设置圆角被遮挡的情况
+6. 优化 uv-tabs 点击一个选项,change事件重复派发的问题
+7. 优化 持续优化文档及其他
+8. 修复 uv-album 设置singleSize、multipleSize、space等值带单位,存在不显示的BUG
+9. 修复 uv-picker 省市级示例设置defaultValue,再次选择第二列错乱的BUG
+10. 修复 uv-icon imgMode默认值改成aspectFit,否则会导致支付宝平台name设置为本地图片显示不全的BUG
+11. 修复 uv-transition 在APP-IOS上不能正常显示的BUG
+12. 修复 uv-toast、uv-swipe-action、uv-sticky、uv-notify、uv-notice-bar、uv-grid、uv-count-down、uv-code等组件,unmounted兼容vue3
+## 1.1.15(2023-10-12)
+1. 优化 uv-keyboard a. 增加disKeys参数,mode = "car"下,被禁用的键,如:['I','O']; b. 增加customabc参数,mode = "car"下,是否启用自定义中英文切换内容模式,为了兼容支付宝等小程序不兼容嵌套插槽,导致同时显示自定义内容和原始内容; c. 增加ref方法changeCarMode,mode = "car"下, 调用此方法可以切换中英文模式; d. 增加@changeCarInputMode,mode = "car"下,调用此方法可以进行切换中英文; e. 增加插槽abc,mode = "car"下,自定义中英文切换内容
+2. 优化 uv-checkbox uv-radio 优化:https://gitee.com/climblee/uv-ui/issues/I872VD
+3. 优化 uv-picker 将immediate-change默认值改为true,该值在于change回调的及时性,微信小程序生效
+4. 优化 uv-tags 兼容customStyle参数等优化
+5. 修复 uv-transition 部分情况,修改某属性自动关闭的BUG
+6. 修复 uv-calendars 懒加载报错:https://gitee.com/climblee/uv-ui/issues/I869JS
+7. 修复 uv-datetime-picker 设置minDate出现选择错乱的BUG
+8. 修复 uv-input 搜狗输入法下存在不可清空的情况
+9. 修复 uv-calendars selected没有设置了info或者info设置为空字符串后,文本则无法恢复BUG
+## 1.1.14(2023-09-27)
+1. 优化 uv-list-item 可使用customStyle变量进行样式控制
+2. 优化 uv-cell 增加cellStyle参数,方便自定义单元格的样式
+3. 优化 uv-switch 优化细节
+4. 优化 不断优化[文档](https://www.uvui.cn/)
+5. 修复 uv-button 通过customStyle修改按钮宽度,组件中最外层节点不改变的问题
+6. 修复 uv-calendars a. 修复range模式下,selected设置了info后选中后,导致文本不恢复的问题;b. 修复multiple模式下,selected自定义信息的颜色没变,依然是白色
+7. 修复 uv-checkbox uv-checkbox-group之change回调中v-model值不更新的BUG
+## 1.1.13(2023-09-15)
+1. 优化 uv-button a. 增加参数iconSize,用于控制图标的大小;b. 增加open-type="agreePrivacyAuthorization"类型,用户同意隐私协议事件回调
+2. 优化 uv-picker 三级联动的案例:[https://www.uvui.cn/components/picker.html#省市区三级联动](https://www.uvui.cn/components/picker.html#省市区三级联动)
+3. 修复 uv-read-more 全局设置rpx时,导致展开高度不对的BUG
+4. 修复 uv-tabs a. 设置lineWidth未带单位产生的误差BUG;b. 首次加载时,处理下划线会有左到右的过渡效果
+5. 修复 uv-textaera 设置autoHeight后出现高度异常的BUG
+6. 修复 uv-input H5等情况设置禁用或可读情况下,点击事件无效的问题,nvue需要特殊处理
+7. 修复 uv-calendars a. 在vue2+小程序渲染时闪烁的问题;b. 增加allowSameDay参数,是否允许日期范围的起止时间为同一天,mode=range时有效
+8. 修复 uv-safe-bottom 兼容飞书小程序
+9. 修复 uv-album 添加依赖,避免导入运行有误
+10. 修复 uv-ui-tools 优化组件用到的相关
+## 1.1.12(2023-09-10)
+1. 修复 uv-popup a. h5初始zIndex错误的问题;b. 修复全局设置prop无效的问题
+2. 修复 uv-button 修复多个按钮由view包裹,显示在一行宽度不正常的BUG
+3. 修复 uv-modal a. 修复两个按钮之间竖线不显示的问题;b. uv-ui项目自定义按钮示例修复
+4. 修复 uv-calendars 修复国际化失效的BUG
+5. 修复 uv-keyboard 修复键盘change回调事件产生冲突的BUG
+## 1.1.11(2023-09-02)
+1. 优化 uv-calendars a. 去除range参数,由mode="range"替换;b. 新增mode参数,不传 / multiple / range,分别为单日期, 多个日期,选择日期范围;c. 与uv-calendar选择日期的功能保持一致
+2. 优化 uv-modal a. 增加align参数,设置文本对齐方式;b. 增加textStyle参数,扩展文本样式
+3. 优化 uv-datetime-picker a. 增加mode="year"模式,方便只选择年;b. 增加clearDate参数,是否清除上次选择
+4. 修复 uv-ui-tools 设置customstyle同名计算属性报错:The computed property "customStyle" is already defined as a prop
+5. 修复 uv-image a. 设置widthFix时出现显示不全的BUG;b. 修复抖音等平台在width和height属性改变时出现不显示的BUG
+6. 修复 uv-checkbox 点击空隙处或label插槽内容不会选中的问题
+7. 修复 uv-radio 点击空隙处或label插槽内容不会选中的问题
+8. 修复 uv-calendars 在pages.json中设置easycom会报错的BUG
+9. 修复 uv-index-list 设置customNavHeight导致定位不准确的BUG
+## 1.1.10(2023-08-30)
+1. 交流反馈 欢迎加入uv-ui官方群1交流反馈: 549833913
+2. 交流反馈 欢迎加入uv-ui官方群2交流反馈: 206060892
+3. 优化 uv-calendars 1. 去除range参数,由mode="range"替换;2. 新增mode参数,不传 / multiple / range,分别为单日期, 多个日期,选择日期范围;3. 与uv-calendar选择日期的功能保持一致
+4. 新增 uv-album 新增相册组件及相关文档
+5. 优化 其他优化
+6. 修复 uv-text app-nvue设置align不生效的BUG
+7. 修复 uv-drop-down 自定义内容,点击自定义内容时会自动关闭弹窗的问题
+8. 修复 uv-image 异步修改宽高不生效的问题,问题来源:https://gitee.com/climblee/uv-ui/issues/I7WUQ3
+9. 修复 uv-calendars 通过setConfig修改属性不生效的问题,出自评论区:https://ext.dcloud.net.cn/plugin?id=12287
+10. 修复 uv-list 设置边框不生效的BUG
+## 1.1.9(2023-08-27)
+1. 优化 uv-calendars 1. 去除range参数,由mode="range"替换;2. 新增mode参数,不传 / multiple / range,分别为单日期, 多个日期,选择日期范围;3. 与uv-calendar选择日期的功能保持一致
+2. 优化 uv-picker 增加round属性,设置圆角
+3. 修复 uv-calendars 点击返回今天按钮时,monthSwitch方法回调参数返回月份不是当天对应月份
+4. 修复 uv-radio 1. 设置 labelSize 属性设置无效的问题:https://gitee.com/climblee/uv-ui/issues/I7W6UN;2. v-model 绑定布尔值控制台报警:https://gitee.com/climblee/uv-ui/issues/I7W714
+5. 修复 uv-checkbox 1. 设置 label 属性为布尔值不生效的BUG
+## 1.1.8(2023-08-24)
+1. 优化 uv-popup 弹出不丝滑优化思路:https://www.uvui.cn/components/popup.html#yh
+2. 修复 uv-switch 取消value传值,只能使用v-model传值,避免异步操作不生效的BUG
+3. 修复 uv-index-list ios端滚动过程中+快速点击右侧导航页面出现空白的BUG
+4. 修复 uv-rate 1. 支付宝报错的BUG; 2. 不能选半星的BUG
+5. 修复 uv-model 异步loading时,确认回调还会一直触发的BUG
+6. 修复 uv-swiper 标题文字过多未隐藏掉的BUG
+7. 修复 uv-text app-nvue编译不能自动换行的BUG
+## 1.1.7(2023-08-22)
+1. 优化 uv-drop-down a. 增加@change回调,返回弹窗关闭状态; b. 增加init方法,方便位置改变进行调整
+2. 优化 部分文档优化
+3. 修复 uv-input a. app-nvue-ios端不能输入的BUG;b. 键盘高度等值不返回BUG
+4. 修复 uv-scroll-list 报错导致不能移动指示器的BUG
+5. 修复 uv-search 边距值在上次更新中误改导致不对的BUG
+6. 修复 uv-image 设置width和height为100%不生效的BUG
+## 1.1.6(2023-08-18)
+1. 优化 优化文档
+2. 修复 uv-list 使用列表右侧显示 switch,switchChange回调中返回数据为undefined的BUG
+3. 修复 uv-checkbox 数据多不换行的BUG
+4. 修复 uv-upload 1. 图片预览位置错误的BUG;2. 视频预览不生效的BUG;3. 改变上传视频宽高不生效的BUG
+5. 修复 uv-navbar 在部分ios高版本机型,返回按钮不好操作的问题
+6. 修复 uv-waterfall 只有一条数据的时候,切换的时候数据会左右显示错误的BUG
+## 1.1.5(2023-08-14)
+1. 优化 uv-pick-color 删除scrollTop参数,内部修改后就不需要了
+2. 优化 uv-loading-icon 增加textStyle参数,可自定义文本样式,比如给上边距
+3. 修复 uv-safe-bottom 百度小程序报错的BUG
+4. 修复 uv-form 设置labelWidth属性时,节点渲染有闪动的BUG
+5. 修复 uv-grid 设置col属性时,节点渲染有闪动的BUG
+6. 修复 uv-parse 阻止a标签跳转文档说明
+## 1.1.4(2023-08-13)
+1. 优化 nvue自定义图标 [详细文档-nvue中自定义图标库](https://www.uvui.cn/guide/customIcon.html#nvue%E4%B8%AD%E8%87%AA%E5%AE%9A%E4%B9%89%E5%9B%BE%E6%A0%87%E5%BA%93)
+2. 优化 uv.$uv.http 在APP.vue页面使用报错的BUG: [Api集中管理](https://www.uvui.cn/js/http.html#_3-api%E9%9B%86%E4%B8%AD%E7%AE%A1%E7%90%86)
+3. 修复 uv-navbar app-nvue运行ios存在背景图片错乱的问题
+4. 修复 uv-list app-nvue运行ios存在,分包页面不滚动
+5. 修复 uv-textarea 值为null或undefined时显示错误的bug
+6. 修复 uv-search 值为null或undefined时显示错误的bug
+7. 修复 uv-scroll-list vue2编译报错的BUG
+8. 修复 uv-calendars 选择月份弹窗层级的问题
+9. 修复 uv-form 动画在vue3 setup语法糖中错乱,以及表单其他相关问题解决: [Issues](https://gitee.com/my_dear_li_pan/uv-ui/issues/I7SNTT)
+10. 修复 uv-picker-color 滚动页面无法点击的BUG:增加scrollTop参数,设置滚动条的位置。不设置如果页面出现滚动就需要传该值,会出现颜色面板无法进行选颜色的情况。
+11. 交流反馈 欢迎加入uv-ui官方群1交流反馈: [549833913](https://www.uvui.cn/components/addQQGroup.html)
+12. 交流反馈 欢迎加入uv-ui官方群2交流反馈: [206060892](https://www.uvui.cn/components/addQQGroup.html)
+## 1.1.3(2023-08-06)
+1. 优化 uv-calendars 1. 增加startText参数; 2. 增加endText参数; 3. 增加selected中的参数; 4. 优化日历范围选择
+2. 优化 uv-empty icon属性支持base64图片
+3. 优化 uv-navbar 增加背景图片的裁剪模式参数imgMode
+4. 优化 uv-picker-color 颜色值不对的BUG
+5. 优化 [API文档优化](https://www.uvui.cn/components/changelog.html)
+6. 优化 常见问题增加:[怎么隐藏uv-tabs等组件的滚动条](https://www.uvui.cn/components/problem.html#%E4%B9%9D%E3%80%81%E6%80%8E%E4%B9%88%E9%9A%90%E8%97%8Fuv-tabs%E7%AD%89%E7%BB%84%E4%BB%B6%E7%9A%84%E6%BB%9A%E5%8A%A8%E6%9D%A1)
+7. 修复 uv-radio name为数字0时不能选中的BUG
+8. 修复 uv-textarea 1. v-model设置为数据时的BUG;2. 复制过多内容,计数显示错误的BUG;3. maxlength为-1改成不显示计数
+9. 修复 uv-code-input 在vue2模式下,v-model设置为0时不生效的BUG
+10. 修复 uv-input 在vue2模式下,v-model设置为0时不生效的BUG
+11. 修复 uv-search 在vue2模式下,v-model设置为0时不生效的BUG
+12. 修复 uv-ui-tools 1. 路由拦截修复;2. 增加events参数
+## 1.1.2(2023-08-03)
+1. 新增 uv-calendars 新版日历发布
+2. 新增 uv-toolbar 组件独立发布,老用户更新uv-picker,需要手动删除uv-picker目录下的uv-toolbar目录,否则会有冲突提示
+3. 优化 uv-tags 增加cellChild参数
+4. 优化 uv-navbar 兼容背景图片
+5. 优化 uv-notice-bar 竖向滚动时候增加change回调
+## 1.1.1(2023-07-30)
+1. 新增 uv-drop-down 下拉筛选组件,兼容app-nvue及多端
+2. 优化 uv-textarea 增加confirm-hold参数,方便设置进行换行处理
+3. 优化 其他关于文档的优化等
+## 1.1.0(2023-07-26)
+1. 重构 uv-list 全面重构,提高性能,放弃使用scroll-view,具体文档参考:uv-list列表
+2. 优化 uv-search 1. 增加prefix和suffix 前置和后置插槽;2. 增加boxStyle参数,方便控制输入框部分的样式
+3. 优化 文档优化:获取节点布局信息,文档新增nvue获取方式的说明
+## 1.0.22(2023-07-26)
+1. 优化 uv-textarea 组件 增加textStyle和countStyle属性,方便控制文本样式
+2. 优化 uv-swiper 增加竖向播放属性:vertical
+3. 优化 uv-icon 支持base64图片格式
+4. 优化 uv-transition 和 uv-image 增加参数cellChild属性,避免nvue中出现回收后不显示的BUG
+5. 优化 uv-button 增加customTextStyle属性,方便自定义按钮文字样式
+6. 优化 优化部分文档说明
+7. 修复 uv-slider 修改背景颜色属性为backgroundColor,避免设置不生效
+8. 修复 uv-index-list 1. 修复全局设置成rpx存在的高度BUG;2. 修复其他BUG
+## 1.0.21(2023-07-22)
+1. 新增 uv-scroll-list 横向滚动列表组件
+2. 优化 增加测试占位图,方便开发者使用线上图片进行测试:[https://www.uvui.cn/components/testPic.html](https://www.uvui.cn/components/testPic.html)
+3. 优化 uv-calendar 组件文档示例等优化,增加setFormatter说明
+4. 优化 uv-notice-bar 优化文档,说明不显示左边图标的使用方法
+5. 修复 uv-input 在微信小程序端清除内容存在不能清除的BUG
+6. 修复 uv-button 1. 解决微信小程序动态设置hover-class点击态不消失的BUG; 2. 文档优化
+7. 修复 uv-waterfall 在tab切换等场景快速切换时,会出现报错的BUG
+8. 优化 优化其他
+## 1.0.20(2023-07-18)
+1. 修复 uv-textarea 设置-1不生效
+2. 修复 uv-icon 恢复uv-empty相关的图标
+3. 修复 uv-empty 恢复设置mode属性的内置图标
+4. 优化 [优化文档](https://www.uvui.cn)
+## 1.0.19(2023-07-14)
+1. 优化 uv-waterfall 当changeList未处理数据时,正确返回对应列的数据,避免误导
+2. 修复 uv-rate VUE3模式下设置value属性不生效的BUG
+3. 修复 uv-input VUE3模式下设置value属性不生效的BUG
+4. 修复 uv-search VUE3模式下设置value属性不生效的BUG
+5. 修复 uv-code-input VUE3模式下设置value属性不生效的BUG
+6. 修复 uv-number-box VUE3模式下设置value属性不生效的BUG
+7. 修复 uv-radio VUE3模式下设置value属性不生效的BUG
+8. 修复 uv-checkbox VUE3模式下设置value属性不生效的BUG
+9. 修复 uv-textarea VUE3模式下设置value属性不生效的BUG
+10. 修复 uv-switch VUE3模式下设置value属性不生效的BUG
+11. 修复 uv-slider VUE3模式下设置value属性不生效的BUG
+12. 修复 uv-datetime-picker VUE3模式下设置value属性不生效的BUG
+13. 修复 uv-icon 部分图标错误的BUG
+## 1.0.18(2023-07-06)
+1. 优化 uv-icon 1. 更新图标,删除一些不常用的图标;2. 删除base64,修改成ttf文件引入读取图标。uv-icon 图标
+2. 优化 uv-icon nvue自定义图标用法,文档说明:[点击跳转](https://www.uvui.cn/guide/customIcon.html)
+3. 优化 uv-upload 文档示例代码,增加fileList参数说明:[点击跳转](https://www.uvui.cn/components/upload.html#filelist-options)
+4. 修复 uv-checkbox vue3模式下,动态修改v-model绑定的值无效的BUG
+5. 修复 uv-radio vue3模式下,动态修改v-model绑定的值无效的BUG
+6. 修复 uv-datetime-picker vue3模式下,动态修改v-model绑定的值无效的BUG
+## 1.0.17(2023-07-04)
+1. 优化 uv-icon 修复,NVUE平台主题颜色在APP不生效的BUG
+2. 优化 uv-notice-bar 优化,增加disableScroll属性
+3. 优化 uv-input uv-back-top uv-cell uv-form uv-search uv-modal uv-navbar uv-index-list uv-empty uv-upload 去除插槽判断,避免某些平台不显示的BUG
+4. 优化 uv-form 优化文档
+5. 优化 优化其他相关文档
+## 1.0.16(2023-07-03)
+1. 优化 uv-transition 动画组件,代码重构优化,性能更加友好,增加自定义动画功能。详情[参考文档](https://www.uvui.cn/components/transition.html)
+2. 优化 uv-popup 弹出层,代码重构优化,性能翻倍,小程序体验性能更加,避免卡顿。打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/popup.html)
+3. 优化 uv-calendar 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/actionSheet.html)
+4. 优化 uv-action-sheet 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/calendar.html)
+5. 优化 uv-datetime-picker 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/datetimePicker.html)
+6. 优化 uv-form 由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。
+7. 优化 uv-keyboard 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/keyboard.html)
+8. 优化 uv-modal 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/modal.html)
+9. 优化 uv-notify 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/notify.html)
+10. 优化 uv-overlay 由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。
+11. 优化 uv-pick-color 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/pickColor.html)
+12. 优化 uv-picker 由于弹出层uv-popup的修改,打开和关闭方法更改,详情[参考文档](https://www.uvui.cn/components/picker.html)
+13. 优化 uv-tooltip 由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。
+14. 优化 uv-loading-page 由于弹出层uv-transition的修改,组件内部做了相应的修改,参数不变。
+15. 优化 相关文档的优化更改。
+16. 修复 uv-safe-bottom 修复,在百度程序,抖音小程序不生效的BUG
+## 1.0.15(2023-06-29)
+1. 欢迎加QQ群交流:[549833913](https://www.uvui.cn/components/addQQGroup.html)
+2. 优化 uv-swiper 优化:1. 增加titleStyle属性,方便修改标题样式;2. 标题上去掉是否是图片的判断,避免无后缀的图片不显示
+3. 优化 uv-steps 优化:1. 增加插槽title; 3. 文档关于插槽相关的参数说明完善;增加customStyle属性
+4. 优化 uv-checkbox 优化:增加label文字插槽,与radio保持一致,优化文档相关说明
+5. 优化 uv-modal 优化:增加closeLoading方法,方便异步加载手动取消加载状态,更新文档
+6. 优化 uv-image 增加文档说明:uv-list、 uv-waterfall等组件在 Android平台使用了list封装,所以在该组件中仍然不能使用uv-image等组件
+7. 优化 优化更多文档
+8. 修复 uv-vtabs 修复非联动情况下,内容过多的情况,滚动一段距离,再切换未滚动到顶部的BUG
+9. 修复 uv-image 修复:duration属性不生效的BUG
+10. 修复 uv-code-input 修复:使用:disabledKeyboard="true"属性,事件全部失效的BUG
+11. 修复 uv-button 修复:设置open-type="chooseAvatar"等值不生效的BUG
+## 1.0.14(2023-06-25)
+1. 欢迎加QQ群交流:[549833913](https://www.uvui.cn/components/addQQGroup.html)
+2. 优化 uv-count-down 增加外部样式customStyle参数
+3. 优化 文档的全面优化
+4. 修复 uv-count-to 1. 修复继续滚动的函数 2. 修改文档错误 4. 适配px和rpx的单位 4. 适配customStyle参数
+5. 修复 uv-load-more 修复customStyle参数设置背景等不生效的BUG
+6. 修复 uv-code-input 优化下边框
+7. 修复 uv-tabs 添加uv-icon依赖
+8. 修复 uv-grid 优化修改
+9. 修复 uv-cell 优化修改
+## 1.0.13(2023-06-20)
+1. 优化 uv-calendar formatter格式化中增加topInfo参数
+2. 优化 uv-tabs 增加customStyle参数
+3. 优化 文档优化,便于开发者直接开干
+4. 优化 uv-switch 优化size属性,适配单位传递
+5. 修复 uv-ui-tools、uv-form、uv-picker 修复vue3编译支付宝异常
+6. 修复 uv-ui-tools、uv-form、uv-picker 修复vue3编译支付宝异常
+7. 修复 uv-parse 修复在nvue不显示的BUG
+8. 修复 uv-form 修复某些条件下报错的BUG
+## 1.0.12(2023-06-14)
+1. 优化部分组件,优化文档部分细节
+2. uv-popup、uv-modal 修复遮罩层zIndex问题
+3. uv-form 在vue3的setup语法中ref使用uvForm会导致报错
+4. uv-tabs activeStyle设置字体大小,可能会导致下划线位置不对BUG
+5. uv-pick-color 百度小程序点击报错
+6. uv-transition 恢复this.$nextTick
+7. uv-picker 抖音小程序选择的时候报错,导致不能关闭的BUG
+8. uv-checkbox 多余的属性labelDisabled,导致APP中报错提示
+9. uv-tabbar 底部安全距离组件无效的BUG
+10. uv-vtabs 头部存在的时候,联动不准确的BUG
+## 1.0.11(2023-06-12)
+1. uv-radio-group、uv-checkbox-group 兼容自定义样式customStyle,方便通过样式调整整体位置等,数据较多时允许换行
+2. uv-ui-tools 优化内置样式等,解决微信小程序使用uvui提示 Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors,[详情](https://www.uvui.cn/components/problem.html)
+3. uv-datetime-picker 取消defaultIndex参数,目前传该值也没实际意义
+4. uv-tabbar 增加iconSize参数
+5. uv-calendar 增加change回调
+6. uv-calendar 修复BUG
+7. uv-rate 修复只读或禁止状态下设置value无效的问题
+8. uv-popup 修复zIndex问题
+9. uv-modal 修复zIndex问题
+10. 文档-扩展配置更新:[扩展配置](https://www.uvui.cn/components/setting.html)
+11. 文档-优化更新:[uv-ui文档](https://www.uvui.cn/components/changelog.html)
+12. 文档-新增常见问题:[常见问题](https://www.uvui.cn/components/problem.html)
+13. 优化其他
+## 1.0.10(2023-06-05)
+1. uv-navbar 渐变背景色兼容
+2. uv-calendar 日历选择BUG修复
+## 1.0.9(2023-06-05)
+1. 新增uv-vtabs垂直选项卡组件,主要用于分类展示,分类切换功能,支持联动和不联动两种模式
+2. uv-qrcode,uv-datetime-picker,uv-subsection等文档说明优化,避免开发困难;优化API相关说明
+3. uv-notice-bar 1. 修复在触发error函数报错的BUG;2. 修复在text值为undefined的时候,解决报错BUG
+4. uv-button 等组件修复触发两次事件的BUG
+5. uv-datetime-picker 1. 修复重置值存在不更新的BUG;2. 优化文档,增加filter使用方法说明
+6. uv-badge 修复type等属性为null或undefined的时候不显示徽标的BUG
+7. uv-ui-tools 优化工具组件,兼容更多功能,小程序分享功能优化等
+...
+## 1.0.8(2023-05-27)
+1. uv-waterfall修复在百度小程序中可能存在的BUG;去掉原有的slot方式
+2. uv-image修复可能报错的问题
+3. uv-pick-color 在文档预览模式中无法点击的问题
+4. uv-index-list 修复select事件不触发的问题
+5. 优化其他组件及示例项目等
+## 1.0.7(2023-05-25)
+1. uv-icon 将线上ttf字体包替换成base64,避免加载时或者网络差时候显示白色方块
+2. uv-text 去掉多余的data-index属性,避免警告
+3. uv-upload 在fileList的watch中增加deep属性
+4. uv-pick-color 去掉template中存在的this.导致头条小程序编译警告
+5. uv-image 去掉template中存在的this.导致头条小程序编译警告
+## 1.0.6(2023-05-23)
+1. 新增uv-pick-color颜色选择器组件
+2. uv-toolbar组件增加showBorder属性,是否显示下边框
+3. uv-transition组件在百度小程序等平台不支持this.$nextick导致下面的逻辑不执行,使用延时替换方案
+4. uv-ui-tools组件中bem()函数兼容百度/头条小程序等
+5. uv-waterfall组件修复在百度/头条小程序显示异常等BUG,增加changeList回调函数处理数据,同步更新示例等
+6. uv-image组件修复在百度/头条小程序等开启observeLazyLoad后显示异常BUG
+7. uv-tabs组件修复上次更新导致的在nvue中不滚动的BUG
+8. uv-qrcode组件修复在部分平台不显示加载的BUG
+9. 修复其他已知问题等
+## 1.0.5(2023-05-17)
+1. 新增uv-qrcode二维码组件
+2. 修复uv-tooltip在vue2模式下的BUG
+3. 优化部分问题
+## 1.0.4(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.3(2023-05-12)
+1. 修复uv-input在vue3模式下双向绑定问题
+2. 修复uv-textarea在vue3模式下双向绑定问题
+3. 修复uv-rate在vue3模式下双向绑定问题
+## 1.0.2(2023-05-11)
+1. 更新文档
+2. 增加插件下载入口
+## 1.0.1(2023-05-10)
+1. 所有组件依赖
+2. 上传示例项目
+## 1.0.0(2023-05-10)
+1. uv-ui
diff --git a/uni_modules/uv-ui/components/uv-ui/uv-ui.vue b/uni_modules/uv-ui/components/uv-ui/uv-ui.vue
new file mode 100644
index 0000000..e023095
--- /dev/null
+++ b/uni_modules/uv-ui/components/uv-ui/uv-ui.vue
@@ -0,0 +1,7 @@
+<template>
+ <view>占位组件,请勿使用;如需下载示例项目,请使用【下载插件并导入HBuilderX】或【使用 HBuilderX 导入示例项目】或【下载示例项目ZIP】</view>
+</template>
+<script>
+</script>
+<style>
+</style>
diff --git a/uni_modules/uv-ui/package.json b/uni_modules/uv-ui/package.json
new file mode 100644
index 0000000..5e919a2
--- /dev/null
+++ b/uni_modules/uv-ui/package.json
@@ -0,0 +1,162 @@
+{
+ "id": "uv-ui",
+ "displayName": "uv-ui 破釜沉舟之兼容vue3+2、app、h5、小程序等多端,灵活导入,利剑出击",
+ "version": "1.1.20",
+ "description": "uv-ui 是基于uni-app、部分组件基于uView2.x、全端兼容、支持独立导入、内容丰富的UI框架。破釜沉舟之兼容vue3+2、app、h5、小程序等多端,利剑出击,开箱即用。",
+ "keywords": [
+ "uv-ui",
+ "uvui",
+ "UI组件库",
+ "ui框架",
+ "ui库"
+ ],
+ "repository": "https://github.com/climblee/uv-ui",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "无"
+ },
+ "npmurl": "https://www.npmjs.com/package/@climblee/uv-ui"
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-skeletons",
+ "uv-album",
+ "uv-drop-down",
+ "uv-calendars",
+ "uv-scroll-list",
+ "uv-vtabs",
+ "uv-pick-color",
+ "uv-qrcode",
+ "uv-ui-tools",
+ "uv-action-sheet",
+ "uv-alert",
+ "uv-avatar",
+ "uv-back-top",
+ "uv-badge",
+ "uv-button",
+ "uv-calendar",
+ "uv-cell",
+ "uv-checkbox",
+ "uv-code",
+ "uv-code-input",
+ "uv-collapse",
+ "uv-count-down",
+ "uv-count-to",
+ "uv-datetime-picker",
+ "uv-divider",
+ "uv-empty",
+ "uv-form",
+ "uv-gap",
+ "uv-grid",
+ "uv-icon",
+ "uv-image",
+ "uv-index-list",
+ "uv-input",
+ "uv-keyboard",
+ "uv-line",
+ "uv-line-progress",
+ "uv-link",
+ "uv-list",
+ "uv-loading-icon",
+ "uv-loading-page",
+ "uv-load-more",
+ "uv-modal",
+ "uv-navbar",
+ "uv-no-network",
+ "uv-notice-bar",
+ "uv-notify",
+ "uv-number-box",
+ "uv-overlay",
+ "uv-parse",
+ "uv-picker",
+ "uv-popup",
+ "uv-radio",
+ "uv-rate",
+ "uv-read-more",
+ "uv-row",
+ "uv-safe-bottom",
+ "uv-search",
+ "uv-skeleton",
+ "uv-slider",
+ "uv-status-bar",
+ "uv-steps",
+ "uv-sticky",
+ "uv-subsection",
+ "uv-swipe-action",
+ "uv-swiper",
+ "uv-switch",
+ "uv-tabbar",
+ "uv-tabs",
+ "uv-tags",
+ "uv-text",
+ "uv-textarea",
+ "uv-toast",
+ "uv-tooltip",
+ "uv-transition",
+ "uv-upload",
+ "uv-waterfall"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-ui/readme.md b/uni_modules/uv-ui/readme.md
new file mode 100644
index 0000000..9dfe1df
--- /dev/null
+++ b/uni_modules/uv-ui/readme.md
@@ -0,0 +1,164 @@
+<p align="center">
+ <span> </span><img alt="logo" src="https://www.uvui.cn/common/logo.png" width="120" height="120">
+</p>
+<h3 align="center">uv-ui</h3>
+<h3 align="center">兼容vue3+2多平台快速开发的UI框架</h3>
+
+[](https://gitee.com/climblee/uv-ui)
+[](https://gitee.com/climblee/uv-ui)
+[](https://github.com/climblee/uv-ui)
+[](https://github.com/climblee/uv-ui/issues)
+[](https://www.uvui.cn)
+[](https://www.uvui.cn/components/changelog.html)
+[](https://en.wikipedia.org/wiki/MIT_License)
+
+## 温馨提示:如需下载uv-ui示例项目,请不要使用【下载插件ZIP】按钮。
+
+### uvui官方群1:<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=aaoyDvfV7nSee9vzWfzyZM1vKlu3xrNZ&authKey=pdU6HDpyzGUoc1QvQvCmzCbwzsoHgBbToF%2F0ChP4lNjPIPgWHGRE4I99XYGiTcNI&noverify=0&group_code=549833913" target="_blank">549833913</a>
+### uvui官方群2:<a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=2QbEeKBn6ysCyQ09V4mgst1W8onxFybQ&authKey=aMTGL5zCYwsinG%2FeH0qMlAqXAdyKr5AjXVf2oMnsmj7NCg%2F2HraMU%2FNFxELLIPvp&noverify=0&group_code=206060892" target="_blank">206060892</a>
+
+## uvui特点
+
+1. **uv-ui的前世今生**,`uv-ui` 是基于 `uview2.x` 版本改造而来。重命名也是为了避开发布冲突和很多组件 `u-`在 `nvue` 中不能使用的情况,所以这才诞生了`uv-ui`。感谢 `uview-ui` 作者的开源奉献,再次为开源点赞。 同时 `uv-ui` 也是无条件开源。
+
+2. **全端兼容**,`uv-ui`支持vue3、vue2、app-vue、app-nvue、h5、小程序等。`uv-ui`的组件都是多端自适应的,底层会抹平很多小程序平台的差异或bug。
+
+3. **扩展配置**,`uv-ui`内置的方法默认不再挂载到`uni`对象之上,也就意味着默认情况下不能在项目中直接使用`uni.$uv.xxx`使用内置方法。但是可以通过扩展可以解决,通过如下方式进行配置即可,使用方式请参考[扩展配置](https://www.uvui.cn/components/setting.html)。其中包括[ JS工具库](https://www.uvui.cn/components/setting.html#%E6%89%A9%E5%B1%95%E9%85%8D%E7%BD%AE-js%E5%B7%A5%E5%85%B7%E5%BA%93)、[ 自定义主题](https://www.uvui.cn/components/setting.html#%E6%89%A9%E5%B1%95%E9%85%8D%E7%BD%AE-%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%BB%E9%A2%98)、[ 基础样式](https://www.uvui.cn/components/setting.html#%E6%89%A9%E5%B1%95%E9%85%8D%E7%BD%AE-%E5%9F%BA%E7%A1%80%E6%A0%B7%E5%BC%8F)、[ setconfig](https://www.uvui.cn/components/setting.html#%E6%89%A9%E5%B1%95%E9%85%8D%E7%BD%AE-setconfig)等。
+
+## 预览
+
+通过微信(APP下载不支持微信扫码)或浏览器扫码查看演示效果。
+
+<img src="https://mp-31ed6237-7b7b-4597-8448-a53d6d6031cf.cdn.bspapp.com/uvui/download-wx.png" width="200" /> <img src="https://mp-31ed6237-7b7b-4597-8448-a53d6d6031cf.cdn.bspapp.com/uvui/download-app.png" width="200" /> <img src="https://mp-31ed6237-7b7b-4597-8448-a53d6d6031cf.cdn.bspapp.com/uvui/download-h5.png" width="200" />
+
+## 链接
+
+- [官方文档](https://www.uvui.cn)
+- [演示地址](https://h5.uvui.cn)
+- [更新日志](https://www.uvui.cn/components/changelog.html)
+- [关于我们](https://www.uvui.cn/cooperation/about.html)
+- <a href="#list">组件列表</a>
+
+## 交流反馈
+
+欢迎加入我们的QQ群交流反馈:[点此跳转](https://www.uvui.cn/components/addQQGroup.html)
+
+## 快速开始
+
+方式一:`uv-ui` 强烈建议通过 `下载插件并导入HbuilderX` 导入组件。
+
+方式二:下载完整 [uv-ui项目](https://ext.dcloud.net.cn/plugin?id=12287) 将 `uni_modules` 复制到自己的项目。
+
+方式三:通过 `npm i @climblee/uv-ui` 下载,此方法需要配置 easycom,配置详情可查看[安装](https://www.uvui.cn/components/install.html)。
+
+请通过[快速上手](https://www.uvui.cn/components/quickstart.html)了解更详细的内容。
+
+**注意:导入插件后,建议`HBuilderX`重新运行项目,可能新导入的插件不能实时更新而导致不能运行。**
+
+## 使用方法
+
+组件导入 `uni_modules` 后,直接在项目中使用,无需通过import引入组件。
+
+```html
+<template>
+ <uv-icon name="baidu" size="30" color="#909399"></uv-icon>
+</template>
+```
+
+## 扩展功能
+
+`uv-ui` 内置了强大的工具函数、请求封装等,可以根据自身需求进行扩展配置,详情请查看[扩展配置](https://www.uvui.cn/components/setting.html)。
+
+**注意:只有[扩展配置](https://www.uvui.cn/components/setting.html)后才能在自己的项目页面中使用组件库内置方法和变量等**。
+
+<div id="list"></div>
+
+## 组件列表
+
+下表为 `uv-ui` 的扩展组件清单,点击每个组件**点击下载&安装**即可在详情页面导入组件到项目下,导入后建议重新运行即可直接使用,组件无需import和注册。
+
+| 组件名 | 组件说明 |
+| --- | --- |
+| uv-skeletons | [新版骨架屏(推荐)](https://www.uvui.cn/components/skeletons.html) |
+| uv-calendars | [新版日历(推荐)](https://www.uvui.cn/components/calendars.html) |
+| uv-drop-down | [下拉筛选](https://www.uvui.cn/components/dropDown.html) |
+| uv-scroll-list | [横向滚动列表](https://www.uvui.cn/components/scrollList.html) |
+| uv-vtabs | [垂直选项卡](https://www.uvui.cn/components/vtabs.html) |
+| uv-pick-color | [颜色选择器](https://www.uvui.cn/components/pickColor.html) |
+| uv-qrcode | [二维码](https://www.uvui.cn/components/qrcode.html) |
+| uv-waterfall | [瀑布流](https://www.uvui.cn/components/waterfall.html) |
+| uv-row | [Layout 布局](https://www.uvui.cn/components/layout.html) |
+| uv-icon | [图标](https://www.uvui.cn/components/icon.html) |
+| uv-button | [按钮](https://www.uvui.cn/components/button.html) |
+| uv-text | [文本](https://www.uvui.cn/components/text.html) |
+| uv-link | [超链接](https://www.uvui.cn/components/link.html) |
+| uv-image | [图片](https://www.uvui.cn/components/image.html) |
+| uv-transition | [动画](https://www.uvui.cn/components/transition.html) |
+| uv-form | [表单](https://www.uvui.cn/components/form.html) |
+| uv-input | [增强输入框](https://www.uvui.cn/components/input.html) |
+| uv-textarea | [增强文本域](https://www.uvui.cn/components/textarea.html) |
+| uv-checkbox | [复选框](https://www.uvui.cn/components/checkbox.html) |
+| uv-radio | [单选框](https://www.uvui.cn/components/radio.html) |
+| uv-switch | [开关选择器](https://www.uvui.cn/components/switch.html) |
+| uv-calendar | [日历](https://www.uvui.cn/components/calendar.html) |
+| uv-picker | [选择器](https://www.uvui.cn/components/picker.html) |
+| uv-datetime-picker | [时间选择器](https://www.uvui.cn/components/datetimePicker.html) |
+| uv-code | [验证码倒计时](https://www.uvui.cn/components/code.html) |
+| uv-keyboard | [键盘](https://www.uvui.cn/components/keyboard.html) |
+| uv-rate | [评分](https://www.uvui.cn/components/rate.html) |
+| uv-search | [多功能搜索框](https://www.uvui.cn/components/search.html) |
+| uv-number-box | [步进器](https://www.uvui.cn/components/numberBox.html) |
+| uv-upload | [上传](https://www.uvui.cn/components/upload.html) |
+| uv-slider | [滑动选择器](https://www.uvui.cn/components/slider.html) |
+| uv-list | [列表](https://www.uvui.cn/components/list.html) |
+| uv-index-list | [索引列表](https://www.uvui.cn/components/indexList.html) |
+| uv-tags | [标签](https://www.uvui.cn/components/tag.html) |
+| uv-line-progress | [线形进度条](https://www.uvui.cn/components/lineProgress.html) |
+| uv-badge | [徽标数](https://www.uvui.cn/components/badge.html) |
+| uv-count-down | [倒计时](https://www.uvui.cn/components/countDown.html) |
+| uv-count-to | [数字滚动](https://www.uvui.cn/components/countTo.html) |
+| uv-avatar | [头像](https://www.uvui.cn/components/avatar.html) |
+| uv-skeleton | [骨架屏](https://www.uvui.cn/components/skeleton.html) |
+| uv-loading-icon | [加载动画](https://www.uvui.cn/components/loadingIcon.html) |
+| uv-loading-page | [加载页](https://www.uvui.cn/components/loadingPage.html) |
+| uv-load-more | [加载更多](https://www.uvui.cn/components/loadMore.html) |
+| uv-empty | [内容为空](https://www.uvui.cn/components/empty.html) |
+| uv-tooltip | [长按提示](https://www.uvui.cn/components/tooltip.html) |
+| uv-alert | [警告提示](https://www.uvui.cn/components/alert.html) |
+| uv-toast | [消息提示](https://www.uvui.cn/components/toast.html) |
+| uv-notice-bar | [滚动通知](https://www.uvui.cn/components/noticeBar.html) |
+| uv-notify | [消息提示](https://www.uvui.cn/components/notify.html) |
+| uv-no-network | [无网络提示](https://www.uvui.cn/components/noNetwork.html) |
+| uv-popup | [弹出层](https://www.uvui.cn/components/popup.html) |
+| uv-modal | [模态框](https://www.uvui.cn/components/modal.html) |
+| uv-cell | [单元格](https://www.uvui.cn/components/cell.html) |
+| uv-swipe-action | [滑动单元格](https://www.uvui.cn/components/swipeAction.html) |
+| uv-swiper | [轮播图](https://www.uvui.cn/components/swiper.html) |
+| uv-collapse | [折叠面板](https://www.uvui.cn/components/collapse.html) |
+| uv-grid | [宫格布局](https://www.uvui.cn/components/grid.html) |
+| uv-album | [相册](https://www.uvui.cn/components/album.html) |
+| uv-tabbar | [底部导航栏](https://www.uvui.cn/components/tabbar.html) |
+| uv-back-top | [返回顶部](https://www.uvui.cn/components/backTop.html) |
+| uv-navbar | [自定义导航栏](https://www.uvui.cn/components/navbar.html) |
+| uv-action-sheet | [底部操作菜单](https://www.uvui.cn/components/actionSheet.html) |
+| uv-tabs | [标签选项卡](https://www.uvui.cn/components/tabs.html) |
+| uv-steps | [步骤条](https://www.uvui.cn/components/steps.html) |
+| uv-subsection | [分段器](https://www.uvui.cn/components/subsection.html) |
+| uv-sticky | [吸顶](https://www.uvui.cn/components/sticky.html) |
+| uv-parse | [富文本解析器](https://www.uvui.cn/components/parse.html) |
+| uv-overlay | [遮罩层](https://www.uvui.cn/components/overlay.html) |
+| uv-code-input | [验证码输入](https://www.uvui.cn/components/codeInput.html) |
+| uv-read-more | [展开阅读更多](https://www.uvui.cn/components/readMore.html) |
+| uv-line | [线条](https://www.uvui.cn/components/line.html) |
+| uv-gap | [间隔槽](https://www.uvui.cn/components/gap.html) |
+| uv-divider | [分割线](https://www.uvui.cn/components/divider.html) |
+
+## 版权信息
+uv-ui遵循[MIT](https://en.wikipedia.org/wiki/MIT_License)开源协议,意味着您无需支付任何费用,也无需授权,即可将uv-ui应用到您的产品中。
+
+## 作者想说
+- 开源真的不易,不图大家的钱财,所以希望大家多多鼓励支持,希望不要恶意评论,有问题加群快速解决。
+- 遇到BUG,是一件很正常的事情,是程序肯定就有BUG,所以希望大家能以理解的心态去提出BUG,然后作者才有动力去努力修复。
+- 最后觉得好用的小伙伴,不要吝啬你的双手,给个好评就是给我们最大的鼓励。
+
+# 恶评者手下留情,有事加QQ群解决:549833913
\ No newline at end of file
diff --git a/uni_modules/uv-upload/changelog.md b/uni_modules/uv-upload/changelog.md
new file mode 100644
index 0000000..dfcefe7
--- /dev/null
+++ b/uni_modules/uv-upload/changelog.md
@@ -0,0 +1,17 @@
+## 1.0.6(2023-12-20)
+1. 修复动态设置deletable为false不生效的BUG
+## 1.0.5(2023-08-31)
+1. 添加uv-popup依赖
+## 1.0.4(2023-08-18)
+1. 修复图片预览位置错误的BUG
+2. 修复视频预览不生效的BUG
+3. 修复改变上传视频宽高不生效的BUG
+## 1.0.3(2023-07-03)
+去除插槽判断,避免某些平台不显示的BUG
+## 1.0.2(2023-05-24)
+1. 优化fileList,watch中增加deep属性
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-upload 上传
diff --git a/uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue b/uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue
new file mode 100644
index 0000000..327c680
--- /dev/null
+++ b/uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue
@@ -0,0 +1,52 @@
+<template>
+ <uv-popup ref="popup" @change="change">
+ <view class="video-view" v-if="show">
+ <video class="video" :src="getSec" :autoplay="autoplay"></video>
+ </view>
+ </uv-popup>
+</template>
+<script>
+ export default {
+ props: {
+ src: {
+ type: String,
+ default: ''
+ },
+ autoplay: {
+ type: Boolean,
+ default: true
+ }
+ },
+ data() {
+ return {
+ videoSrc: '',
+ show: false
+ }
+ },
+ computed: {
+ getSec() {
+ return this.src || this.videoSrc;
+ }
+ },
+ methods: {
+ open(url) {
+ this.videoSrc = url;
+ this.$refs.popup.open();
+ },
+ close() {
+ this.$refs.popup.close();
+ },
+ change(e) {
+ this.show = e.show;
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ .video-view {
+ width: 750rpx;
+ .video {
+ width: 750rpx;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-upload/components/uv-upload/mixin.js b/uni_modules/uv-upload/components/uv-upload/mixin.js
new file mode 100644
index 0000000..13ab0d2
--- /dev/null
+++ b/uni_modules/uv-upload/components/uv-upload/mixin.js
@@ -0,0 +1,22 @@
+import { error } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
+export default {
+ watch: {
+ // 监听accept的变化,判断是否符合个平台要求
+ // 只有微信小程序才支持选择媒体,文件类型,所以这里做一个判断提示
+ accept: {
+ immediate: true,
+ handler(val) {
+ // #ifndef MP-WEIXIN
+ if (val === 'all' || val === 'media') {
+ error('只有微信小程序才支持把accept配置为all、media之一')
+ }
+ // #endif
+ // #ifndef H5 || MP-WEIXIN
+ if (val === 'file') {
+ error('只有微信小程序和H5(HX2.9.9)才支持把accept配置为file')
+ }
+ // #endif
+ }
+ }
+ }
+}
diff --git a/uni_modules/uv-upload/components/uv-upload/props.js b/uni_modules/uv-upload/components/uv-upload/props.js
new file mode 100644
index 0000000..2aad7b6
--- /dev/null
+++ b/uni_modules/uv-upload/components/uv-upload/props.js
@@ -0,0 +1,130 @@
+export default {
+ props: {
+ // 接受的文件类型, 可选值为all media image file video
+ accept: {
+ type: String,
+ default: 'image'
+ },
+ // 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头
+ capture: {
+ type: [String, Array],
+ default: () => ['album', 'camera']
+ },
+ // 当accept为video时生效,是否压缩视频,默认为true
+ compressed: {
+ type: Boolean,
+ default: true
+ },
+ // 当accept为video时生效,可选值为back或front
+ camera: {
+ type: String,
+ default: 'back'
+ },
+ // 当accept为video时生效,拍摄视频最长拍摄时间,单位秒
+ maxDuration: {
+ type: Number,
+ default: 60
+ },
+ // 上传区域的图标,只能内置图标
+ uploadIcon: {
+ type: String,
+ default: 'camera-fill'
+ },
+ // 上传区域的图标的颜色,默认
+ uploadIconColor: {
+ type: String,
+ default: '#D3D4D6'
+ },
+ // 是否开启文件读取前事件
+ useBeforeRead: {
+ type: Boolean,
+ default: false
+ },
+ // 读取后的处理函数
+ afterRead: {
+ type: Function,
+ default: null
+ },
+ // 读取前的处理函数
+ beforeRead: {
+ type: Function,
+ default: null
+ },
+ // 是否开启图片预览功能
+ previewFullImage: {
+ type: Boolean,
+ default: true
+ },
+ // 是否开启视频预览功能
+ previewFullVideo: {
+ type: Boolean,
+ default: true
+ },
+ // 最大上传数量
+ maxCount: {
+ type: [String, Number],
+ default: 52
+ },
+ // 是否禁用
+ disabled: {
+ type: Boolean,
+ default: false
+ },
+ // 预览上传的图片时的裁剪模式,和image组件mode属性一致
+ imageMode: {
+ type: String,
+ default: 'aspectFill'
+ },
+ // 标识符,可以在回调函数的第二项参数中获取
+ name: {
+ type: String,
+ default: ''
+ },
+ // 所选的图片的尺寸, 可选值为original compressed
+ sizeType: {
+ type: Array,
+ default: () => ['original', 'compressed']
+ },
+ // 是否开启图片多选,部分安卓机型不支持
+ multiple: {
+ type: Boolean,
+ default: false
+ },
+ // 是否展示删除按钮
+ deletable: {
+ type: Boolean,
+ default: true
+ },
+ // 文件大小限制,单位为byte
+ maxSize: {
+ type: [String, Number],
+ default: Number.MAX_VALUE
+ },
+ // 显示已上传的文件列表
+ fileList: {
+ type: Array,
+ default: () => []
+ },
+ // 上传区域的提示文字
+ uploadText: {
+ type: String,
+ default: ''
+ },
+ // 内部预览图片区域和选择图片按钮的区域宽度
+ width: {
+ type: [String, Number],
+ default: 80
+ },
+ // 内部预览图片区域和选择图片按钮的区域高度
+ height: {
+ type: [String, Number],
+ default: 80
+ },
+ // 是否在上传完成后展示预览图
+ previewImage: {
+ type: Boolean,
+ default: true
+ },
+ ...uni.$uv?.props?.upload
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-upload/components/uv-upload/utils.js b/uni_modules/uv-upload/components/uv-upload/utils.js
new file mode 100644
index 0000000..88cb602
--- /dev/null
+++ b/uni_modules/uv-upload/components/uv-upload/utils.js
@@ -0,0 +1,151 @@
+function pickExclude(obj, keys) {
+ // 某些情况下,type可能会为
+ if (!['[object Object]', '[object File]'].includes(Object.prototype.toString.call(obj))) {
+ return {}
+ }
+ return Object.keys(obj).reduce((prev, key) => {
+ if (!keys.includes(key)) {
+ prev[key] = obj[key]
+ }
+ return prev
+ }, {})
+}
+
+function formatImage(res) {
+ return res.tempFiles.map((item) => ({
+ ...pickExclude(item, ['path']),
+ type: 'image',
+ url: item.path,
+ thumb: item.path,
+ size: item.size,
+ // #ifdef H5
+ name: item.name
+ // #endif
+ }))
+}
+
+function formatVideo(res) {
+ return [
+ {
+ ...pickExclude(res, ['tempFilePath', 'thumbTempFilePath', 'errMsg']),
+ type: 'video',
+ url: res.tempFilePath,
+ thumb: res.thumbTempFilePath,
+ size: res.size,
+ // #ifdef H5
+ name: res.name
+ // #endif
+ }
+ ]
+}
+
+function formatMedia(res) {
+ return res.tempFiles.map((item) => ({
+ ...pickExclude(item, ['fileType', 'thumbTempFilePath', 'tempFilePath']),
+ type: res.type,
+ url: item.tempFilePath,
+ thumb: res.type === 'video' ? item.thumbTempFilePath : item.tempFilePath,
+ size: item.size
+ }))
+}
+
+function formatFile(res) {
+ return res.tempFiles.map((item) => ({
+ ...pickExclude(item, ['path']),
+ url: item.path,
+ size:item.size,
+ // #ifdef H5
+ name: item.name,
+ type: item.type
+ // #endif
+ }))
+}
+export function chooseFile({
+ accept,
+ multiple,
+ capture,
+ compressed,
+ maxDuration,
+ sizeType,
+ camera,
+ maxCount
+}) {
+ return new Promise((resolve, reject) => {
+ switch (accept) {
+ case 'image':
+ uni.chooseImage({
+ count: multiple ? Math.min(maxCount, 9) : 1,
+ sourceType: capture,
+ sizeType,
+ success: (res) => resolve(formatImage(res)),
+ fail: reject
+ })
+ break
+ // #ifdef MP-WEIXIN
+ // 只有微信小程序才支持chooseMedia接口
+ case 'media':
+ wx.chooseMedia({
+ count: multiple ? Math.min(maxCount, 9) : 1,
+ sourceType: capture,
+ maxDuration,
+ sizeType,
+ camera,
+ success: (res) => resolve(formatMedia(res)),
+ fail: reject
+ })
+ break
+ // #endif
+ case 'video':
+ uni.chooseVideo({
+ sourceType: capture,
+ compressed,
+ maxDuration,
+ camera,
+ success: (res) => resolve(formatVideo(res)),
+ fail: reject
+ })
+ break
+ // #ifdef MP-WEIXIN || H5
+ // 只有微信小程序才支持chooseMessageFile接口
+ case 'file':
+ // #ifdef MP-WEIXIN
+ wx.chooseMessageFile({
+ count: multiple ? maxCount : 1,
+ type: accept,
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ })
+ // #endif
+ // #ifdef H5
+ // 需要hx2.9.9以上才支持uni.chooseFile
+ uni.chooseFile({
+ count: multiple ? maxCount : 1,
+ type: accept,
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ })
+ // #endif
+ break
+ // #endif
+ default:
+ // 此为保底选项,在accept不为上面任意一项的时候选取全部文件
+ // #ifdef MP-WEIXIN
+ wx.chooseMessageFile({
+ count: multiple ? maxCount : 1,
+ type: 'all',
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ })
+ // #endif
+ // #ifdef H5
+ // 需要hx2.9.9以上才支持uni.chooseFile
+ uni.chooseFile({
+ count: multiple ? maxCount : 1,
+ type: 'all',
+ success: (res) => resolve(formatFile(res)),
+ fail: reject
+ })
+ // #endif
+ }
+ })
+}
diff --git a/uni_modules/uv-upload/components/uv-upload/uv-upload.vue b/uni_modules/uv-upload/components/uv-upload/uv-upload.vue
new file mode 100644
index 0000000..213be0f
--- /dev/null
+++ b/uni_modules/uv-upload/components/uv-upload/uv-upload.vue
@@ -0,0 +1,488 @@
+<template>
+ <view class="uv-upload" :style="[$uv.addStyle(customStyle)]">
+ <view class="uv-upload__wrap">
+ <template v-if="previewImage">
+ <view class="uv-upload__wrap__preview" v-for="(item, index) in lists" :key="index">
+ <image
+ v-if="item.isImage || (item.type && item.type === 'image')"
+ :src="item.thumb || item.url" :mode="imageMode"
+ class="uv-upload__wrap__preview__image"
+ @tap="onPreviewImage(item,index)"
+ :style="[{
+ width: $uv.addUnit(width),
+ height: $uv.addUnit(height)
+ }]"
+ />
+ <view
+ v-else
+ class="uv-upload__wrap__preview__other"
+ @tap="onPreviewVideo(item,index)"
+ :style="[{
+ width: $uv.addUnit(width),
+ height: $uv.addUnit(height)
+ }]"
+ >
+ <uv-icon color="#80CBF9" size="26" :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'"></uv-icon>
+ <text class="uv-upload__wrap__preview__other__text">{{item.isVideo || (item.type && item.type === 'video') ? '视频' : '文件'}}</text>
+ </view>
+ <view class="uv-upload__status" v-if="item.status === 'uploading' || item.status === 'failed'">
+ <view class="uv-upload__status__icon">
+ <uv-icon v-if="item.status === 'failed'" name="close-circle" color="#ffffff" size="25" />
+ <uv-loading-icon size="22" mode="circle" v-else />
+ </view>
+ <text v-if="item.message" class="uv-upload__status__message">{{ item.message }}</text>
+ </view>
+ <view class="uv-upload__deletable" v-if="item.status !== 'uploading' && (deletable || item.deletable)" @tap.stop="deleteItem(index)">
+ <view class="uv-upload__deletable__icon">
+ <uv-icon name="close" color="#ffffff" size="10"></uv-icon>
+ </view>
+ </view>
+ <view class="uv-upload__success" v-if="item.status === 'success'">
+ <!-- #ifdef APP-NVUE -->
+ <image :src="successIcon" class="uv-upload__success__icon"></image>
+ <!-- #endif -->
+ <!-- #ifndef APP-NVUE -->
+ <view class="uv-upload__success__icon">
+ <uv-icon name="checkmark" color="#ffffff" size="12"></uv-icon>
+ </view>
+ <!-- #endif -->
+ </view>
+ </view>
+ </template>
+ <template v-if="isInCount">
+ <view @tap="chooseFile">
+ <slot>
+ <view class="uv-upload__button" :hover-class="!disabled ? 'uv-upload__button--hover' : ''" hover-stay-time="150" @tap.stop="chooseFile" :class="[disabled && 'uv-upload__button--disabled']" :style="[{
+ width: $uv.addUnit(width),
+ height: $uv.addUnit(height)
+ }]">
+ <uv-icon :name="uploadIcon" size="26" :color="uploadIconColor"></uv-icon>
+ <text v-if="uploadText" class="uv-upload__button__text">{{ uploadText }}</text>
+ </view>
+ </slot>
+ </view>
+ </template>
+ </view>
+ <uv-preview-video ref="previewVideo"></uv-preview-video>
+ </view>
+</template>
+
+<script>
+ import { func, image, video, array, promise } from '@/uni_modules/uv-ui-tools/libs/function/test.js';
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import { chooseFile } from './utils';
+ import mixin_accept from './mixin.js';
+ import props from './props.js';
+ /**
+ * upload 上传
+ * @description 该组件用于上传图片场景
+ * @tutorial https://www.uvui.cn/components/upload.html
+ * @property {String} accept 接受的文件类型, 可选值为all media image file video (默认 'image' )
+ * @property {String | Array} capture 图片或视频拾取模式,当accept为image类型时设置capture可选额外camera可以直接调起摄像头(默认 ['album', 'camera'] )
+ * @property {Boolean} compressed 当accept为video时生效,是否压缩视频,默认为true(默认 true )
+ * @property {String} camera 当accept为video时生效,可选值为back或front(默认 'back' )
+ * @property {Number} maxDuration 当accept为video时生效,拍摄视频最长拍摄时间,单位秒(默认 60 )
+ * @property {String} uploadIcon 上传区域的图标,只能内置图标(默认 'camera-fill' )
+ * @property {String} uploadIconColor 上传区域的图标的字体颜色,只能内置图标(默认 #D3D4D6 )
+ * @property {Boolean} useBeforeRead 是否开启文件读取前事件(默认 false )
+ * @property {Boolean} previewFullImage 是否开启图片预览功能(默认 true )
+ * @property {Boolean} previewFullVideo 是否开启视频预览功能(默认 true )
+ * @property {String | Number} maxCount 最大上传数量(默认 52 )
+ * @property {Boolean} disabled 是否启用(默认 false )
+ * @property {String} imageMode 预览上传的图片时的裁剪模式,和image组件mode属性一致(默认 'aspectFill' )
+ * @property {String} name 标识符,可以在回调函数的第二项参数中获取
+ * @property {Array} sizeType 所选的图片的尺寸, 可选值为original compressed(默认 ['original', 'compressed'] )
+ * @property {Boolean} multiple 是否开启图片多选,部分安卓机型不支持 (默认 false )
+ * @property {Boolean} deletable 是否展示删除按钮(默认 true )
+ * @property {String | Number} maxSize 文件大小限制,单位为byte (默认 Number.MAX_VALUE )
+ * @property {Array} fileList 显示已上传的文件列表
+ * @property {String} uploadText 上传区域的提示文字
+ * @property {String | Number} width 内部预览图片区域和选择图片按钮的区域宽度(默认 80 )
+ * @property {String | Number} height 内部预览图片区域和选择图片按钮的区域高度(默认 80 )
+ * @property {Object} customStyle 组件的样式,对象形式
+ * @event {Function} afterRead 读取后的处理函数
+ * @event {Function} beforeRead 读取前的处理函数
+ * @event {Function} oversize 文件超出大小限制
+ * @event {Function} clickPreview 点击预览时触发
+ * @event {Function} delete 删除图片
+ * @example <uv-upload :action="action" :fileList="fileList" ></uv-upload>
+ */
+ export default {
+ name: "uv-upload",
+ // #ifdef VUE3
+ emits: ['error', 'beforeRead', 'oversize', 'afterRead', 'delete', 'clickPreview'],
+ // #endif
+ mixins: [mpMixin, mixin, mixin_accept, props],
+ data() {
+ return {
+ // #ifdef APP-NVUE
+ successIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKKADAAQAAAABAAAAKAAAAAB65masAAACP0lEQVRYCc3YXygsURwH8K/dpcWyG3LF5u/6/+dKVylSypuUl6uUPMifKMWL8oKEB1EUT1KeUPdR3uTNUsSLxb2udG/cbvInNuvf2rVnazZ/ZndmZ87snjM1Z+Z3zpzfp9+Z5mEAhlvjRtZgCKs+gnPAOcAkkMOR4jEHfItjDvgRxxSQD8cM0BuOCaAvXNCBQrigAsXgggYUiwsK0B9cwIH+4gIKlIILGFAqLiBAOTjFgXJxigJp4BQD0sIpAqSJow6kjSNAFTnRaHJwLenD6Mud52VQAcrBfTd2oyq+HtGaGGWAcnAVcXWoM3bCZrdi+ncPfaAcXE5UKVpdW/vitGPqqAtn98d0gXJwX7Qp6MmegUYVhvmTIezdmHlxJCjpHRTCFerLkRRu4k0aqdajN3sWOo0BK//msHa+xDuPC/oNFMKRhTtM4xjIX0SCNpXL4+7VIaHuyiWEp2L7ahWLf8fejfPdqPmC3mJicORZUp1CQzm+GiphvljGk+PBvWRbxii+xVTj5M6CiZ/tsDufvaXyxEUDxeLIyvu3m0iOyEFWVAkydcVYdyFrE9tQk9iMq6f/GNlvwt3LjQfh60LUrw9/cFyyMJUW/XkLSNMV4Mi6C5ML+ui4x5ClAX9sB9w0wV6wglJwJCv5fOxcr6EstgbGiEw4XcfUry4cWrcEUW8n+ARKxXEJHhw2WG43UKSvwI/TSZgvl7kh0b3XLZaLEy0QmMgLZAVH7J+ALOE+AVnDvQOyiPMAWcW5gSzjCPAV+78S5WE0GrQAAAAASUVORK5CYII=',
+ // #endif
+ lists: [],
+ isInCount: true,
+ }
+ },
+ watch: {
+ // 监听文件列表的变化,重新整理内部数据
+ fileList: {
+ deep: true,
+ immediate: true,
+ handler() {
+ this.formatFileList()
+ }
+ },
+ deletable(newVal) {
+ if(!newVal) {
+ this.lists.map(item=>{
+ item.deletable = this.deletable;
+ })
+ }
+ }
+ },
+ methods: {
+ formatFileList() {
+ const {
+ fileList = [], maxCount
+ } = this;
+ const lists = fileList.map((item) =>
+ Object.assign(Object.assign({}, item), {
+ // 如果item.url为本地选择的blob文件的话,无法判断其为video还是image,此处优先通过accept做判断处理
+ isImage: this.accept === 'image' || image(item.url || item.thumb),
+ isVideo: this.accept === 'video' || video(item.url || item.thumb),
+ deletable: typeof(item.deletable) === 'boolean' ? item.deletable : this.deletable,
+ })
+ );
+ this.lists = lists
+ this.isInCount = lists.length < maxCount
+ },
+ chooseFile() {
+ this.timer && clearTimeout(this.timer);
+ this.timer = setTimeout(() => {
+ const {
+ maxCount,
+ multiple,
+ lists,
+ disabled
+ } = this;
+ if (disabled) return;
+ // 如果用户传入的是字符串,需要格式化成数组
+ let capture;
+ try {
+ capture = array(this.capture) ? this.capture : this.capture.split(',');
+ } catch (e) {
+ capture = [];
+ }
+ chooseFile(
+ Object.assign({
+ accept: this.accept,
+ multiple: this.multiple,
+ capture: capture,
+ compressed: this.compressed,
+ maxDuration: this.maxDuration,
+ sizeType: this.sizeType,
+ camera: this.camera,
+ }, {
+ maxCount: maxCount - lists.length,
+ })
+ )
+ .then((res) => {
+ this.onBeforeRead(multiple ? res : res[0]);
+ })
+ .catch((error) => {
+ this.$emit('error', error);
+ });
+ }, 100)
+ },
+ // 文件读取之前
+ onBeforeRead(file) {
+ const {
+ beforeRead,
+ useBeforeRead,
+ } = this;
+ let res = true
+ // beforeRead是否为一个方法
+ if (func(beforeRead)) {
+ // 如果用户定义了此方法,则去执行此方法,并传入读取的文件回调
+ res = beforeRead(file, this.getDetail());
+ }
+ if (useBeforeRead) {
+ res = new Promise((resolve, reject) => {
+ this.$emit(
+ 'beforeRead',
+ Object.assign(Object.assign({
+ file
+ }, this.getDetail()), {
+ callback: (ok) => {
+ ok ? resolve() : reject();
+ },
+ })
+ );
+ });
+ }
+ if (!res) {
+ return;
+ }
+ if (promise(res)) {
+ res.then((data) => this.onAfterRead(data || file));
+ } else {
+ this.onAfterRead(file);
+ }
+ },
+ getDetail(index) {
+ return {
+ name: this.name,
+ index: index == null ? this.fileList.length : index,
+ };
+ },
+ onAfterRead(file) {
+ const {
+ maxSize,
+ afterRead
+ } = this;
+ const oversize = Array.isArray(file) ?
+ file.some((item) => item.size > maxSize) :
+ file.size > maxSize;
+ if (oversize) {
+ this.$emit('oversize', Object.assign({
+ file
+ }, this.getDetail()));
+ return;
+ }
+ if (typeof afterRead === 'function') {
+ afterRead(file, this.getDetail());
+ }
+ this.$emit('afterRead', Object.assign({
+ file
+ }, this.getDetail()));
+ },
+ deleteItem(index) {
+ this.$emit(
+ 'delete',
+ Object.assign(Object.assign({}, this.getDetail(index)), {
+ file: this.fileList[index],
+ })
+ );
+ },
+ // 预览图片
+ onPreviewImage(item, index) {
+ const lists = this.$uv.deepClone(this.lists);
+ lists.map((i,j)=>{
+ if(j == index) {
+ i.current = true;
+ }
+ });
+ const filters = lists.filter(i=>i.isImage);
+ const findIndex = filters.findIndex(i=>i.current);
+ this.onClickPreview(item, index);
+ if (!item.isImage || !this.previewFullImage) return
+ uni.previewImage({
+ // 先filter找出为图片的item,再返回filter结果中的图片url
+ urls: this.lists.filter((item) => this.accept === 'image' || image(item.url || item.thumb)).map((item) => item.url || item.thumb),
+ current: findIndex,
+ fail() {
+ this.$uv.toast('预览图片失败')
+ },
+ });
+ },
+ onPreviewVideo(item, index) {
+ this.onClickPreview(item, index);
+ if (!this.previewFullVideo || !item.isVideo) return;
+ this.$refs.previewVideo.open(item.url);
+ },
+ onClickPreview(item, index) {
+ this.$emit(
+ 'clickPreview',
+ Object.assign(Object.assign({}, item), this.getDetail(index))
+ );
+ }
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ $uv-upload-preview-border-radius: 2px !default;
+ $uv-upload-preview-margin: 0 8px 8px 0 !default;
+ $uv-upload-image-width: 80px !default;
+ $uv-upload-image-height: $uv-upload-image-width;
+ $uv-upload-other-bgColor: rgb(242, 242, 242) !default;
+ $uv-upload-other-flex: 1 !default;
+ $uv-upload-text-font-size: 11px !default;
+ $uv-upload-text-color: $uv-tips-color !default;
+ $uv-upload-text-margin-top: 2px !default;
+ $uv-upload-deletable-right: 0 !default;
+ $uv-upload-deletable-top: 0 !default;
+ $uv-upload-deletable-bgColor: rgb(55, 55, 55) !default;
+ $uv-upload-deletable-height: 14px !default;
+ $uv-upload-deletable-width: $uv-upload-deletable-height;
+ $uv-upload-deletable-boder-bottom-left-radius: 100px !default;
+ $uv-upload-deletable-zIndex: 3 !default;
+ $uv-upload-success-bottom: 0 !default;
+ $uv-upload-success-right: 0 !default;
+ $uv-upload-success-border-style: solid !default;
+ $uv-upload-success-border-top-color: transparent !default;
+ $uv-upload-success-border-left-color: transparent !default;
+ $uv-upload-success-border-bottom-color: $uv-success !default;
+ $uv-upload-success-border-right-color: $uv-upload-success-border-bottom-color;
+ $uv-upload-success-border-width: 9px !default;
+ $uv-upload-icon-top: 0px !default;
+ $uv-upload-icon-right: 0px !default;
+ $uv-upload-icon-h5-top: 1px !default;
+ $uv-upload-icon-h5-right: 0 !default;
+ $uv-upload-icon-width: 16px !default;
+ $uv-upload-icon-height: $uv-upload-icon-width;
+ $uv-upload-success-icon-bottom: -10px !default;
+ $uv-upload-success-icon-right: -10px !default;
+ $uv-upload-status-right: 0 !default;
+ $uv-upload-status-left: 0 !default;
+ $uv-upload-status-bottom: 0 !default;
+ $uv-upload-status-top: 0 !default;
+ $uv-upload-status-bgColor: rgba(0, 0, 0, 0.5) !default;
+ $uv-upload-status-icon-Zindex: 1 !default;
+ $uv-upload-message-font-size: 12px !default;
+ $uv-upload-message-color: #FFFFFF !default;
+ $uv-upload-message-margin-top: 5px !default;
+ $uv-upload-button-width: 80px !default;
+ $uv-upload-button-height: $uv-upload-button-width;
+ $uv-upload-button-bgColor: rgb(244, 245, 247) !default;
+ $uv-upload-button-border-radius: 2px !default;
+ $uv-upload-botton-margin: 0 8px 8px 0 !default;
+ $uv-upload-text-font-size: 11px !default;
+ $uv-upload-text-color: $uv-tips-color !default;
+ $uv-upload-text-margin-top: 2px !default;
+ $uv-upload-hover-bgColor: rgb(230, 231, 233) !default;
+ $uv-upload-disabled-opacity: .5 !default;
+ .uv-upload {
+ @include flex(column);
+ flex: 1;
+ &__wrap {
+ @include flex;
+ flex-wrap: wrap;
+ flex: 1;
+ &__preview {
+ border-radius: $uv-upload-preview-border-radius;
+ margin: $uv-upload-preview-margin;
+ position: relative;
+ overflow: hidden;
+ @include flex;
+ &__image {
+ width: $uv-upload-image-width;
+ height: $uv-upload-image-height;
+ }
+ &__other {
+ width: $uv-upload-image-width;
+ height: $uv-upload-image-height;
+ background-color: $uv-upload-other-bgColor;
+ flex: $uv-upload-other-flex;
+ @include flex(column);
+ justify-content: center;
+ align-items: center;
+ &__text {
+ font-size: $uv-upload-text-font-size;
+ color: $uv-upload-text-color;
+ margin-top: $uv-upload-text-margin-top;
+ }
+ }
+ }
+ }
+ &__deletable {
+ position: absolute;
+ top: $uv-upload-deletable-top;
+ right: $uv-upload-deletable-right;
+ background-color: $uv-upload-deletable-bgColor;
+ height: $uv-upload-deletable-height;
+ width: $uv-upload-deletable-width;
+ @include flex;
+ border-bottom-left-radius: $uv-upload-deletable-boder-bottom-left-radius;
+ align-items: center;
+ justify-content: center;
+ z-index: $uv-upload-deletable-zIndex;
+ &__icon {
+ position: absolute;
+ transform: scale(0.7);
+ top: $uv-upload-icon-top;
+ right: $uv-upload-icon-right;
+ /* #ifdef H5 */
+ top: $uv-upload-icon-h5-top;
+ right: $uv-upload-icon-h5-right;
+ /* #endif */
+ }
+ }
+ &__success {
+ position: absolute;
+ bottom: $uv-upload-success-bottom;
+ right: $uv-upload-success-right;
+ @include flex;
+ // 由于weex(nvue)为阿里巴巴的KPI(部门业绩考核)的laji产物,不支持css绘制三角形
+ // 所以在nvue下使用图片,非nvue下使用css实现
+ /* #ifndef APP-NVUE */
+ border-style: $uv-upload-success-border-style;
+ border-top-color: $uv-upload-success-border-top-color;
+ border-left-color: $uv-upload-success-border-left-color;
+ border-bottom-color: $uv-upload-success-border-bottom-color;
+ border-right-color: $uv-upload-success-border-right-color;
+ border-width: $uv-upload-success-border-width;
+ align-items: center;
+ justify-content: center;
+ /* #endif */
+ &__icon {
+ /* #ifndef APP-NVUE */
+ position: absolute;
+ transform: scale(0.7);
+ bottom: $uv-upload-success-icon-bottom;
+ right: $uv-upload-success-icon-right;
+ /* #endif */
+ /* #ifdef APP-NVUE */
+ width: $uv-upload-icon-width;
+ height: $uv-upload-icon-height;
+ /* #endif */
+ }
+ }
+ &__status {
+ position: absolute;
+ top: $uv-upload-status-top;
+ bottom: $uv-upload-status-bottom;
+ left: $uv-upload-status-left;
+ right: $uv-upload-status-right;
+ background-color: $uv-upload-status-bgColor;
+ @include flex(column);
+ align-items: center;
+ justify-content: center;
+ &__icon {
+ position: relative;
+ z-index: $uv-upload-status-icon-Zindex;
+ }
+ &__message {
+ font-size: $uv-upload-message-font-size;
+ color: $uv-upload-message-color;
+ margin-top: $uv-upload-message-margin-top;
+ }
+ }
+ &__button {
+ @include flex(column);
+ align-items: center;
+ justify-content: center;
+ width: $uv-upload-button-width;
+ height: $uv-upload-button-height;
+ background-color: $uv-upload-button-bgColor;
+ border-radius: $uv-upload-button-border-radius;
+ margin: $uv-upload-botton-margin;
+ /* #ifndef APP-NVUE */
+ box-sizing: border-box;
+ /* #endif */
+ &__text {
+ font-size: $uv-upload-text-font-size;
+ color: $uv-upload-text-color;
+ margin-top: $uv-upload-text-margin-top;
+ }
+ &--hover {
+ background-color: $uv-upload-hover-bgColor;
+ }
+ &--disabled {
+ opacity: $uv-upload-disabled-opacity;
+ }
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-upload/package.json b/uni_modules/uv-upload/package.json
new file mode 100644
index 0000000..b29dbb0
--- /dev/null
+++ b/uni_modules/uv-upload/package.json
@@ -0,0 +1,90 @@
+{
+ "id": "uv-upload",
+ "displayName": "uv-upload 上传 全面兼容小程序、nvue、vue2、vue3等多端",
+ "version": "1.0.6",
+ "description": "该组件用于上传图片等文件场景。",
+ "keywords": [
+ "uv-upload",
+ "uvui",
+ "uv-ui",
+ "upload",
+ "上传"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-icon",
+ "uv-loading-icon",
+ "uv-popup"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-upload/readme.md b/uni_modules/uv-upload/readme.md
new file mode 100644
index 0000000..de4aedf
--- /dev/null
+++ b/uni_modules/uv-upload/readme.md
@@ -0,0 +1,11 @@
+## Upload 上传
+
+> **组件名:uv-upload**
+
+该组件用于上传图片等文件场景。
+
+### <a href="https://www.uvui.cn/components/upload.html" target="_blank">查看文档</a>
+
+### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+#### 如使用过程中有任何问题,或者您对uv-ui有一些好的建议,欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/uni_modules/uv-vtabs/changelog.md b/uni_modules/uv-vtabs/changelog.md
new file mode 100644
index 0000000..4d73568
--- /dev/null
+++ b/uni_modules/uv-vtabs/changelog.md
@@ -0,0 +1,20 @@
+## 1.0.8(2023-12-20)
+1. 优化
+## 1.0.7(2023-12-20)
+1. 优化
+## 1.0.6(2023-12-12)
+1. 修复设置current,快速点击菜单,导致死循环的BUG
+## 1.0.5(2023-06-27)
+修复:非联动,内容过多的情况,滚动一段距离,再切换未滚动到顶部的BUG
+## 1.0.4(2023-06-13)
+1. 增加scrolltolower回调函数
+2. 优化
+## 1.0.3(2023-06-13)
+1. 优化
+## 1.0.2(2023-06-13)
+1. 增加hdHeight参数,避免顶部有内容计算联动不准确的BUG
+2. 优化滑动触发频率,避免跳动
+## 1.0.1(2023-06-04)
+1. 文档说明
+## 1.0.0(2023-06-04)
+1. 新增垂直选项卡组件
diff --git a/uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue b/uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue
new file mode 100644
index 0000000..286dc8c
--- /dev/null
+++ b/uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue
@@ -0,0 +1,68 @@
+<template>
+ <view
+ class="uv-vtabs-item"
+ :id="`content_${index}`"
+ ref="uv-vtabs-item"
+ >
+ <slot />
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom');
+ // #endif
+ export default {
+ name: 'uv-vtabs-item',
+ mixins: [mpMixin, mixin],
+ props: {
+ index: {
+ type: [Number,String],
+ default: 0
+ }
+ },
+ data(){
+ return {
+ // 记录item的离顶部的距离
+ top: 0,
+ // 记录item的高度
+ height: 0
+ // 是否为联动
+ }
+ },
+ mounted() {
+ this.init();
+ },
+ methods: {
+ async init(){
+ this.getParentData('uv-vtabs');
+ if (!this.parent) {
+ return this.$uv.error('uv-vtabs必须要搭配uv-vtabs-item组件使用')
+ }
+ if(!this.parent.chain) return;
+ await this.$uv.sleep();
+ this.getItemRect().then(size=>{
+ // 由于对象的引用特性,此处会同时生效到父组件的children数组的本实例的top属性中,供父组件判断读取
+ this.top = size.top;
+ this.height = size.height;
+ });
+ },
+ getItemRect(){
+ return new Promise(resolve => {
+ // #ifndef APP-NVUE
+ this.$uvGetRect('.uv-vtabs-item').then(size => {
+ resolve(size)
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ const ref = this.$refs['uv-vtabs-item']
+ dom.getComponentRect(ref, res => {
+ resolve(res.size)
+ })
+ // #endif
+ })
+ }
+ }
+ }
+</script>
diff --git a/uni_modules/uv-vtabs/components/uv-vtabs/props.js b/uni_modules/uv-vtabs/components/uv-vtabs/props.js
new file mode 100644
index 0000000..a64dce8
--- /dev/null
+++ b/uni_modules/uv-vtabs/components/uv-vtabs/props.js
@@ -0,0 +1,79 @@
+export default {
+ props: {
+ // 列表数据
+ list: {
+ type: Array,
+ default: ()=>[]
+ },
+ // 从list元素对象中读取的键名,默认name
+ keyName: {
+ type: String,
+ default: 'name'
+ },
+ // 当前选中项
+ current: {
+ type: [Number, String],
+ default: 0
+ },
+ // 头部内容的高度
+ hdHeight: {
+ type: [Number, String],
+ default: 0
+ },
+ // 是否联动,默认开启联动
+ chain: {
+ type: Boolean,
+ default: true
+ },
+ // 整个列表的高度,默认auto屏幕高度
+ height: {
+ type: [Number, String],
+ default: 'auto'
+ },
+ // 左边列表的宽度,默认200rpx
+ barWidth: {
+ type: [Number, String],
+ default: '180rpx'
+ },
+ // 左边列表是否允许滚动
+ barScrollable: {
+ type: Boolean,
+ default: true
+ },
+ // 背景颜色 默认主题颜色 $bg-color
+ barBgColor: {
+ type: String,
+ default: ''
+ },
+ // 左边列表的自定义样式
+ barStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ // 左边列表项的自定义样式
+ barItemStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ // 左边选择项激活时的自定义样式
+ barItemActiveStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ // 左边选择项激活时的左边线条自定义样式
+ barItemActiveLineStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ // 菜单项中的徽标自定义样式,比如定位位置
+ barItemBadgeStyle: {
+ type: [Object, String],
+ default: ''
+ },
+ // 右边区域自定义样式
+ contentStyle: {
+ type: [Object, String],
+ default: ''
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue b/uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue
new file mode 100644
index 0000000..cd23320
--- /dev/null
+++ b/uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue
@@ -0,0 +1,438 @@
+<template>
+ <view
+ class="uv-vtabs"
+ :style="[vtabsStyle]"
+ >
+ <scroll-view
+ class="uv-vtabs__bar"
+ ref="uv-vtabs__bar"
+ :style="[getBarStyle]"
+ :scroll-y="barScrollable"
+ :scroll-x="scrollX"
+ :show-scrollbar="false"
+ :scroll-with-animation="true"
+ :scroll-top="barScrollTop"
+ :scroll-into-view="barScrollToView"
+ >
+ <view
+ :class="[
+ 'uv-vtabs__bar-item',
+ `uv-vtabs__bar-item--${index}`,
+ index == activeIndex && 'uv-vtabs__bar-item-active'
+ ]"
+ :ref="`uv-vtabs__bar-item--${index}`"
+ v-for="(item,index) in list"
+ :key="index"
+ :id="`bar_${index}`"
+ :style="[itemStyle(index)]"
+ @tap.stop="clickHandler(index)"
+ >
+ <view
+ class="uv-vtabs__bar-item--line"
+ v-if="index == activeIndex"
+ :style="[$uv.addStyle(barItemActiveLineStyle)]"
+ ></view>
+ <text
+ :class="[
+ 'uv-vtabs__bar-item--value',
+ index == activeIndex && 'uv-vtabs__bar-item-active--value'
+ ]"
+ :style="[itemStyle(index),textStyle(index)]"
+ >{{item[keyName]}}</text>
+ <view
+ class="uv-vtabs__bar-item--badge"
+ :style="[$uv.addStyle(barItemBadgeStyle)]"
+ v-if="!!(item.badge && (item.badge.show || item.badge.isDot || item.badge.value))"
+ >
+ <uv-badge
+ :show="!!(item.badge && (item.badge.show || item.badge.isDot || item.badge.value))"
+ :isDot="item.badge && item.badge.isDot || propsBadge.isDot"
+ :value="item.badge && item.badge.value || propsBadge.value"
+ :max="item.badge && item.badge.max || propsBadge.max"
+ :type="item.badge && item.badge.type || propsBadge.type"
+ :showZero="item.badge && item.badge.showZero || propsBadge.showZero"
+ :bgColor="item.badge && item.badge.bgColor || propsBadge.bgColor"
+ :color="item.badge && item.badge.color || propsBadge.color"
+ :shape="item.badge && item.badge.shape || propsBadge.shape"
+ :numberType="item.badge && item.badge.numberType || propsBadge.numberType"
+ :inverted="item.badge && item.badge.inverted || propsBadge.inverted"
+ ></uv-badge>
+ </view>
+ </view>
+ </scroll-view>
+ <scroll-view
+ class="uv-vtabs__content"
+ :style="[getContentStyle,$uv.addStyle(contentStyle)]"
+ :scroll-y="true"
+ :scroll-x="scrollX"
+ :show-scrollbar="false"
+ :scroll-top="contentScrollTop"
+ :scroll-into-view="contentScrollTo"
+ :scroll-with-animation="true"
+ @scroll="scrollHandler"
+ @scrolltolower="scrolltolower"
+ v-if="chain"
+ >
+ <slot />
+ </scroll-view>
+ <scroll-view
+ v-else
+ class="uv-vtabs__content"
+ :style="[getContentStyle,$uv.addStyle(contentStyle)]"
+ :scroll-y="true"
+ :scroll-x="scrollX"
+ :show-scrollbar="false"
+ :scroll-top="contentScrollTop2"
+ @scrolltolower="scrolltolower"
+ >
+ <slot />
+ </scroll-view>
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import debounce from '@/uni_modules/uv-ui-tools/libs/function/debounce.js'
+ import throttle from '@/uni_modules/uv-ui-tools/libs/function/throttle.js'
+ import uvBadgeProps from '@/uni_modules/uv-badge/components/uv-badge/props.js'
+ import props from './props.js';
+ // #ifdef APP-NVUE
+ const dom = uni.requireNativePlugin('dom')
+ // #endif
+ /**
+ * 垂直选项卡
+ * @description 该组件兼容所有端,提供了分类展示和联动等功能
+ * @tutorial https://www.uvui.cn/components/vtabs.html
+ * @property {Array} list 选项数组,元素为对象,如[{name:'uv-ui'}](默认 [] )
+ * @property {String} keyName 从list元素对象中读取的键名(默认 name )
+ * @property {Number} current 当前选中项,从0开始(默认 0 )
+ * @property {Number | String} hdHeight 头部内容的高度,头部有内容必传,否则会有联动误差(默认 0 )
+ * @property {Boolean} chain 是否开启联动,开启后右边区域可以滑动查看内容(默认 true )
+ * @property {Number|String} height 整个列表的高度,默认auto或空则为屏幕高度(默认 auto屏幕高度 )
+ * @property {Number|String} barWidth 左边选项区域的宽度(默认 180rpx )
+ * @property {Boolean} barScrollable 左边选项区域是否允许滚动 (默认 true )
+ * @property {String} barBgColor 左边选项区域的背景颜色(默认$uv-bg-color)
+ * @property {Object} barStyle 左边选项区域的自定义样式 (默认{})
+ * @property {Object} barItemStyle 左边选项区域每个选项的自定义样式 (默认{})
+ * @property {Object} barItemActiveStyle 左边选项区域选中选项的自定义样式 (默认{})
+ * @property {Object} barItemActiveLineStyle 左边选项区域选中选项竖线条的自定义样式 (默认{})
+ * @property {Object} barItemBadgeStyle 左边选项区域选中选项徽标的自定义样式,主要用于设置位置 (默认{})
+ * @property {Object} contentStyle 右边区域自定义样式 (默认{})
+ * @example <uv-vtabs :list="list"><uv-vtabs-item>...</uv-vtabs-item></uv-vtabs>
+ */
+ export default {
+ name: 'uv-vtabs',
+ mixins: [mpMixin, mixin, props],
+ created() {
+ this.children = []
+ },
+ mounted() {
+ this.$nextTick(()=>{
+ this.init(this.current);
+ })
+ },
+ data() {
+ return {
+ activeIndex: 0,
+ // 微信小程序下,scroll-view的scroll-into-view属性无法对slot中的内容的id生效,只能通过设置scrollTop的形式去移动滚动条
+ contentScrollTop: 0,
+ contentScrollTop2: 0,//针对非联动
+ contentScrollTo: '',
+ scrolling: false,
+ barScrolling: false,
+ touching: false,
+ hasHeight: 0,
+ scrollViewHeight: 0,
+ barScrollTop: 0,
+ barScrollToView: '',
+ timer2: 0
+ }
+ },
+ computed: {
+ scrollX(){
+ // #ifdef APP-NVUE
+ return true;
+ // #endif
+ return false;
+ },
+ vtabsStyle() {
+ const style = {};
+ style.height = this.getHeight();
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle));
+ },
+ getBarStyle() {
+ const style = {};
+ style.width = this.$uv.getPx(this.barWidth, true);
+ style.background = this.barBgColor;
+ style.height = this.getHeight();
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.barStyle));
+ },
+ itemStyle(){
+ return index =>{
+ const style = {};
+ let barItemInitStyle = this.barItemStyle;
+ // 避免在nvue模式下,切换时候上一个选中颜色不变
+ if(this.barItemStyle && !this.barItemStyle?.background) {
+ barItemInitStyle.background = 'transparent';
+ }
+ // 是否激活的样式
+ const customeStyle = index === this.activeIndex ? this.$uv.addStyle(this.barItemActiveStyle) : this.$uv.addStyle(barItemInitStyle);
+ if (this.list[index].disabled) {
+ style.color = '#c8c9cc'
+ }
+ return this.$uv.deepMerge(style, customeStyle);
+ }
+ },
+ // nvue设置字体样式必须要text标签上进行
+ textStyle(){
+ return index=>{
+ const style = {};
+ style.width = this.$uv.getPx(this.barWidth, true);
+ return style;
+ }
+ },
+ getContentStyle() {
+ const style = {};
+ style.height = this.getHeight();
+ return style;
+ },
+ propsBadge() {
+ return uvBadgeProps
+ }
+ },
+ watch: {
+ current(newVal){
+ if(!this.touching)
+ this.$nextTick(()=>{
+ this.init(newVal?newVal:0);
+ })
+ },
+ list(newVal) {
+ if (newVal.length) {
+ this.$uv.sleep(30).then(res => {
+ this.resize();
+ })
+ }
+ },
+ activeIndex(newVal){
+ if(!this.chain) {// 解决:非联动,内容过多的情况,滚动一段距离,再切换未滚动到顶部的BUG
+ this.contentScrollTop2 = 0 - Math.random() * 4 - 4;
+ }
+ this.$emit('change',newVal);
+ }
+ },
+ methods: {
+ init(index){
+ let num = 0;
+ clearInterval(this.timer2);
+ this.timer2 = setInterval(async ()=>{
+ num++;
+ if(num>50) clearInterval(this.timer2);
+ if(this.children.length) {
+ clearInterval(this.timer2);
+ await this.$uv.sleep(300);
+ this.clickHandler(index);
+ }
+ },100)
+ },
+ // 内容滚动到底部触发
+ scrolltolower(){
+ this.$emit('scrolltolower',this.activeIndex);
+ },
+ async resize() {
+ // 如果list数组长度为0就不处理 || 选中目标未变则不处理
+ if (this.list.length == 0 || !this.barScrollable) return;
+ // 避免滑太快,修复位置
+ Promise.all([this.getTabsRect(), this.getAllItemRect()]).then(([tabsRect, itemRect = []]) => {
+ this.tabsRect = tabsRect;
+ this.scrollViewHeight = 0
+ itemRect.map((item, index) => {
+ this.scrollViewHeight += item.height;
+ this.list[index].rect = item;
+ })
+ this.setBarScrollTop();
+ })
+ },
+ // 设置左边菜单滚动条的位置,目标:将当前的选项移到中间位置
+ setBarScrollTop() {
+ const tabRect = this.list[this.activeIndex];
+ const offsetTop = this.list
+ .slice(0, this.activeIndex)
+ .reduce((total, item) => {
+ return total + item.rect.height;
+ }, 0);
+ const scrollViewHeight = this.$uv.getPx(this.getHeight());
+ let barScrollTop = tabRect.rect.height / 2 + offsetTop - scrollViewHeight / 2;
+ // 先给一点随机值,避免出现不能滚动的BUG
+ barScrollTop = Math.min(barScrollTop, this.scrollViewHeight - this.tabsRect.height);
+ this.barScrollTop = Math.max(0, barScrollTop);
+ // 已经不能滚动的时候,就使用scroll-into-view的方式进行定位,避免失效
+ if(barScrollTop>=(this.scrollViewHeight - this.tabsRect.height)) {
+ this.timer && clearTimeout(this.timer);
+ this.timer = setTimeout(()=>{
+ this.barScrollToView = `bar_${this.activeIndex}`;
+ },400)
+ }
+ },
+ // 左边菜单点击
+ async clickHandler(currentIndex) {
+ if (currentIndex == this.activeIndex) return;
+ this.touching = true;
+ this.activeIndex = currentIndex;
+ if(this.chain) {
+ // 给一点随机值,避免出现不能滚动的BUG。微信端必须用此方法
+ this.contentScrollTop = this.children[currentIndex].top - this.$uv.getPx(this.hdHeight) - Math.random() * 4 - 4;
+ // #ifndef MP-WEIXIN
+ this.contentScrollTo = `content_${currentIndex}`;
+ // #endif
+ }
+ this.timer && clearTimeout(this.timer);
+ throttle(()=>{
+ this.resize();
+ },300,false)
+ debounce(() => {
+ this.touching = false;
+ }, 900)
+ },
+ // 内容滚动
+ scrollHandler(e) {
+ if (this.touching || this.scrolling) return;
+ // 每过一定时间取样一次,减少资源损耗以及可能带来的卡顿
+ this.scrolling = true;
+ this.$uv.sleep(80).then(() => {
+ this.scrolling = false;
+ })
+ const scrollTop = e.detail.scrollTop;
+ let children = this.children;
+ const len = children.length;
+ let top = 0;
+ let activeIndex = 0;
+ children = this.children.map((item, index) => {
+ if (item.height > 0) this.hasHeight = item.height;
+ item.height = item.height > 0 ? item.height : this.hasHeight;
+ const child = {
+ height: item.height,
+ top
+ }
+ // 进行累加,给下一个item提供计算依据
+ top += item.height;
+ return child;
+ })
+ for (let i = 0; i < len; i++) {
+ const item = children[i];
+ const nextItem = children[i + 1];
+ // 如果滚动条高度小于第一个item的top值,此时无需设置任意字母为高亮
+ if (scrollTop <= children[0].top) {
+ activeIndex = 0;
+ break
+ } else if (!nextItem) {
+ // 当不存在下一个item时,意味着历遍到了最后一个
+ activeIndex = len - 1;
+ break
+ } else if (scrollTop > item.top && scrollTop < nextItem.top) {
+ activeIndex = i;
+ break
+ }
+ }
+ this.activeIndex = activeIndex;
+ // 当前选中项索引必然来源于前后两个索引,满足才执行,避免闪烁的bug
+ this.timer4 && clearTimeout(this.timer4);
+ this.timer4 = setTimeout(()=>{
+ this.resize();
+ },100)
+ },
+ // 设置高度
+ getHeight() {
+ let height = 0;
+ const isEmpty = this.$uv.test.empty(this.height);
+ if (isEmpty || this.height=='auto') height = this.$uv.addUnit(this.$uv.sys().windowHeight);
+ else height = this.$uv.getPx(this.height, true);
+ return height;
+ },
+ // 获取导航菜单的尺寸
+ getTabsRect() {
+ return new Promise(resolve => {
+ this.queryRect('uv-vtabs__bar').then(size => resolve(size))
+ })
+ },
+ // 获取所有标签的尺寸
+ getAllItemRect() {
+ return new Promise(resolve => {
+ const promiseAllArr = this.list.map((item, index) => this.queryRect(
+ `uv-vtabs__bar-item--${index}`, true))
+ Promise.all(promiseAllArr).then(sizes => resolve(sizes))
+ })
+ },
+ // 获取各个标签的尺寸
+ queryRect(el, item) {
+ // #ifndef APP-NVUE
+ // $uvGetRect为uv-ui自带的节点查询简化方法,详见文档介绍:https://www.uvui.cn/js/getRect.html
+ // 组件内部一般用this.$uvGetRect,对外的为getRect,二者功能一致,名称不同
+ return new Promise(resolve => {
+ this.$uvGetRect(`.${el}`).then(size => {
+ resolve(size)
+ })
+ })
+ // #endif
+ // #ifdef APP-NVUE
+ // nvue下,使用dom模块查询元素高度
+ // 返回一个promise,让调用此方法的主体能使用then回调
+ return new Promise(resolve => {
+ dom.getComponentRect(item ? this.$refs[el][0] : this.$refs[el], res => {
+ resolve(res.size)
+ })
+ })
+ // #endif
+ }
+ }
+ }
+</script>
+<style scoped lang="scss">
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ @import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
+ .uv-vtabs {
+ @include flex;
+ &__bar {
+ background: $uv-bg-color;
+ &-item {
+ position: relative;
+ @include flex;
+ align-items: center;
+ justify-content: center;
+ padding: 35rpx 12rpx 35rpx 20rpx;
+ &--value {
+ /* #ifdef APP-NVUE */
+ padding: 0 12rpx;
+ /* #endif */
+ font-size: 14px;
+ color: $uv-content-color;
+ }
+ &-active {
+ background: #fff;
+ &--value {
+ color: $uv-primary;
+ }
+ }
+ &--line {
+ position: absolute;
+ width: 2px;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ z-index: 1;
+ background-color: $uv-primary;
+ }
+ &--badge {
+ position: absolute;
+ top: 4px;
+ right: 10px;
+ z-index: 1;
+ }
+ }
+ }
+ &__content {
+ flex: 1;
+ background: #fff;
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-vtabs/package.json b/uni_modules/uv-vtabs/package.json
new file mode 100644
index 0000000..9848beb
--- /dev/null
+++ b/uni_modules/uv-vtabs/package.json
@@ -0,0 +1,87 @@
+{
+ "id": "uv-vtabs",
+ "displayName": "uv-vtabs 垂直选项卡 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.8",
+ "description": "uv-vtabs 垂直分类组件主要用于分类选择,简单配置即可使用,左右自动进行联动,不用自己再去做复杂的计算,组件内部已经完成相关计算。支持联动和不联动,商品分类,灵活配置,多端兼容开箱即用。",
+ "keywords": [
+ "uv-vtabs",
+ "uvui",
+ "uv-ui",
+ "垂直分类",
+ "垂直选项卡"
+],
+ "repository": "",
+"engines": {
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-badge"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-vtabs/readme.md b/uni_modules/uv-vtabs/readme.md
new file mode 100644
index 0000000..812b10c
--- /dev/null
+++ b/uni_modules/uv-vtabs/readme.md
@@ -0,0 +1,19 @@
+## Vtabs 垂直选项卡
+
+> **组件名:uv-vtabs**
+
+该组件主要用于分类选择,开箱即用,简单配置参数即可使用,左右自动进行联动,不用自己再去做复杂的计算,组件内部已经完成相关计算。联动和不联动两种可选方式,联动-左右均可滚动,不联动-右边区域只会在选中时显示。
+
+# <a href="https://www.uvui.cn/components/vtabs.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP)</small>
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
\ No newline at end of file
diff --git a/uni_modules/uv-waterfall/changelog.md b/uni_modules/uv-waterfall/changelog.md
new file mode 100644
index 0000000..18f9162
--- /dev/null
+++ b/uni_modules/uv-waterfall/changelog.md
@@ -0,0 +1,24 @@
+## 1.0.8(2023-08-17)
+1. 修复只有一条数据切换时可能存在位置错误的BUG
+## 1.0.7(2023-07-22)
+1. 避免快速切换报错的BUG
+## 1.0.6(2023-07-17)
+1. 优化文档
+2. 优化其他
+## 1.0.5(2023-07-14)
+1. 优化changeList未处理数据时,正确返回对应列的数据,避免误导
+## 1.0.4(2023-05-27)
+1. 修复在百度小程序中可能存在的BUG
+2. 去掉原有的slot方式
+## 1.0.3(2023-05-23)
+1. 修复在百度/头条小程序显示异常等BUG
+2. 增加changeList回调函数处理数据
+3. 更新示例
+## 1.0.2(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.1(2023-05-12)
+1. 增加clear回调函数
+2. 增加remove回调函数
+## 1.0.0(2023-05-10)
+uv-waterfall 瀑布流
diff --git a/uni_modules/uv-waterfall/components/uv-waterfall/props.js b/uni_modules/uv-waterfall/components/uv-waterfall/props.js
new file mode 100644
index 0000000..de8cb8f
--- /dev/null
+++ b/uni_modules/uv-waterfall/components/uv-waterfall/props.js
@@ -0,0 +1,69 @@
+export default {
+ props: {
+ // 瀑布流数据
+ // #ifdef VUE2
+ value: {
+ type: Array,
+ default: () => []
+ },
+ // #endif
+ // #ifdef VUE3
+ modelValue: {
+ type: Array,
+ default: () => []
+ },
+ // #endif
+ // 数据的id值,根据id值对数据执行删除操作
+ // 如数据为:{id: 1, name: 'uv-ui'},那么该值设置为id
+ idKey: {
+ type: String,
+ default: 'id'
+ },
+ // 每次插入数据的事件间隔,间隔越长能保证两列高度相近,但是用户体验不好,单位ms
+ addTime: {
+ type: Number,
+ default: 200
+ },
+ // 瀑布流的列数,默认2,最高为5
+ columnCount: {
+ type: [Number, String],
+ default: 2
+ },
+ // 列与列的间隙,默认20
+ columnGap: {
+ type: [Number, String],
+ default: 20
+ },
+ // 左边和列表的间隙
+ leftGap: {
+ type: [Number, String],
+ default: 0
+ },
+ // 右边和列表的间隙
+ rightGap: {
+ type: [Number, String],
+ default: 0
+ },
+ // 是否显示滚动条,仅nvue生效
+ showScrollbar: {
+ type: [Boolean],
+ default: false
+ },
+ // 列宽,nvue生效
+ columnWidth: {
+ type: [Number, String],
+ default: 'auto'
+ },
+ // 瀑布流的宽度,nvue生效
+ width: {
+ type: [Number, String],
+ default: ''
+ },
+ // 瀑布流的高度,nvue生效
+ height: {
+ type: [Number, String],
+ default: ''
+ },
+ ...uni.$uv?.props?.waterfall
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue b/uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue
new file mode 100644
index 0000000..cfb3ebc
--- /dev/null
+++ b/uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue
@@ -0,0 +1,265 @@
+<template>
+ <view class="uv-waterfall">
+ <!-- #ifndef APP-NVUE -->
+ <view class="uv-waterfall__gap_left" :style="[gapLeftStyle]"></view>
+ <template v-if="columnNum>=1">
+ <view id="uv-waterfall-1" class="uv-waterfall__column">
+ <slot name="list1"></slot>
+ </view>
+ </template>
+ <template v-if="columnNum>=2">
+ <view class="uv-waterfall__gap_center" :style="[gapCenterStyle]"></view>
+ <view id="uv-waterfall-2" class="uv-waterfall__column">
+ <slot name="list2"></slot>
+ </view>
+ </template>
+ <template v-if="columnNum>=3">
+ <view class="uv-waterfall__gap_center" :style="[gapCenterStyle]"></view>
+ <view id="uv-waterfall-3" class="uv-waterfall__column">
+ <slot name="list3"></slot>
+ </view>
+ </template>
+ <template v-if="columnNum>=4">
+ <view class="uv-waterfall__gap_center" :style="[gapCenterStyle]">
+ </view>
+ <view id="uv-waterfall-4" class="uv-waterfall__column">
+ <slot name="list4"></slot>
+ </view>
+ </template>
+ <template v-if="columnNum>=5">
+ <view class="uv-waterfall__gap_center" :style="[gapCenterStyle]">
+ </view>
+ <view id="uv-waterfall-5" class="uv-waterfall__column">
+ <slot name="list5"></slot>
+ </view>
+ </template>
+ <view class="uv-waterfall__gap_right" :style="[gapRightStyle]">
+ </view>
+ <!-- #endif -->
+ <!-- #ifdef APP-NVUE -->
+ <view class="waterfall-warapper">
+ <waterfall :column-count="columnNum" :show-scrollbar="false" column-width="auto" :column-gap="columnGap" :left-gap="leftGap" :right-gap="rightGap" :always-scrollable-vertical="true" :style="[nvueWaterfallStyle]"
+ @loadmore="scrolltolower">
+ <slot></slot>
+ </waterfall>
+ </view>
+ <!-- #endif -->
+ </view>
+</template>
+<script>
+ import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
+ import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
+ import props from './props.js';
+ /**
+ * 瀑布流
+ * @description 该组件兼容所有端,nvue参考https://uniapp.dcloud.net.cn/component/waterfall.html
+ * @tutorial https://www.uvui.cn/components/list.html
+ * @property {Array} value/modelValue 瀑布流数组数据,非nvue生效 (默认 [] )
+ * @property {String} idKey 数据的id值,根据id值对数据执行删除操作,如数据为:{id: 1, name: 'uv-ui'},那么该值设置为id,非nvue有效 (默认 '' )
+ * @property {String | Number} addTime 每次插入数据的事件间隔,间隔越长能保证两列高度相近,但是用户体验不好,单位ms,非nvue生效(默认 200 )
+ * @property {String | Number} columnCount 瀑布流的列数(默认 2 )
+ * @property {String | Number} columnGap 列与列的间隙(默认 0 )
+ * @property {String | Number} leftGap 左边和列表的间隙(默认 0 )
+ * @property {String | Number} rightGap 右边和列表的间隙(默认 0 )
+ * @property {Boolean} showScrollbar 控制是否出现滚动条,仅nvue有效 (默认 false )
+ * @property {String | Number} columnWidth 描述瀑布流每一列的列宽,nvue生效 (默认 auto)
+ * @property {String | Number} width 瀑布流的宽度,nvue生效 (默认 屏幕宽 )
+ * @property {String | Number} height 瀑布流的高度,nvue生效 (默认 屏幕高 )
+ * @property {Object} customStyle 定义需要用到的外部样式
+ *
+ * @example <uv-waterfall v-model="list"></uv-waterfall>
+ */
+ export default {
+ name: 'uv-waterfall',
+ mixins: [mpMixin, mixin, props],
+ data() {
+ return {
+ list1: [],
+ list2: [],
+ list3: [],
+ list4: [],
+ list5: [],
+ // 临时列表
+ tempList: []
+ }
+ },
+ computed: {
+ // 破坏value变量引用,否则数据会保持不变
+ copyValue() {
+ // #ifdef VUE2
+ return this.$uv.deepClone(this.value)
+ // #endif
+ // #ifdef VUE3
+ return this.$uv.deepClone(this.modelValue)
+ // #endif
+ },
+ columnNum() {
+ return this.columnCount <= 0 ? 0 : this.columnCount >= 5 ? 5 : this.columnCount;
+ },
+ gapLeftStyle() {
+ const style = {}
+ style.width = this.$uv.addUnit(this.leftGap)
+ return style;
+ },
+ gapRightStyle() {
+ const style = {}
+ style.width = this.$uv.addUnit(this.rightGap)
+ return style;
+ },
+ gapCenterStyle() {
+ const style = {}
+ style.width = this.$uv.addUnit(this.columnGap)
+ return style;
+ },
+ nvueWaterfallStyle() {
+ const style = {};
+ if (this.width != 0) style.width = this.$uv.addUnit(this.width)
+ if (this.height != 0) style.height = this.$uv.addUnit(this.height)
+ // 如果没有定义列表高度,则默认使用屏幕高度
+ if (!style.width) style.width = this.$uv.addUnit(this.$uv.sys().windowWidth, 'px')
+ if (!style.height) style.height = this.$uv.addUnit(this.$uv.sys().windowHeight, 'px')
+ return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
+ }
+ },
+ watch: {
+ copyValue(nVal, oVal) {
+ // #ifndef APP-NVUE
+ if (nVal.length != 0) {
+ // 取出数组发生变化的部分
+ let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0
+ // 拼接原有数据
+ this.tempList = this.tempList.concat(this.$uv.deepClone(nVal.slice(startIndex)))
+ this.splitData()
+ }
+ // #endif
+ }
+ },
+ mounted() {
+ // #ifndef APP-NVUE
+ this.tempList = this.$uv.deepClone(this.copyValue)
+ this.splitData()
+ // #endif
+ },
+ methods: {
+ // 滚动到底部触发事件
+ scrolltolower(e) {
+ this.$uv.sleep(30).then(() => {
+ this.$emit('scrolltolower')
+ })
+ },
+ // 拆分数据
+ async splitData() {
+ let rectArr = [];
+ let emitList = {};
+ if (!this.tempList.length) return
+ for (let i = 1; i <= this.columnNum; i++) {
+ const rect = await this.$uvGetRect(`#uv-waterfall-${i}`);
+ rectArr.push({ ...rect, name: i });
+ }
+ let item = this.tempList[0]
+ // 因为经过上面两个await节点查询和定时器,数组有可能会变成空[],导致item的值为undefined
+ // 解决多次快速滚动会导致数据乱的问题
+ if (!item) return
+ const minCol = this.getMin(rectArr);
+ // 列宽可能使用的到
+ item.width = minCol.width;
+ this[`list${minCol.name}`].push(item);
+ emitList.name = `list${minCol.name}`;
+ emitList.value = item;
+ this.$emit('changeList', emitList);
+ // 移除临时数组中已处理的数据
+ this.tempList.splice(0, 1)
+ // 如果还有数据则继续执行
+ if (this.tempList.length) {
+ let _timeout = this.addTime;
+ // 部分平台在延时较短的情况会出现BUG
+ // #ifdef MP-BAIDU
+ _timeout = _timeout < 200 ? 200 : _timeout;
+ // #endif
+ await this.$uv.sleep(_timeout);
+ this.splitData()
+ } else {
+ this.$emit('finish')
+ }
+ },
+ getMin(arr) {
+ let result = null;
+ const filter = arr.filter(item => item.height == 0);
+ if (!filter.length) {
+ const min = Math.min.apply(Math, arr.map(item => {
+ return item.height;
+ }))
+ const [item] = arr.filter(item => item.height == min);
+ result = item;
+ } else {
+ let newArr = [];
+ arr.map((item, index) => {
+ newArr.push({ len: this[`list${index+1}`].length, item: item });
+ });
+ const minLen = Math.min.apply(Math, newArr.map(item => {
+ return item.len;
+ }))
+ try {
+ const { item } = newArr.find(item => item.len == minLen && item.item.height == 0);
+ result = item;
+ } catch (e) {
+ const { item } = newArr.find(item => item.item.height == 0);
+ result = item;
+ }
+ }
+ return result;
+ },
+ // 清空数据列表
+ async clear() {
+ // 清除数据
+ for (let i = 0; i < this.columnCount; i++) {
+ this[`list${i+1}`] = [];
+ }
+ // #ifdef VUE2
+ this.$emit('input', [])
+ // #endif
+ // #ifdef VUE3
+ this.$emit('update:modelValue', [])
+ // #endif
+ this.tempList = []
+ await this.$uv.sleep(300);
+ this.$emit('clear');
+ },
+ // 清除指定的某一条数据,根据id来实现
+ remove(id) {
+ let index = -1
+ // 删除组件数据
+ for (let i = 1; i <= this.columnCount; i++) {
+ index = this[`list${i}`].findIndex(item => item[this.idKey] == id)
+ if (index != -1) {
+ this[`list${i}`].splice(index, 1)
+ }
+ }
+ // 同时删除父组件对应的数据
+ // #ifdef VUE2
+ index = this.value.findIndex(item => item[this.idKey] == id)
+ if (index != -1) this.$emit('input', this.value.splice(index, 1))
+ // #endif
+ // #ifdef VUE3
+ index = this.modelValue.findIndex(item => item[this.idKey] == id)
+ if (index != -1) this.$emit('update:modelValue', this.modelValue.splice(index, 1))
+ // #endif
+ this.$emit('remove', id);
+ }
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ @import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
+ .uv-waterfall {
+ @include flex(row);
+ align-items: flex-start;
+ &__column {
+ @include flex(column);
+ flex: 1;
+ // #ifndef APP-NVUE
+ height: auto;
+ // #endif
+ }
+ }
+</style>
\ No newline at end of file
diff --git a/uni_modules/uv-waterfall/package.json b/uni_modules/uv-waterfall/package.json
new file mode 100644
index 0000000..9a2bbfc
--- /dev/null
+++ b/uni_modules/uv-waterfall/package.json
@@ -0,0 +1,89 @@
+{
+ "id": "uv-waterfall",
+ "displayName": "uv-waterfall 瀑布流 全面兼容vue3+2、app、h5、小程序等多端",
+ "version": "1.0.8",
+ "description": "该组件主要用于瀑布流式布局显示,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,同时集成nvue的原生瀑布流。",
+ "keywords": [
+ "uv-waterfall",
+ "uvui",
+ "uv-ui",
+ "waterfall",
+ "瀑布流"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.1.0"
+ },
+ "dcloudext": {
+ "type": "component-vue",
+ "sale": {
+ "regular": {
+ "price": "0.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "插件不采集任何数据",
+ "permissions": "无"
+ },
+ "npmurl": ""
+ },
+ "uni_modules": {
+ "dependencies": [
+ "uv-ui-tools",
+ "uv-image",
+ "uv-loading-icon"
+ ],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "y",
+ "aliyun": "y"
+ },
+ "client": {
+ "Vue": {
+ "vue2": "y",
+ "vue3": "y"
+ },
+ "App": {
+ "app-vue": "y",
+ "app-nvue": "y"
+ },
+ "H5-mobile": {
+ "Safari": "y",
+ "Android Browser": "y",
+ "微信浏览器(Android)": "y",
+ "QQ浏览器(Android)": "y"
+ },
+ "H5-pc": {
+ "Chrome": "y",
+ "IE": "y",
+ "Edge": "y",
+ "Firefox": "y",
+ "Safari": "y"
+ },
+ "小程序": {
+ "微信": "y",
+ "阿里": "y",
+ "百度": "y",
+ "字节跳动": "y",
+ "QQ": "y",
+ "钉钉": "u",
+ "快手": "u",
+ "飞书": "u",
+ "京东": "u"
+ },
+ "快应用": {
+ "华为": "u",
+ "联盟": "u"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/uv-waterfall/readme.md b/uni_modules/uv-waterfall/readme.md
new file mode 100644
index 0000000..b043b85
--- /dev/null
+++ b/uni_modules/uv-waterfall/readme.md
@@ -0,0 +1,19 @@
+## Waterfall 瀑布流
+
+> **组件名:uv-waterfall**
+
+该组件主要用于瀑布流式布局显示,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部,同时集成`nvue`的原生瀑布流用于`app-nvue`。常用于一些电商商品展示等,如某宝首页、x红书等。
+
+研究uniapp瀑布流多年,**该方式是目前小程序端最佳方案**,灵活配置,简单易用,开箱即用。
+
+该插件请根据文档耐心查看,`vue`的写法稍微麻烦点,但是效果是很好的,比之前上线的两个版本的瀑布流适用,更有扩展性,我自己的上线项目也是用的此插件。
+
+# <a href="https://www.uvui.cn/components/waterfall.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
diff --git a/util.js b/util.js
index e28c766..bb536c0 100644
--- a/util.js
+++ b/util.js
@@ -1,5 +1,5 @@
import CryptoJS from './node_modules/crypto-js/crypto-js.js'
-const baseUrl = 'https://tmy.xjzbh.com/api/'
+const baseUrl = 'https://xxg1996.xjjyxh.com/api/'
const cryptKey = '2025Shop2025Shop'
const cryptIv = '2025Shop2025Shop'
@@ -38,7 +38,7 @@
}
const getHeader = () => {
- let token = uni.getStorageSync('lzsc-token')
+ let token = uni.getStorageSync('pig-token')
let header = {
'Accept': 'application/json',
'Content-type': 'application/json',
@@ -208,7 +208,8 @@
}
const checkLogin = (url = '', burl = '') => {
- let token = uni.getStorageSync('lzsc-token') ? uni.getStorageSync('lzsc-token') : null
+ console.log('url', url)
+ let token = uni.getStorageSync('pig-token') ? uni.getStorageSync('pig-token') : null
if(token == null){
uni.showModal({
title: '温馨提示',
@@ -237,7 +238,7 @@
}
const checkGoPages = (url = '', burl = '', isgo = 1) => {
- let token = uni.getStorageSync('lzsc-token') ? uni.getStorageSync('lzsc-token') : null
+ let token = uni.getStorageSync('pig-token') ? uni.getStorageSync('pig-token') : null
console.log('token', token)
if(token == null){
uni.showModal({
@@ -260,39 +261,41 @@
})
return false
} else {
- let userInfo = getUserInfo()
- let goUrl = ''
- if(userInfo.idcard_auth == 0){
- goUrl = '/pages/idcard/idcard'
- } else if(userInfo.face_auth == 0){
- goUrl = '/pages/face/face'
- } else if(userInfo.job_auth == 0){
- goUrl = '/pages/job/job'
- } else if(userInfo.bank_auth == 0){
- goUrl = '/pages/addBank/addBank?backUrl=/pages/face/face'
- }
- console.log('goUrl', goUrl)
- if(goUrl != ''){
- uni.showModal({
- title: '温馨提示',
- content: '根据国家法律法规规定,使用本服务需要完成实名认证,是否前往认证?',
- success: function (res) {
- if (res.confirm) {
- uni.navigateTo({
- url: goUrl
- })
- }
- }
- })
- return false
- }
- if(url != '' && isgo == 1){
- uni.navigateTo({
- url: url
- })
- } else {
- return true
- }
+ return true
+ // 隐藏掉实名认证
+ // let userInfo = getUserInfo()
+ // let goUrl = ''
+ // if(userInfo.idcard_auth == 0){
+ // goUrl = '/pages/idcard/idcard'
+ // } else if(userInfo.face_auth == 0){
+ // goUrl = '/pages/face/face'
+ // } else if(userInfo.job_auth == 0){
+ // goUrl = '/pages/job/job'
+ // } else if(userInfo.bank_auth == 0){
+ // goUrl = '/pages/addBank/addBank?backUrl=/pages/face/face'
+ // }
+ // console.log('goUrl', goUrl)
+ // if(goUrl != ''){
+ // uni.showModal({
+ // title: '温馨提示',
+ // content: '根据国家法律法规规定,使用本服务需要完成实名认证,是否前往认证?',
+ // success: function (res) {
+ // if (res.confirm) {
+ // uni.navigateTo({
+ // url: goUrl
+ // })
+ // }
+ // }
+ // })
+ // return false
+ // }
+ // if(url != '' && isgo == 1){
+ // uni.navigateTo({
+ // url: url
+ // })
+ // } else {
+ // return true
+ // }
}
}
--
Gitblit v1.9.3