lin
2026-04-17 8eb6cb869fb69888abc8f99d3b1fad4fbddcdace
feat: 小猪优选极速版提交
41 files modified
419 files added
43336 ■■■■■ changed files
App.vue 14 ●●●● patch | view | raw | blame | history
androidPrivacy.json 6 ●●●● patch | view | raw | blame | history
main.js 4 ●●● patch | view | raw | blame | history
manifest.json 32 ●●●● patch | view | raw | blame | history
pages.json 25 ●●●●● patch | view | raw | blame | history
pages/ginfo/ginfo.vue 2 ●●● patch | view | raw | blame | history
pages/index/index.vue 349 ●●●●● patch | view | raw | blame | history
pages/login/login.vue 90 ●●●● patch | view | raw | blame | history
pages/my/my.vue 178 ●●●● patch | view | raw | blame | history
pages/my/my_bak.vue 246 ●●●●● patch | view | raw | blame | history
pages/order/order.vue 6 ●●●● patch | view | raw | blame | history
pages/pays/pays.vue 4 ●●●● patch | view | raw | blame | history
pages/profile/profile.vue 8 ●●●● patch | view | raw | blame | history
pages/quanshou/index.vue 311 ●●●●● patch | view | raw | blame | history
pages/quanyi/add.vue 410 ●●●●● patch | view | raw | blame | history
pages/quanyi/components/addAddress/index.vue 12 ●●●● patch | view | raw | blame | history
pages/quanyi/components/agreement/index.vue 16 ●●●● patch | view | raw | blame | history
pages/quanyi/components/code/index.vue 323 ●●●●● patch | view | raw | blame | history
pages/quanyi/components/code/set.vue 323 ●●●●● patch | view | raw | blame | history
pages/quanyi/index.vue 341 ●●●●● patch | view | raw | blame | history
pages/web/alipay.vue 42 ●●●●● patch | view | raw | blame | history
pages/web/authorization.vue 106 ●●●●● patch | view | raw | blame | history
pages/web/ceshixz.vue 41 ●●●●● patch | view | raw | blame | history
pages/web/index.vue 46 ●●●●● patch | view | raw | blame | history
pages/web/xieyi.vue 5 ●●●●● patch | view | raw | blame | history
static/css/collect.css 2 ●●● patch | view | raw | blame | history
static/css/index.css 651 ●●●● patch | view | raw | blame | history
static/css/my2.css 154 ●●●●● patch | view | raw | blame | history
static/img/back.png patch | view | raw | blame | history
static/img/kk.png patch | view | raw | blame | history
static/img/left.png patch | view | raw | blame | history
static/img/nion.png patch | view | raw | blame | history
static/img/nion1.png patch | view | raw | blame | history
static/img/off.png patch | view | raw | blame | history
static/img/qsBg.png patch | view | raw | blame | history
static/img/qsBg2.png patch | view | raw | blame | history
static/img/qsLeft.png patch | view | raw | blame | history
static/img/qsLife.png patch | view | raw | blame | history
static/img/qsLifeIcon.png patch | view | raw | blame | history
static/img/qsPhysical.png patch | view | raw | blame | history
static/img/qsRight.png patch | view | raw | blame | history
static/img/qyBg.png patch | view | raw | blame | history
static/img/qyBg2.png patch | view | raw | blame | history
static/img/smsBg.png patch | view | raw | blame | history
static/img/yifa.png patch | view | raw | blame | history
static/imgs/ad1.png patch | view | raw | blame | history
static/imgs/ad2.png patch | view | raw | blame | history
static/imgs/ad3.png patch | view | raw | blame | history
static/imgs/c1.png patch | view | raw | blame | history
static/imgs/c2.png patch | view | raw | blame | history
static/imgs/deb.png patch | view | raw | blame | history
static/imgs/index-logo.png patch | view | raw | blame | history
static/imgs/index-top.png patch | view | raw | blame | history
static/imgs/input-bg.png patch | view | raw | blame | history
static/imgs/logo-icon.png patch | view | raw | blame | history
static/imgs/mf1.png patch | view | raw | blame | history
static/imgs/mf2.png patch | view | raw | blame | history
static/imgs/mf3.png patch | view | raw | blame | history
static/imgs/mf4.png patch | view | raw | blame | history
static/imgs/mf5.png patch | view | raw | blame | history
static/imgs/mf6.png patch | view | raw | blame | history
static/imgs/more.png patch | view | raw | blame | history
static/imgs/my-bg.png patch | view | raw | blame | history
static/imgs/notice.png patch | view | raw | blame | history
static/imgs/nv1.png patch | view | raw | blame | history
static/imgs/nv2.png patch | view | raw | blame | history
static/imgs/nv3.png patch | view | raw | blame | history
static/imgs/nv4.png patch | view | raw | blame | history
static/imgs/nv5.png patch | view | raw | blame | history
static/imgs/tb1.png patch | view | raw | blame | history
static/imgs/tb2.png patch | view | raw | blame | history
static/imgs/tb3.png patch | view | raw | blame | history
static/imgs/tb4.png patch | view | raw | blame | history
static/imgs/xxhf.png patch | view | raw | blame | history
static/imgs/xxhh.png patch | view | raw | blame | history
uni_modules/uni-popup/changelog.md 32 ●●●●● patch | view | raw | blame | history
uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue 74 ●●●● patch | view | raw | blame | history
uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue 11 ●●●● patch | view | raw | blame | history
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue 90 ●●●●● patch | view | raw | blame | history
uni_modules/uni-popup/components/uni-popup/uni-popup.vue 75 ●●●● patch | view | raw | blame | history
uni_modules/uni-popup/package.json 82 ●●●●● patch | view | raw | blame | history
uni_modules/uni-transition/changelog.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uni-transition/components/uni-transition/createAnimation.js 5 ●●●● patch | view | raw | blame | history
uni_modules/uni-transition/components/uni-transition/uni-transition.vue 79 ●●●●● patch | view | raw | blame | history
uni_modules/uni-transition/package.json 97 ●●●●● patch | view | raw | blame | history
uni_modules/uv-album/changelog.md 10 ●●●●● patch | view | raw | blame | history
uni_modules/uv-album/components/uv-album/uv-album.vue 312 ●●●●● patch | view | raw | blame | history
uni_modules/uv-album/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-album/readme.md 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-alert/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-alert/components/uv-alert/props.js 45 ●●●●● patch | view | raw | blame | history
uni_modules/uv-alert/components/uv-alert/uv-alert.vue 246 ●●●●● patch | view | raw | blame | history
uni_modules/uv-alert/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-alert/readme.md 15 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/components/uv-avatar-group/props.js 53 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue 106 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/components/uv-avatar/props.js 80 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue 175 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-avatar/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-back-top/changelog.md 8 ●●●●● patch | view | raw | blame | history
uni_modules/uv-back-top/components/uv-back-top/props.js 58 ●●●●● patch | view | raw | blame | history
uni_modules/uv-back-top/components/uv-back-top/uv-back-top.vue 116 ●●●●● patch | view | raw | blame | history
uni_modules/uv-back-top/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-back-top/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-badge/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-badge/components/uv-badge/props.js 73 ●●●●● patch | view | raw | blame | history
uni_modules/uv-badge/components/uv-badge/uv-badge.vue 176 ●●●●● patch | view | raw | blame | history
uni_modules/uv-badge/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-badge/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-button/components/uv-button/uv-button.vue 2 ●●● patch | view | raw | blame | history
uni_modules/uv-calendar/changelog.md 16 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/components/uv-calendar/calendar.js 546 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/components/uv-calendar/header.vue 104 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/components/uv-calendar/month.vue 616 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/components/uv-calendar/props.js 145 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/components/uv-calendar/uv-calendar.vue 390 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendar/readme.md 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/changelog.md 40 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/calendar-body.vue 376 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/calendar-item.vue 248 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/calendar.js 546 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/i18n/en.json 12 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/i18n/index.js 8 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hans.json 12 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hant.json 12 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/util.js 435 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/components/uv-calendars/uv-calendars.vue 452 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-calendars/readme.md 23 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code-input/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code-input/components/uv-code-input/props.js 83 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code-input/components/uv-code-input/uv-code-input.vue 272 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code-input/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code-input/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code/changelog.md 9 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code/components/uv-code/props.js 35 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code/components/uv-code/uv-code.vue 136 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-code/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/changelog.md 5 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/components/uv-collapse-item/props.js 60 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/components/uv-collapse-item/uv-collapse-item.vue 229 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/components/uv-collapse/props.js 20 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/components/uv-collapse/uv-collapse.vue 86 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-collapse/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-down/changelog.md 9 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-down/components/uv-count-down/props.js 25 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-down/components/uv-count-down/utils.js 62 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-down/components/uv-count-down/uv-count-down.vue 169 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-down/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-down/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-to/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-to/components/uv-count-to/props.js 60 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-to/components/uv-count-to/uv-count-to.vue 187 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-to/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-count-to/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js 2 ●●● patch | view | raw | blame | history
uni_modules/uv-datetime-picker/components/uv-datetime-picker/uv-datetime-picker.vue 2 ●●● patch | view | raw | blame | history
uni_modules/uv-divider/changelog.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-divider/components/uv-divider/props.js 45 ●●●●● patch | view | raw | blame | history
uni_modules/uv-divider/components/uv-divider/uv-divider.vue 113 ●●●●● patch | view | raw | blame | history
uni_modules/uv-divider/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-divider/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-drop-down/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-drop-down/components/uv-drop-down-item/uv-drop-down-item.vue 169 ●●●●● patch | view | raw | blame | history
uni_modules/uv-drop-down/components/uv-drop-down-popup/uv-drop-down-popup.vue 242 ●●●●● patch | view | raw | blame | history
uni_modules/uv-drop-down/components/uv-drop-down/uv-drop-down.vue 135 ●●●●● patch | view | raw | blame | history
uni_modules/uv-drop-down/package.json 91 ●●●●● patch | view | raw | blame | history
uni_modules/uv-drop-down/readme.md 23 ●●●●● patch | view | raw | blame | history
uni_modules/uv-empty/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-empty/components/uv-empty/props.js 60 ●●●●● patch | view | raw | blame | history
uni_modules/uv-empty/components/uv-empty/uv-empty.vue 126 ●●●●● patch | view | raw | blame | history
uni_modules/uv-empty/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-empty/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-gap/changelog.md 2 ●●● patch | view | raw | blame | history
uni_modules/uv-grid/changelog.md 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-grid/components/uv-grid-item/props.js 15 ●●●●● patch | view | raw | blame | history
uni_modules/uv-grid/components/uv-grid-item/uv-grid-item.vue 226 ●●●●● patch | view | raw | blame | history
uni_modules/uv-grid/components/uv-grid/props.js 20 ●●●●● patch | view | raw | blame | history
uni_modules/uv-grid/components/uv-grid/uv-grid.vue 100 ●●●●● patch | view | raw | blame | history
uni_modules/uv-grid/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-grid/readme.md 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-image/changelog.md 36 ●●●●● patch | view | raw | blame | history
uni_modules/uv-image/components/uv-image/props.js 95 ●●●●● patch | view | raw | blame | history
uni_modules/uv-image/components/uv-image/uv-image.vue 287 ●●●●● patch | view | raw | blame | history
uni_modules/uv-image/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-image/readme.md 15 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/changelog.md 18 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/components/uv-index-anchor/props.js 30 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/components/uv-index-anchor/uv-index-anchor.vue 98 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/components/uv-index-item/uv-index-item.vue 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/components/uv-index-list/props.js 30 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/components/uv-index-list/uv-index-list.vue 461 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-index-list/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/changelog.md 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/components/uv-keyboard-car/props.js 24 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/components/uv-keyboard-car/uv-keyboard-car.vue 347 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/components/uv-keyboard-number/props.js 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/components/uv-keyboard-number/uv-keyboard-number.vue 201 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/components/uv-keyboard/props.js 95 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/components/uv-keyboard/uv-keyboard.vue 180 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-keyboard/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-line-progress/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-line-progress/components/uv-line-progress/props.js 29 ●●●●● patch | view | raw | blame | history
uni_modules/uv-line-progress/components/uv-line-progress/uv-line-progress.vue 146 ●●●●● patch | view | raw | blame | history
uni_modules/uv-line-progress/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-line-progress/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-link/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-link/components/uv-link/props.js 40 ●●●●● patch | view | raw | blame | history
uni_modules/uv-link/components/uv-link/uv-link.vue 83 ●●●●● patch | view | raw | blame | history
uni_modules/uv-link/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-link/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-list/changelog.md 25 ●●●●● patch | view | raw | blame | history
uni_modules/uv-list/components/uv-list-item/uv-list-item.vue 535 ●●●●● patch | view | raw | blame | history
uni_modules/uv-list/components/uv-list/uv-list.vue 147 ●●●●● patch | view | raw | blame | history
uni_modules/uv-list/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-list/readme.md 27 ●●●●● patch | view | raw | blame | history
uni_modules/uv-load-more/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-load-more/components/uv-load-more/props.js 95 ●●●●● patch | view | raw | blame | history
uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue 152 ●●●●● patch | view | raw | blame | history
uni_modules/uv-load-more/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-load-more/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-modal/changelog.md 28 ●●●●● patch | view | raw | blame | history
uni_modules/uv-modal/components/uv-modal/props.js 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-modal/components/uv-modal/uv-modal.vue 245 ●●●●● patch | view | raw | blame | history
uni_modules/uv-modal/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-modal/readme.md 23 ●●●●● patch | view | raw | blame | history
uni_modules/uv-navbar/changelog.md 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-navbar/components/uv-navbar/props.js 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue 245 ●●●●● patch | view | raw | blame | history
uni_modules/uv-navbar/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-navbar/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-no-network/changelog.md 5 ●●●●● patch | view | raw | blame | history
uni_modules/uv-no-network/components/uv-no-network/props.js 20 ●●●●● patch | view | raw | blame | history
uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue 222 ●●●●● patch | view | raw | blame | history
uni_modules/uv-no-network/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-no-network/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/changelog.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/components/uv-column-notice/props.js 61 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue 176 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/components/uv-notice-bar/props.js 76 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue 110 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/components/uv-row-notice/props.js 40 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue 341 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notice-bar/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notify/changelog.md 9 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notify/components/uv-notify/props.js 45 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notify/components/uv-notify/uv-notify.vue 220 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notify/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-notify/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-number-box/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-number-box/components/uv-number-box/props.js 113 ●●●●● patch | view | raw | blame | history
uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue 395 ●●●●● patch | view | raw | blame | history
uni_modules/uv-number-box/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-number-box/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/components/uv-parse/node/node.vue 576 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/components/uv-parse/parser.js 1335 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/components/uv-parse/uv-parse.vue 498 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/readme.md 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js 224 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-parse/static/app-plus/uv-parse/local.html 1 ●●●● patch | view | raw | blame | history
uni_modules/uv-pick-color/changelog.md 18 ●●●●● patch | view | raw | blame | history
uni_modules/uv-pick-color/components/uv-pick-color/colors.js 217 ●●●●● patch | view | raw | blame | history
uni_modules/uv-pick-color/components/uv-pick-color/props.js 47 ●●●●● patch | view | raw | blame | history
uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue 551 ●●●●● patch | view | raw | blame | history
uni_modules/uv-pick-color/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-pick-color/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-picker/components/uv-picker/props.js 2 ●●● patch | view | raw | blame | history
uni_modules/uv-picker/components/uv-picker/uv-picker.vue 2 ●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/cache.js 1 ●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js 241 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js 18 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js 8 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js 666 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js 298 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js 142 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js 23 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js 1191 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js 22 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js 22 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js 22 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js 3 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js 74 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js 96 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js 24 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js 39 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/props.js 85 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js 34 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/queue.js 41 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue 1038 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-qrcode/readme.md 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/changelog.md 31 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/components/uv-radio-group/props.js 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue 115 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/components/uv-radio/props.js 65 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/components/uv-radio/uv-radio.vue 348 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-radio/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-rate/changelog.md 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-rate/components/uv-rate/props.js 73 ●●●●● patch | view | raw | blame | history
uni_modules/uv-rate/components/uv-rate/uv-rate.vue 274 ●●●●● patch | view | raw | blame | history
uni_modules/uv-rate/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-rate/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-read-more/changelog.md 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-read-more/components/uv-read-more/props.js 62 ●●●●● patch | view | raw | blame | history
uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue 162 ●●●●● patch | view | raw | blame | history
uni_modules/uv-read-more/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-read-more/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/changelog.md 5 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/components/uv-col/props.js 30 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/components/uv-col/uv-col.vue 165 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/components/uv-row/props.js 20 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/components/uv-row/uv-row.vue 95 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-row/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/changelog.md 12 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js 29 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/components/uv-scroll-list/props.js 35 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs 51 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue 218 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-scroll-list/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-search/changelog.md 25 ●●●●● patch | view | raw | blame | history
uni_modules/uv-search/components/uv-search/props.js 127 ●●●●● patch | view | raw | blame | history
uni_modules/uv-search/components/uv-search/uv-search.vue 311 ●●●●● patch | view | raw | blame | history
uni_modules/uv-search/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-search/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeleton/changelog.md 9 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeleton/components/uv-skeleton/props.js 65 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue 250 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeleton/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeleton/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeletons/changelog.md 4 ●●●● patch | view | raw | blame | history
uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue 247 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeletons/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-skeletons/readme.md 23 ●●●●● patch | view | raw | blame | history
uni_modules/uv-slider/changelog.md 9 ●●●●● patch | view | raw | blame | history
uni_modules/uv-slider/components/uv-slider/props.js 58 ●●●●● patch | view | raw | blame | history
uni_modules/uv-slider/components/uv-slider/uv-slider.vue 58 ●●●●● patch | view | raw | blame | history
uni_modules/uv-slider/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-slider/readme.md 15 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/changelog.md 10 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/components/uv-steps-item/props.js 25 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue 347 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/components/uv-steps/props.js 40 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/components/uv-steps/uv-steps.vue 83 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-steps/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-sticky/changelog.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-sticky/components/uv-sticky/props.js 41 ●●●●● patch | view | raw | blame | history
uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue 223 ●●●●● patch | view | raw | blame | history
uni_modules/uv-sticky/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-sticky/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-subsection/changelog.md 10 ●●●●● patch | view | raw | blame | history
uni_modules/uv-subsection/components/uv-subsection/props.js 54 ●●●●● patch | view | raw | blame | history
uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue 269 ●●●●● patch | view | raw | blame | history
uni_modules/uv-subsection/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-subsection/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/changelog.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs 256 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs 225 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js 264 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js 182 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js 40 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue 200 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js 15 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action/props.js 10 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue 65 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swipe-action/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/changelog.md 16 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/components/uv-swiper-indicator/props.js 30 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue 112 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/components/uv-swiper/props.js 136 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue 263 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-swiper/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-switch/changelog.md 14 ●●●●● patch | view | raw | blame | history
uni_modules/uv-switch/components/uv-switch/props.js 58 ●●●●● patch | view | raw | blame | history
uni_modules/uv-switch/components/uv-switch/uv-switch.vue 192 ●●●●● patch | view | raw | blame | history
uni_modules/uv-switch/package.json 88 ●●●●● patch | view | raw | blame | history
uni_modules/uv-switch/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/changelog.md 14 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/components/uv-tabbar-item/props.js 40 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue 146 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/components/uv-tabbar/props.js 50 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue 146 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabbar/readme.md 21 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabs/changelog.md 22 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabs/components/uv-tabs/props.js 71 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue 390 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabs/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tabs/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tags/changelog.md 10 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tags/components/uv-tags/props.js 95 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tags/components/uv-tags/uv-tags.vue 379 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tags/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tags/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-text/changelog.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-text/components/uv-text/props.js 113 ●●●●● patch | view | raw | blame | history
uni_modules/uv-text/components/uv-text/uv-text.vue 218 ●●●●● patch | view | raw | blame | history
uni_modules/uv-text/components/uv-text/value.js 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-text/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-text/readme.md 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-toast/changelog.md 10 ●●●●● patch | view | raw | blame | history
uni_modules/uv-toast/components/uv-toast/uv-toast.vue 333 ●●●●● patch | view | raw | blame | history
uni_modules/uv-toast/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-toast/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-toolbar/components/uv-toolbar/props.js 2 ●●● patch | view | raw | blame | history
uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue 4 ●●●● patch | view | raw | blame | history
uni_modules/uv-toolbar/readme.md 2 ●●● patch | view | raw | blame | history
uni_modules/uv-tooltip/changelog.md 13 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tooltip/components/uv-tooltip/props.js 60 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue 372 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tooltip/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-tooltip/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-ui/changelog.md 353 ●●●●● patch | view | raw | blame | history
uni_modules/uv-ui/components/uv-ui/uv-ui.vue 7 ●●●●● patch | view | raw | blame | history
uni_modules/uv-ui/package.json 162 ●●●●● patch | view | raw | blame | history
uni_modules/uv-ui/readme.md 164 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/changelog.md 17 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue 52 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/components/uv-upload/mixin.js 22 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/components/uv-upload/props.js 130 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/components/uv-upload/utils.js 151 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/components/uv-upload/uv-upload.vue 488 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/package.json 90 ●●●●● patch | view | raw | blame | history
uni_modules/uv-upload/readme.md 11 ●●●●● patch | view | raw | blame | history
uni_modules/uv-vtabs/changelog.md 20 ●●●●● patch | view | raw | blame | history
uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue 68 ●●●●● patch | view | raw | blame | history
uni_modules/uv-vtabs/components/uv-vtabs/props.js 79 ●●●●● patch | view | raw | blame | history
uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue 438 ●●●●● patch | view | raw | blame | history
uni_modules/uv-vtabs/package.json 87 ●●●●● patch | view | raw | blame | history
uni_modules/uv-vtabs/readme.md 19 ●●●●● patch | view | raw | blame | history
uni_modules/uv-waterfall/changelog.md 24 ●●●●● patch | view | raw | blame | history
uni_modules/uv-waterfall/components/uv-waterfall/props.js 69 ●●●●● patch | view | raw | blame | history
uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue 265 ●●●●● patch | view | raw | blame | history
uni_modules/uv-waterfall/package.json 89 ●●●●● patch | view | raw | blame | history
uni_modules/uv-waterfall/readme.md 19 ●●●●● patch | view | raw | blame | history
util.js 9 ●●●●● patch | view | raw | blame | history
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%;
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://qxg1996.lllykj.com/?gp=1 \">《注册协议》</a>和<a href=\"https://qxg1996.lllykj.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://qxg1996.lllykj.com/?gp=1\">《注册协议》</a>和<a href=\"https://qxg1996.lllykj.com/?gp=2\">《隐私政策》</a>,否则将退出应用。",
        "buttonAccept" : "同意并继续",
        "buttonRefuse" : "退出应用"
    },
main.js
@@ -4,11 +4,13 @@
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://qxg1996.lllykj.com'
Vue.prototype.$bank = [
    '中国工商银行',
    '中国建设银行',
manifest.json
@@ -1,14 +1,14 @@
{
    "name" : "齐享购",
    "appid" : "__UNI__3333B1D",
    "name" : "小猪优选极速版",
    "appid" : "__UNI__D83BA03",
    "description" : "",
    "versionName" : "1.4.0",
    "versionCode" : 140,
    "versionName" : "1.3.6",
    "versionCode" : 136,
    "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" : "pig-app",
                "targetSdkVersion" : 30,
                "abiFilters" : [ "arm64-v8a" ]
            },
@@ -82,7 +82,7 @@
                        "com.apple.developer.associated-domains" : [ "applinks:www.ncmiruo.com" ]
                    }
                },
                "urltypes" : "miruo-app"
                "urltypes" : "pig-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.lelinjs.xzyx",
                    "ios_bundle_id" : "com.lelinjs.xzyx",
                    "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.lelinjs.xzyx",
                    "ios_bundle_id" : "com.lelinjs.xzyx",
                    "isCloud" : true,
                    "bought" : 1,
                    "pid" : "1491",
                    "parameters" : {}
                }
            }
        }
    },
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": "会员权收"
            }
        },
@@ -249,33 +252,33 @@
        "list": [
            {
                "pagePath": "pages/index/index",
                "iconPath": "static/imgs/tab-1.png",
                "selectedIconPath": "static/imgs/tab-s-1.png",
                "iconPath": "static/imgs/tb1.png",
                "selectedIconPath": "static/imgs/tb1.png",
                "text": "首页"
            },
            {
                "pagePath": "pages/cates/cates",
                "iconPath": "static/imgs/tab-2.png",
                "selectedIconPath": "static/imgs/tab-s-2.png",
                "iconPath": "/static/imgs/tb2.png",
                "selectedIconPath": "/static/imgs/tb2.png",
                "text": "分类"
            },
            {
                "pagePath": "pages/carts/carts",
                "iconPath": "static/imgs/tab-3.png",
                "selectedIconPath": "static/imgs/tab-s-3.png",
                "iconPath": "static/imgs/tb3.png",
                "selectedIconPath": "static/imgs/tb3.png",
                "text": "购物车"
            },
            {
                "pagePath": "pages/my/my",
                "iconPath": "static/imgs/tab-4.png",
                "selectedIconPath": "static/imgs/tab-s-4.png",
                "iconPath": "static/imgs/tb4.png",
                "selectedIconPath": "static/imgs/tb4.png",
                "text": "我的"
            }
        ]
    },
    "globalStyle": {
        "navigationBarTextStyle": "black",
        "navigationBarTitleText": "齐享购",
        "navigationBarTitleText": "小猪优选极速版",
        "navigationBarBackgroundColor": "#F8F8F8",
        "backgroundColor": "#F8F8F8"
    },
pages/ginfo/ginfo.vue
@@ -14,7 +14,7 @@
            <view class="">
                <text>¥</text>
                <text>{{goodsData.price}}</text>
                <text>积分:{{goodsData.points}}</text>
                <!-- <text>积分:{{goodsData.points}}</text> -->
            </view>
            <view class="collect" @tap="collectGoods">
                <image :src="goodsData.collect == 1 ? '/static/imgs/colls.png' : '/static/imgs/coll.png'" mode=""></image>
pages/index/index.vue
@@ -1,128 +1,93 @@
<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>
                <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 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>
            <view class="search">
                <view class="search-1">
                    <image src="@/static/imgs/s2.png" mode=""></image>
                    <input type="text" placeholder="请输入关键词" />
        <!-- #endif -->
        <view class="hLogo">
            <view class="hLogoView">
                <view class="hLogo-1">
                    <image class="h-logo" src="/static/imgs/index-logo.png" mode=""></image>
                </view>
                <view class="search-2">
                    <view class="">搜索</view>
                </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 class="hLogo-2" @tap="$util.goPages('../search/search')">
                    <image class="s-icon" src="/static/imgs/search.png" mode=""></image>
                    <input type="text" placeholder="请搜索您想要的商品" />
                </view>
            </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="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="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="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.goSwitch('/pages/cates/cates')">
            <image src="/static/imgs/ad1.png" mode=""></image>
                </view>
        <view class="ad2" @tap="$util.goSwitch('/pages/cates/cates')">
            <image src="/static/imgs/ad2.png" mode=""></image>
            </view>
            <view class="comm-2"></view>
            <view class="comm-3">
                <view class="comm-title">
                    <image src="@/static/imgs/jfdh.png" mode=""></image>
        <view class="ad3" @tap="$util.goSwitch('/pages/cates/cates')">
            <image src="/static/imgs/ad3.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 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>
            </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 class="goods-main">
            <view class="goods-main-view">
                <view class="goods-list">
                    <view class="goods-item" v-for="(itm, index) in goodsData" @click="$util.goPages('/pages/ginfo/ginfo?id=' + itm.id)">
            <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.price}}</view>
                            <view class="hyj">
                                <image src="@/static/imgs/hyj.png" mode=""></image>
                            </view>
                            <view>¥</view>
                            <view class="red">{{itm.price}}</view>
                        </view>
                    </view>
                </scroll-view>
                </view>
            </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">
                    <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 class="edu-btns" @tap="go_auth">立即领取</view>
          <!-- <view class="edu-btns" @tap="$util.goPages('/pages/idcard/idcard')">立即领取</view> -->
                </view>
            </view>
        </uni-popup>
@@ -132,10 +97,25 @@
<script>
    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 {
                scrollIndex: 0,
                scrollCurrent: '',
      contactsFlag: false,
                staHeight: 0,
                paddingHeight: 0,
                indicatorDots: true,
@@ -146,78 +126,114 @@
                duration: 500,
                bannerData: [],
                catesNavData: [],
                goodsData: [],
                prefGoods: [],
                hotGoods: [],
                pointGoods: [],
      catesHotData: [],
                userInfo: null,
                config: null,
      statusBarHeight: 30,
      StatusBar: 0,
      hotGoods: null,
      newGoods: null,
      hotCates: null,
            }
        },
        onShow() {        
            this.getData()
            this.getConfig()
            this.getUserInfo()
    // #ifdef APP-PLUS
    plus.navigator.setStatusBarBackground('transparent');
    // #endif
  },
  created() {
        },
        onLoad() {
    const systemInfo = uni.getSystemInfoSync();
    this.statusBarHeight = systemInfo.statusBarHeight || 30;
        },
        onReady() {
            // #ifdef APP-PLUS
            plus.navigator.setStatusBarBackground('#FFD2D8')
    this.getSystemInfo();
            // #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
                })
  mounted() {
            },
            scrollClick (index) {
                this.scrollIndex = index
                this.scrollCurrent = 'scrollItem' + index
                this.goodsData = []
                this.goodsData = this.catesNavData[index].goods
                setTimeout(() => {
                    this.scrollCurrent = ''; // 重置以便下次可以滚动到相同位置
                }, 500);
  methods: {
    //获取状态栏高度
    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)
                        } else {
                            setTimeout(() => {
                                t.$refs.popup2.close()
              //t.$refs.popup2.open()
                            }, 100)
                        }
                        if(res.data.contacts == 0 && uni.getSystemInfoSync().platform == 'android' && this.config.put_open == 2){
                            t.requestAndroidPermission('android.permission.READ_CONTACTS')
                        }
          // 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.$util.checkLogin('/pages/index/index')){
                    return
      if(!this.userInfo){
          return uni.showToast({
            title: '请先登录!',
            icon: 'error'
          })
                }
                if(this.userInfo && this.userInfo.over_auth == 1) {
                    return uni.showToast({
@@ -257,7 +273,16 @@
                    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
      })
    },
            setContacts () {
                uni.showLoading({
                    title: '授权中...'
@@ -302,3 +327,75 @@
<style>
    @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>
pages/login/login.vue
@@ -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,6 +77,62 @@
            agreeClick () {
                this.loginAct = !this.loginAct
            },
            // 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
@@ -115,29 +171,29 @@
          if (res.code == 1) {
            uni.setStorageSync('lzsc-token', res.data.userToken)
                    uni.setStorageSync('pig-token', res.data.userToken)
            uni.setStorageSync('user-info', JSON.stringify(res.data))
            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
            }
                    // 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
                    // }
            t.qxgLogin = ygxRes.data.data
            uni.setStorageSync('qxgLogin', t.qxgLogin)
                    // t.qxgLogin = ygxRes.data.data
                    // uni.setStorageSync('qxgLogin', t.qxgLogin)
            if (this.backUrl == '') {
              isSwitch = 1
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,82 +23,49 @@
                        </template>
                    </template>
                    <template v-if="userInfo != null && userInfo.face_auth == 0">
                        <view class="edu" @tap="$util.checkGoPages('/pages/my/my')">未认证(去认证)</view>
                    </template>
                </view>
                        <view class="edu" @tap="$util.checkGoPages('/pages/my/my')">未认证</view>
                    </template> -->
            </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">
            <!-- <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="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 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="myServe">
            <view class="myNavs-tips">
                <text class="myNavs-tips-text">我的服务</text>
            <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">
                            <image :src="'../../static/imgs/mf' + item.index + '.png'" mode=""></image>
                            <view>{{item.text}}</view>
            </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 class="mySerCon-v" @tap="goKefu" v-else>
                            <image :src="'../../static/imgs/mf' + item.index + '.png'" mode=""></image>
                            <view>{{item.text}}</view>
                </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>
                    </template>
                </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>
@@ -105,24 +73,13 @@
                <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 @tap="goWebUrl('http://beian.miit.gov.cn')">Copyright © 2025 乐陵力宇科技 All Rights Reserved.</view>
            
        </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 +94,30 @@
            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: '联系客服'},
                ]
            }
        },
    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)
          }
        })
      }
        },
        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'
              })
            } else {
              _this.errState = res.data.data;
              _this.$refs.comment.open()
            }
          }
        })
      },
            goKefu() {
                if(this.Config && this.Config.kefu_open == 1) {
                    this.wechatWork()
@@ -300,7 +229,6 @@
        }
    }
</script>
<style>
    @import url(/static/css/my.css);
    @import url(/static/css/my2.css);
</style>
pages/my/my_bak.vue
New file
@@ -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>
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>
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>
pages/profile/profile.vue
@@ -27,7 +27,7 @@
                    <input type="text" disabled v-model="userInfo.idcard" placeholder="请输入身份证" />
                </view>
            </view>
            <view class="profile-item">
            <!-- <view class="profile-item">
                <text class="label">总余额</text>
                <view class="">
                    <input type="text" disabled v-model="userInfo.edu" placeholder="请输入可用额度" />
@@ -45,7 +45,7 @@
                    <switch @change="equityOpen" v-if="userInfo.equity_open == 1" checked color="#e3b400" style="transform:scale(0.7)"/>
                    <switch @change="equityOpen" v-else color="#e3b400" style="transform:scale(0.7)"/>
                </view>
            </view>
            </view> -->
        </view>
        <view class="logout" @tap="logOut">退出登录</view>
        <view class="logout red" @tap="logOff">注销账号</view>
@@ -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,7 +110,7 @@
                })
            },
            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)
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-->
<!--        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-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>
          <view class="card-date" style="height: 40rpx"> </view>
          <!--          <view class="card-date">{{ i.buyDate }}</view>-->
          <view class="has-buy">{{ i.isBuy?'已扣款' : '待扣款' }}</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="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/imgs/yifa.png" style="width:372rpx;height: 240rpx;"></image>
            <image src="/static/img/yifa.png" style="width: 370rpx;height: 274rpx;"></image>
        </view>
        <view style="font-size: 32rpx;font-weight: 600;color: #333333;text-align: center;margin-top: 34rpx;margin-bottom: 80rpx;padding: 0 28rpx;">
        </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.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>
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];
@@ -144,18 +144,41 @@
    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(() => {
          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()
            }
@@ -362,9 +424,6 @@
      })
    },
    agreeClick () {
      this.loginAct = !this.loginAct
    },
    getSmsCode(){
      const that = this;
@@ -383,8 +442,9 @@
        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'
      });
@@ -396,7 +456,7 @@
        url: that.$webHost+'/api/v2/xinheyuan/bankcard/save',
        method:'POST',
        data:{
          "customerId": uni.getStorageSync('qxgLogin').customerId,
            "customerId": uni.getStorageSync('ygxLogin').customerId,
          "nameReal": that.cardName, // 名字
          "cardNo":that.cardNumber,
          "idCard": that.idCardNo,
@@ -417,7 +477,7 @@
            url: that.$webHost+'/api/v2/pay/bindCardToCodes',
            method:'POST',
            data:{
              "customerId": uni.getStorageSync('qxgLogin').customerId,
                "customerId": uni.getStorageSync('ygxLogin').customerId,
              "cardName": that.cardName, // 名字
              "cardIdNo": that.idCardNo,
@@ -520,7 +580,7 @@
      }
      const paymentMethod = this.getPayBindCardInfo[1]['paymentMethod'];
      const _this = this;
      const pro = uni.getStorageSync('qxgLogin')
        const pro = uni.getStorageSync('ygxLogin')
      pro['userName'] = _this.cardName;
      uni.request({
        url: _this.$webHost+'/api/v2/pay/bindCardToCodes',
@@ -591,7 +651,7 @@
        data:{
          ...callbackData,
          "customerId": uni.getStorageSync('qxgLogin').customerId,
            "customerId": uni.getStorageSync('ygxLogin').customerId,
          "cardPhone" : _this.cardPhone,  //手机号
          "cardNumber": _this.cardNumber, //卡号
@@ -605,11 +665,18 @@
        },
        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('qxgLogin').customerId}`,
              url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
            method:'get',
            success:function(res){
              const  ls =  res.data.data;
@@ -619,10 +686,13 @@
                _this.countText_A = '';
                _this.$refs.codeUi.close()
                _this.$refs.setUi.open()
                return;
              }
              if(!ls || !ls.some(item => item.bindStatus == 0)) {_this.kk();return;}
            }
@@ -638,7 +708,7 @@
      }
      const paymentMethod = this.getPayBindCardInfo[2]['paymentMethod'];
      const _this = this;
      const pro = uni.getStorageSync('qxgLogin')
        const pro = uni.getStorageSync('ygxLogin')
      pro['userName'] = _this.cardName;
      uni.request({
        url: _this.$webHost+'/api/v2/pay/bindCardToCodes',
@@ -718,7 +788,7 @@
        data:{
          ...callbackData,
          "customerId": uni.getStorageSync('qxgLogin').customerId,
            "customerId": uni.getStorageSync('ygxLogin').customerId,
          "cardPhone" : _this.cardPhone,  //手机号
          "cardNumber": _this.cardNumber, //卡号
@@ -732,13 +802,17 @@
        },
        success(dts){
          if(dts.data.code!==200){
            _this.showToast({
              title: dts.data.data
              // _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('qxgLogin').customerId}`,
              url:_this.$webHost+`/api/v2/pay/getPayBindCardInfo/${uni.getStorageSync('ygxLogin').customerId}`,
            method:'get',
            success:function(res){
              const ls = res.data.data;
@@ -762,7 +836,17 @@
    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'
@@ -776,103 +860,124 @@
</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>
        <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;">
    </uv-popup>
    <uv-popup mode="center" round="20" ref="setUi" :closeOnClickOverlay="false" >
      <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/imgs/closeA.png"
            style="  width: 50rpx; height: 50rpx; "
                @click="$refs.setUi.close()"
                src="@/static/img/off.png"
                style="
            width: 50rpx;
            height: 50rpx;
            position: absolute;
            right: 0;
            top: -2rpx;
          "
        ></image>
      </view>
    </uv-popup>
    <uv-popup mode="center" round="20" ref="setUi" :closeOnClickOverlay="false"  bgColor="" z-index="999">
      <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>
        <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>
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,7 +83,7 @@
                this.detail.cityCode = e.value[1].code
                this.detail.cityName = e.value[1].title
        console.log(e.value[2]);
        if(e.value[2]) {
                    this.detail.areaCode = e.value[2].code
                    this.detail.areaName = e.value[2].title
@@ -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;
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;
pages/quanyi/components/code/index.vue
New file
@@ -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>
pages/quanyi/components/code/set.vue
New file
@@ -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>
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;
    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',
      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(() => {
@@ -49,26 +45,6 @@
            })
          }
        })
      }
    })
    // 获取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 {
            }
          }
        })
@@ -81,32 +57,28 @@
    this.time= setTimeout(()=>{
      this.protocolChecked =  [1];
      this.tm = true;
      this.isAgree = true;
    },10000)
    },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="margin-left: 20rpx;display: flex;justify-content: space-between;">
        <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 style="display: flex;justify-content: flex-start;position: absolute;left: 0;top: 15rpx;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 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 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>
          <image src="/static/imgs/VIPSOA.png" style="width: 272rpx;height: 272rpx;"></image>
            小猪优选极速版商城会员年卡是本商城为会员特别打造的专属服务。成为会员后,您可享受各类商品折扣、促销优惠、生活权益及专属特权,并根据会员等级获赠相应超值礼品,体验我们为您准备的尊享会员服务。
          </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;">
            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 class="tab-wrap">
        <text @click="active=0" :class="active==0?'active':''">实物权益</text>
        <text @click="active=1" :class="active==1?'active':''">虚拟权益</text>
        <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="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 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 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>
pages/web/alipay.vue
New file
@@ -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>
pages/web/authorization.vue
New file
@@ -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>
pages/web/ceshixz.vue
New file
@@ -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>
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>
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
        })
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;
static/css/index.css
@@ -1,258 +1,105 @@
page{
    background-color: #EEEBEC;
    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: 422rpx;
    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%;
}
.index-title{
    padding: 30rpx 0;
.btn-group {
    display: flex;
    margin-top: 30rpx;
}
.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;
.cancel-btn {
    flex: 1;
    justify-content: center;
    align-items: center;
    margin-right: 20rpx;
    background-color: #f5f5f5;
}
.index-title-view-rf image{
    width: 40rpx;
    height: 40rpx;
    display: block;
    margin: 0 auto;
.allow-btn {
    flex: 1;
    background-color: #007aff;
    color: white;
}
.search{
    position: fixed;
    left: 0;
    width: 100%;
    top: var(--status-bar-height);
    z-index: 9999;
    background: #fbf8f8;
    height: 110upx;
}
.search-v{
    width: 94%;
    margin-left: 3%;
    display: flex;
    height: 70upx;
    margin-top: 20upx;
    margin-bottom: 30upx;
}
.search-1{
    width: 76%;
    display: flex;
    justify-content: center;
    align-items: center;
}
.search-1 input{
    width: 76%;
    height: 72rpx;
    background: #FFFFFF;
    border-radius: 50rpx;
    padding-left: 14%;
    font-size: 26upx;
}
.search-1 image{
    position: absolute;
    width: 40rpx;
    height: 40rpx;
    left: 8%;
}
.search-2{
    width: 20%;
    display: flex;
    font-size: 24upx;
    justify-content: center;
    align-items: center;
    color: #9e9d9d;
    background-color: #ffffff;
    border-radius: 30upx;
}
.search-2 view{
    width: 112rpx;
    height: 72rpx;
    text-align: center;
    line-height: 72rpx;
    color: #ffffff;
    background: #FF1472;
    border-radius: 200rpx;
.search-icon{
    width:  40upx;
    height: 40upx;
    margin-right: 20upx;
}
.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;
    margin: 20upx auto 20upx;
}
.swiper, .swiper image{
    width: 100%;
    height: 278rpx;
    border-radius: 32rpx;
    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: 100%;
    margin: 0;
    border-top-right-radius: 30rpx;
    border-top-left-radius: 30rpx;
    background-color: #ffffff;
    padding: 0;
    display: flex;
    flex-wrap: wrap; /* 确保子元素换行 */
}
.cates-v{
    float: left;
    width: 20%;
    border-radius: 50%;
    margin-bottom: 0upx;
}
.cates-v-icon{
    width: 80upx;
    height: 80upx;
    width: 90rpx;
    height: 90rpx;
    display: block;
    margin: 40upx auto 20upx;
    border-radius: 50%;
    border-radius: 10upx;
}
.cates-v view{
    text-align: center;
@@ -261,10 +108,9 @@
}
.goods-main{
    width: 94%;
    margin: 0upx auto 0upx;
    border-bottom-left-radius: 10upx;
    border-bottom-right-radius: 10upx;
    border-top: 1px solid #eeeeee;
    margin: 10upx auto 30upx;
    background-color: #ffffff;
    border-radius: 10upx;
}
.goods-main-title{
    padding: 20upx;
@@ -288,20 +134,11 @@
    height: 340upx;
}
.goods-list{
    width: 100%;
    width: 96%;
    display: flex;
    margin-left: 2%;
    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;
}
.goods-item{
    width: 48%;
@@ -315,7 +152,7 @@
    margin-bottom: 20upx;
}
.goods-price{
    margin-top: 16upx;
    margin-top: 0upx;
    display: flex;
    align-items: center;
    color: #F75451;
@@ -324,7 +161,8 @@
    padding-bottom: 20upx;
}
.goods-price .red{
    font-size: 26upx !important;
    margin-left: 10upx;
    font-size: 24rpx !important;
    font-weight: 600;
}
.goods-title{
@@ -385,7 +223,7 @@
.auth{
    width: 94%;
    margin: 30upx auto;
    margin: 110upx auto 0;
    height: 240upx;
}
.auth image{
@@ -457,97 +295,300 @@
    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: 211rpx;
    height: 48rpx;
}
.hLogo-1{
    margin: 0 30upx;
}
.hLogo-2{
    position: relative;
    width: 420rpx;
    height: 70rpx;
    background: url(@/static/imgs/input-bg.png) no-repeat;
    background-size: 100% 100%;
}
.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: 320rpx;
}
.ad1 image{
    width: 100%;
    height: 100%;
}
.ad2{
    width: 96%;
    margin: 10upx auto 20upx;
    height: 270rpx;
}
.ad2 image{
    width: 100%;
    height: 100%;
}
.ad3{
    width: 96%;
    margin: 10upx auto 20upx;
    height: 260rpx;
}
.ad3 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: #EE2626;
    border-radius: 62rpx 62rpx 62rpx 62rpx;
}
.c-goods{
    background-color: #f5f5f5;
}
static/css/my2.css
New file
@@ -0,0 +1,154 @@
.myView-bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 908rpx;
    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: 650rpx;
    margin: 30rpx auto;
    background-color: #ffffff;
    height: 236rpx;
    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: 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: 33.33%;
    margin-bottom: 50rpx;
    font-size: 24rpx;
    text-align: center;
}
.mySerCon image{
    width: 72rpx;
    height: 72rpx;
    display: block;
    margin: 0 auto 10rpx;
}
.myBeiAn{
    margin-top: 50rpx;
    text-align: center;
    font-size: 24rpx;
    color: #666666;
}
.myBeiAn view{
    margin-bottom: 10rpx;
}
static/img/back.png
static/img/kk.png
static/img/left.png
static/img/nion.png
static/img/nion1.png
static/img/off.png
static/img/qsBg.png
static/img/qsBg2.png
static/img/qsLeft.png
static/img/qsLife.png
static/img/qsLifeIcon.png
static/img/qsPhysical.png
static/img/qsRight.png
static/img/qyBg.png
static/img/qyBg2.png
static/img/smsBg.png
static/img/yifa.png
static/imgs/ad1.png
static/imgs/ad2.png
static/imgs/ad3.png
static/imgs/c1.png
static/imgs/c2.png
static/imgs/deb.png
static/imgs/index-logo.png

static/imgs/index-top.png
static/imgs/input-bg.png
static/imgs/logo-icon.png
static/imgs/mf1.png
static/imgs/mf2.png
static/imgs/mf3.png
static/imgs/mf4.png
static/imgs/mf5.png
static/imgs/mf6.png
static/imgs/more.png
static/imgs/my-bg.png
static/imgs/notice.png
static/imgs/nv1.png
static/imgs/nv2.png
static/imgs/nv3.png
static/imgs/nv4.png
static/imgs/nv5.png
static/imgs/tb1.png
static/imgs/tb2.png
static/imgs/tb3.png
static/imgs/tb4.png
static/imgs/xxhf.png
static/imgs/xxhh.png
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)
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>
@@ -31,13 +32,16 @@
    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:{
                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: ''
@@ -96,12 +116,19 @@
            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,16 +180,26 @@
            // 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() {
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'
                    },
                    // {
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
New file
@@ -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>
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'
                //微信小程序下,组合动画会出现文字向上闪动问题,再此做特殊处理
                // #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.showPoptrans()
            },
            showPoptrans(){
                this.$nextTick(()=>{
                this.showPopup = true
                this.showTrans = true
                })
            }
        }
    }
uni_modules/uni-popup/package.json
@@ -1,7 +1,7 @@
{
    "id": "uni-popup",
    "displayName": "uni-popup 弹出层",
    "version": "1.8.3",
  "version": "1.9.10",
    "description": " Popup 组件,提供常用的弹层",
    "keywords": [
        "uni-ui",
@@ -12,7 +12,9 @@
    ],
    "repository": "https://github.com/dcloudio/uni-ui",
    "engines": {
        "HBuilderX": ""
    "HBuilderX": "",
    "uni-app": "^4.06",
    "uni-app-x": ""
    },
    "directories": {
        "example": "../../temps/example_temps"
@@ -35,7 +37,10 @@
            "permissions": "无"
        },
        "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
        "type": "component-vue"
    "type": "component-vue",
    "darkmode": "x",
    "i18n": "x",
    "widescreen": "x"
    },
    "uni_modules": {
        "dependencies": [
@@ -45,41 +50,56 @@
        "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": "√"
                },
                "H5-mobile": {
                    "Safari": "y",
                    "Android Browser": "y",
                    "微信浏览器(Android)": "y",
                    "QQ浏览器(Android)": "y"
          "web": {
            "safari": "√",
            "chrome": "√"
                },
                "H5-pc": {
                    "Chrome": "y",
                    "IE": "y",
                    "Edge": "y",
                    "Firefox": "y",
                    "Safari": "y"
          "app": {
            "vue": "√",
            "nvue": "√",
            "android": "√",
            "ios": "√",
            "harmony": "√"
                },
                "小程序": {
                    "微信": "y",
                    "阿里": "y",
                    "百度": "y",
                    "字节跳动": "y",
                    "QQ": "y"
          "mp": {
            "weixin": "√",
            "alipay": "√",
            "toutiao": "√",
            "baidu": "√",
            "kuaishou": "-",
            "jd": "-",
            "harmony": "-",
            "qq": "√",
            "lark": "-"
                },
                "快应用": {
                    "华为": "u",
                    "联盟": "u"
          "quickapp": {
            "huawei": "-",
            "union": "-"
          }
                },
                "Vue": {
                    "vue2": "y",
                    "vue3": "y"
        "uni-app-x": {
          "web": {
            "safari": "√",
            "chrome": "√"
          },
          "app": {
            "android": "√",
            "ios": "√",
            "harmony": "√"
          },
          "mp": {
            "weixin": "√"
          }
                }
            }
        }
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)
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
uni_modules/uni-transition/components/uni-transition/uni-transition.vue
@@ -1,5 +1,14 @@
<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>
@@ -48,13 +57,17 @@
        customClass:{
            type: String,
            default: ''
        }
            },
            onceRender: {
                type: Boolean,
                default: false
            },
    },
    data() {
        return {
            isShow: false,
            transform: '',
            opacity: 1,
                opacity: 0,
            animationData: {},
            durationTime: 300,
            config: {}
@@ -127,18 +140,15 @@
         * @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])
                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)
                    }
                } catch (e) {
                    console.error(`方法 ${i} 不存在`)
                }
            }
                })
            this.animation.step(config)
            return this
        },
@@ -152,24 +162,31 @@
        // 开始过度动画
        open() {
            clearTimeout(this.timer)
            this.transform = ''
            this.isShow = true
            let { opacity, transform } = this.styleInit(false)
            if (typeof opacity !== 'undefined') {
                this.opacity = opacity
            }
            this.transform = transform
                // 新增初始状态重置逻辑(关键)
                this.transform = this.styleInit(false).transform || ''
                this.opacity = this.styleInit(false).opacity || 0
            // 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
            this.$nextTick(() => {
                // TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
                this.timer = setTimeout(() => {
                    this.animation = createAnimation(this.config, this)
                    this.tranfromInit(false).step()
                    this.animation.run()
                        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
                    })
                }, 20)
                        })
                    }, 80)
            })
        },
        // 关闭过度动画
@@ -191,22 +208,20 @@
        },
        // 处理动画开始前的默认样式
        styleInit(type) {
            let styles = {
                transform: ''
            }
            let buildStyle = (type, mode) => {
                if (mode === 'fade') {
                    styles.opacity = this.animationType(type)[mode]
                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 += this.animationType(type)[mode] + ' '
                        styles.transform += value + ' '
                }
            }
            if (typeof this.modeClass === 'string') {
                buildStyle(type, this.modeClass)
            } else {
                this.modeClass.forEach(mode => {
                    buildStyle(type, mode)
                })
                    this.modeClass.forEach(mode => buildStyle(type, mode))
            }
            return styles
        },
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": "√"
        },
        "H5-mobile": {
          "Safari": "y",
          "Android Browser": "y",
          "微信浏览器(Android)": "y",
          "QQ浏览器(Android)": "y"
          "web": {
            "safari": "√",
            "chrome": "√"
        },
        "H5-pc": {
          "Chrome": "y",
          "IE": "y",
          "Edge": "y",
          "Firefox": "y",
          "Safari": "y"
          "app": {
            "vue": "√",
            "nvue": "√",
            "android": "√",
            "ios": "√",
            "harmony": "√"
        },
        "小程序": {
          "微信": "y",
          "阿里": "y",
          "百度": "y",
          "字节跳动": "y",
          "QQ": "y"
          "mp": {
            "weixin": {
        },
        "快应用": {
          "华为": "u",
          "联盟": "u"
            "alipay": {
        },
        "Vue": {
            "vue2": "y",
            "vue3": "y"
            "toutiao": {
            },
            "baidu": {
            },
            "kuaishou": {
            },
            "jd": {
            },
            "harmony": "-",
            "qq": "√",
            "lark": "-"
          },
          "quickapp": {
            "huawei": "√",
            "union": "√"
          }
        },
        "uni-app-x": {
          "web": {
            "safari": "-",
            "chrome": "-"
          },
          "app": {
            "android": "-",
            "ios": "-",
            "harmony": "-"
          },
          "mp": {
            "weixin": "-"
          }
        }
      }
    }
uni_modules/uv-album/changelog.md
New file
@@ -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相册组件
uni_modules/uv-album/components/uv-album/uv-album.vue
New file
@@ -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>
uni_modules/uv-album/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-album/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-alert/changelog.md
New file
@@ -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 警告提示组件
uni_modules/uv-alert/components/uv-alert/props.js
New file
@@ -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
    }
}
uni_modules/uv-alert/components/uv-alert/uv-alert.vue
New file
@@ -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>
uni_modules/uv-alert/package.json
New file
@@ -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"
                }
            }
        }
    }
}
uni_modules/uv-alert/readme.md
New file
@@ -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>
uni_modules/uv-avatar/changelog.md
New file
@@ -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 头像组件
uni_modules/uv-avatar/components/uv-avatar-group/props.js
New file
@@ -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
    }
}
uni_modules/uv-avatar/components/uv-avatar-group/uv-avatar-group.vue
New file
@@ -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>
uni_modules/uv-avatar/components/uv-avatar/props.js
New file
@@ -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
    }
}
uni_modules/uv-avatar/components/uv-avatar/uv-avatar.vue
New file
@@ -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>
uni_modules/uv-avatar/package.json
New file
@@ -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"
                }
            }
        }
    }
}
uni_modules/uv-avatar/readme.md
New file
@@ -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>
uni_modules/uv-back-top/changelog.md
New file
@@ -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 返回顶部
uni_modules/uv-back-top/components/uv-back-top/props.js
New file
@@ -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
    }
}
uni_modules/uv-back-top/components/uv-back-top/uv-back-top.vue
New file
@@ -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>
uni_modules/uv-back-top/package.json
New file
@@ -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"
                }
            }
        }
    }
}
uni_modules/uv-back-top/readme.md
New file
@@ -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>
uni_modules/uv-badge/changelog.md
New file
@@ -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 徽标数,数字角标
uni_modules/uv-badge/components/uv-badge/props.js
New file
@@ -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
    }
}
uni_modules/uv-badge/components/uv-badge/uv-badge.vue
New file
@@ -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>
uni_modules/uv-badge/package.json
New file
@@ -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"
                }
            }
        }
    }
}
uni_modules/uv-badge/readme.md
New file
@@ -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>
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";
uni_modules/uv-calendar/changelog.md
New file
@@ -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 日历
uni_modules/uv-calendar/components/uv-calendar/calendar.js
New file
@@ -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
uni_modules/uv-calendar/components/uv-calendar/header.vue
New file
@@ -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>
uni_modules/uv-calendar/components/uv-calendar/month.vue
New file
@@ -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>
uni_modules/uv-calendar/components/uv-calendar/props.js
New file
@@ -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
    }
}
uni_modules/uv-calendar/components/uv-calendar/uv-calendar.vue
New file
@@ -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>
uni_modules/uv-calendar/package.json
New file
@@ -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"
                }
            }
        }
    }
}
uni_modules/uv-calendar/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-calendars/changelog.md
New file
@@ -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 新版日历发布
uni_modules/uv-calendars/components/uv-calendars/calendar-body.vue
New file
@@ -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>
uni_modules/uv-calendars/components/uv-calendars/calendar-item.vue
New file
@@ -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>
uni_modules/uv-calendars/components/uv-calendars/calendar.js
New file
@@ -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
uni_modules/uv-calendars/components/uv-calendars/i18n/en.json
New file
@@ -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"
}
uni_modules/uv-calendars/components/uv-calendars/i18n/index.js
New file
@@ -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
}
uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hans.json
New file
@@ -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": "六"
}
uni_modules/uv-calendars/components/uv-calendars/i18n/zh-Hant.json
New file
@@ -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": "六"
}
uni_modules/uv-calendars/components/uv-calendars/util.js
New file
@@ -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
uni_modules/uv-calendars/components/uv-calendars/uv-calendars.vue
New file
@@ -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>
uni_modules/uv-calendars/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-calendars/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-code-input/changelog.md
New file
@@ -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 验证码输入
uni_modules/uv-code-input/components/uv-code-input/props.js
New file
@@ -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
    }
}
uni_modules/uv-code-input/components/uv-code-input/uv-code-input.vue
New file
@@ -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>
uni_modules/uv-code-input/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-code-input/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-code/changelog.md
New file
@@ -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 验证码倒计时
uni_modules/uv-code/components/uv-code/props.js
New file
@@ -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
    }
}
uni_modules/uv-code/components/uv-code/uv-code.vue
New file
@@ -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>
uni_modules/uv-code/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-code/readme.md
New file
@@ -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>
uni_modules/uv-collapse/changelog.md
New file
@@ -0,0 +1,5 @@
## 1.0.1(2023-05-16)
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.0(2023-05-10)
uv-collapse 折叠面板
uni_modules/uv-collapse/components/uv-collapse-item/props.js
New file
@@ -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
    }
}
uni_modules/uv-collapse/components/uv-collapse-item/uv-collapse-item.vue
New file
@@ -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>
uni_modules/uv-collapse/components/uv-collapse/props.js
New file
@@ -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
    }
}
uni_modules/uv-collapse/components/uv-collapse/uv-collapse.vue
New file
@@ -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>
uni_modules/uv-collapse/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-collapse/readme.md
New file
@@ -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>
uni_modules/uv-count-down/changelog.md
New file
@@ -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 倒计时
uni_modules/uv-count-down/components/uv-count-down/props.js
New file
@@ -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
    }
}
uni_modules/uv-count-down/components/uv-count-down/utils.js
New file
@@ -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)
}
uni_modules/uv-count-down/components/uv-count-down/uv-count-down.vue
New file
@@ -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>
uni_modules/uv-count-down/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-count-down/readme.md
New file
@@ -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>
uni_modules/uv-count-to/changelog.md
New file
@@ -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 数字滚动
uni_modules/uv-count-to/components/uv-count-to/props.js
New file
@@ -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
    }
}
uni_modules/uv-count-to/components/uv-count-to/uv-count-to.vue
New file
@@ -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>
uni_modules/uv-count-to/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-count-to/readme.md
New file
@@ -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>
uni_modules/uv-datetime-picker/components/uv-datetime-picker/props.js
@@ -98,7 +98,7 @@
        // 确认按钮的颜色
        confirmColor: {
            type: String,
            default: '#EE2626'
            default: '#3c9cff'
        },
        // 每列中可见选项的数量
        visibleItemCount: {
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 )
uni_modules/uv-divider/changelog.md
New file
@@ -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 分割线
uni_modules/uv-divider/components/uv-divider/props.js
New file
@@ -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
    }
}
uni_modules/uv-divider/components/uv-divider/uv-divider.vue
New file
@@ -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>
uni_modules/uv-divider/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-divider/readme.md
New file
@@ -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>
uni_modules/uv-drop-down/changelog.md
New file
@@ -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 下拉筛选组件
uni_modules/uv-drop-down/components/uv-drop-down-item/uv-drop-down-item.vue
New file
@@ -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>
uni_modules/uv-drop-down/components/uv-drop-down-popup/uv-drop-down-popup.vue
New file
@@ -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>
uni_modules/uv-drop-down/components/uv-drop-down/uv-drop-down.vue
New file
@@ -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>
uni_modules/uv-drop-down/package.json
New file
@@ -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"
                }
            }
        }
    }
}
uni_modules/uv-drop-down/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-empty/changelog.md
New file
@@ -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 内容为空
uni_modules/uv-empty/components/uv-empty/props.js
New file
@@ -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
    }
}
uni_modules/uv-empty/components/uv-empty/uv-empty.vue
New file
@@ -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>
uni_modules/uv-empty/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-empty/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
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)
uni_modules/uv-grid/changelog.md
New file
@@ -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 宫格布局
uni_modules/uv-grid/components/uv-grid-item/props.js
New file
@@ -0,0 +1,15 @@
export default {
    props: {
        // 宫格的name
        name: {
            type: [String, Number, null],
            default: null
        },
        // 背景颜色
        bgColor: {
            type: String,
            default: 'transparent'
        },
        ...uni.$uv?.props?.gridItem
    }
}
uni_modules/uv-grid/components/uv-grid-item/uv-grid-item.vue
New file
@@ -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>
uni_modules/uv-grid/components/uv-grid/props.js
New file
@@ -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
    }
}
uni_modules/uv-grid/components/uv-grid/uv-grid.vue
New file
@@ -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>
uni_modules/uv-grid/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-grid/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-image/changelog.md
New file
@@ -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 图片
uni_modules/uv-image/components/uv-image/props.js
New file
@@ -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
    }
}
uni_modules/uv-image/components/uv-image/uv-image.vue
New file
@@ -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>
uni_modules/uv-image/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-image/readme.md
New file
@@ -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)
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-index-list/changelog.md
New file
@@ -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 索引列表
uni_modules/uv-index-list/components/uv-index-anchor/props.js
New file
@@ -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
    }
}
uni_modules/uv-index-list/components/uv-index-anchor/uv-index-anchor.vue
New file
@@ -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>
uni_modules/uv-index-list/components/uv-index-item/uv-index-item.vue
New file
@@ -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>
uni_modules/uv-index-list/components/uv-index-list/props.js
New file
@@ -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
    }
}
uni_modules/uv-index-list/components/uv-index-list/uv-index-list.vue
New file
@@ -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>
uni_modules/uv-index-list/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-index-list/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-keyboard/changelog.md
New file
@@ -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 键盘
uni_modules/uv-keyboard/components/uv-keyboard-car/props.js
New file
@@ -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
        }
    }
}
uni_modules/uv-keyboard/components/uv-keyboard-car/uv-keyboard-car.vue
New file
@@ -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>
uni_modules/uv-keyboard/components/uv-keyboard-number/props.js
New file
@@ -0,0 +1,19 @@
export default {
    props: {
        // 键盘的类型,number-数字键盘,card-身份证键盘
        mode: {
            type: String,
            default: 'number'
        },
        // 是否显示键盘的"."符号
        dotDisabled: {
            type: Boolean,
            default: false
        },
        // 是否打乱键盘按键的顺序
        random: {
            type: Boolean,
            default: false
        }
    }
}
uni_modules/uv-keyboard/components/uv-keyboard-number/uv-keyboard-number.vue
New file
@@ -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>
uni_modules/uv-keyboard/components/uv-keyboard/props.js
New file
@@ -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
    }
}
uni_modules/uv-keyboard/components/uv-keyboard/uv-keyboard.vue
New file
@@ -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>
uni_modules/uv-keyboard/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-keyboard/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-line-progress/changelog.md
New file
@@ -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 线形进度条
uni_modules/uv-line-progress/components/uv-line-progress/props.js
New file
@@ -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
    }
}
uni_modules/uv-line-progress/components/uv-line-progress/uv-line-progress.vue
New file
@@ -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>
uni_modules/uv-line-progress/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-line-progress/readme.md
New file
@@ -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>
uni_modules/uv-link/changelog.md
New file
@@ -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 超链接组件
uni_modules/uv-link/components/uv-link/props.js
New file
@@ -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
    }
}
uni_modules/uv-link/components/uv-link/uv-link.vue
New file
@@ -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>
uni_modules/uv-link/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-link/readme.md
New file
@@ -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>
uni_modules/uv-list/changelog.md
New file
@@ -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 列表
uni_modules/uv-list/components/uv-list-item/uv-list-item.vue
New file
@@ -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>
uni_modules/uv-list/components/uv-list/uv-list.vue
New file
@@ -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>
uni_modules/uv-list/package.json
New file
@@ -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"
                }
            }
        }
  }
}
uni_modules/uv-list/readme.md
New file
@@ -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">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>
uni_modules/uv-load-more/changelog.md
New file
@@ -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 加载更多
uni_modules/uv-load-more/components/uv-load-more/props.js
New file
@@ -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
    }
}
uni_modules/uv-load-more/components/uv-load-more/uv-load-more.vue
New file
@@ -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 truncated after the above file
uni_modules/uv-load-more/package.json uni_modules/uv-load-more/readme.md uni_modules/uv-modal/changelog.md uni_modules/uv-modal/components/uv-modal/props.js uni_modules/uv-modal/components/uv-modal/uv-modal.vue uni_modules/uv-modal/package.json uni_modules/uv-modal/readme.md uni_modules/uv-navbar/changelog.md uni_modules/uv-navbar/components/uv-navbar/props.js uni_modules/uv-navbar/components/uv-navbar/uv-navbar.vue uni_modules/uv-navbar/package.json uni_modules/uv-navbar/readme.md uni_modules/uv-no-network/changelog.md uni_modules/uv-no-network/components/uv-no-network/props.js uni_modules/uv-no-network/components/uv-no-network/uv-no-network.vue uni_modules/uv-no-network/package.json uni_modules/uv-no-network/readme.md uni_modules/uv-notice-bar/changelog.md uni_modules/uv-notice-bar/components/uv-column-notice/props.js uni_modules/uv-notice-bar/components/uv-column-notice/uv-column-notice.vue uni_modules/uv-notice-bar/components/uv-notice-bar/props.js uni_modules/uv-notice-bar/components/uv-notice-bar/uv-notice-bar.vue uni_modules/uv-notice-bar/components/uv-row-notice/props.js uni_modules/uv-notice-bar/components/uv-row-notice/uv-row-notice.vue uni_modules/uv-notice-bar/package.json uni_modules/uv-notice-bar/readme.md uni_modules/uv-notify/changelog.md uni_modules/uv-notify/components/uv-notify/props.js uni_modules/uv-notify/components/uv-notify/uv-notify.vue uni_modules/uv-notify/package.json uni_modules/uv-notify/readme.md uni_modules/uv-number-box/changelog.md uni_modules/uv-number-box/components/uv-number-box/props.js uni_modules/uv-number-box/components/uv-number-box/uv-number-box.vue uni_modules/uv-number-box/package.json uni_modules/uv-number-box/readme.md uni_modules/uv-parse/changelog.md uni_modules/uv-parse/components/uv-parse/node/node.vue uni_modules/uv-parse/components/uv-parse/parser.js uni_modules/uv-parse/components/uv-parse/uv-parse.vue uni_modules/uv-parse/package.json uni_modules/uv-parse/readme.md uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js uni_modules/uv-parse/static/app-plus/uv-parse/local.html uni_modules/uv-pick-color/changelog.md uni_modules/uv-pick-color/components/uv-pick-color/colors.js uni_modules/uv-pick-color/components/uv-pick-color/props.js uni_modules/uv-pick-color/components/uv-pick-color/uv-pick-color.vue uni_modules/uv-pick-color/package.json uni_modules/uv-pick-color/readme.md uni_modules/uv-picker/components/uv-picker/props.js uni_modules/uv-picker/components/uv-picker/uv-picker.vue uni_modules/uv-qrcode/changelog.md uni_modules/uv-qrcode/components/uv-qrcode/cache.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/bridge/bridge-weex.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleLinearGradient.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStylePattern.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/FillStyleRadialGradient.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-2d/RenderingContext.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ActiveInfo.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Buffer.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Framebuffer.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLenum.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLmethod.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/GLtype.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Program.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Renderbuffer.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/RenderingContext.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Shader.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/ShaderPrecisionFormat.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/Texture.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/UniformLocation.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/context-webgl/classUtils.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/canvas.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/image.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/env/tool.js uni_modules/uv-qrcode/components/uv-qrcode/gcanvas/index.js uni_modules/uv-qrcode/components/uv-qrcode/props.js uni_modules/uv-qrcode/components/uv-qrcode/qrcode.js uni_modules/uv-qrcode/components/uv-qrcode/queue.js uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue uni_modules/uv-qrcode/package.json uni_modules/uv-qrcode/readme.md uni_modules/uv-radio/changelog.md uni_modules/uv-radio/components/uv-radio-group/props.js uni_modules/uv-radio/components/uv-radio-group/uv-radio-group.vue uni_modules/uv-radio/components/uv-radio/props.js uni_modules/uv-radio/components/uv-radio/uv-radio.vue uni_modules/uv-radio/package.json uni_modules/uv-radio/readme.md uni_modules/uv-rate/changelog.md uni_modules/uv-rate/components/uv-rate/props.js uni_modules/uv-rate/components/uv-rate/uv-rate.vue uni_modules/uv-rate/package.json uni_modules/uv-rate/readme.md uni_modules/uv-read-more/changelog.md uni_modules/uv-read-more/components/uv-read-more/props.js uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue uni_modules/uv-read-more/package.json uni_modules/uv-read-more/readme.md uni_modules/uv-row/changelog.md uni_modules/uv-row/components/uv-col/props.js uni_modules/uv-row/components/uv-col/uv-col.vue uni_modules/uv-row/components/uv-row/props.js uni_modules/uv-row/components/uv-row/uv-row.vue uni_modules/uv-row/package.json uni_modules/uv-row/readme.md uni_modules/uv-scroll-list/changelog.md uni_modules/uv-scroll-list/components/uv-scroll-list/nvue.js uni_modules/uv-scroll-list/components/uv-scroll-list/props.js uni_modules/uv-scroll-list/components/uv-scroll-list/scrollWxs.wxs uni_modules/uv-scroll-list/components/uv-scroll-list/uv-scroll-list.vue uni_modules/uv-scroll-list/package.json uni_modules/uv-scroll-list/readme.md uni_modules/uv-search/changelog.md uni_modules/uv-search/components/uv-search/props.js uni_modules/uv-search/components/uv-search/uv-search.vue uni_modules/uv-search/package.json uni_modules/uv-search/readme.md uni_modules/uv-skeleton/changelog.md uni_modules/uv-skeleton/components/uv-skeleton/props.js uni_modules/uv-skeleton/components/uv-skeleton/uv-skeleton.vue uni_modules/uv-skeleton/package.json uni_modules/uv-skeleton/readme.md uni_modules/uv-skeletons/changelog.md uni_modules/uv-skeletons/components/uv-skeletons/uv-skeletons.vue uni_modules/uv-skeletons/package.json uni_modules/uv-skeletons/readme.md uni_modules/uv-slider/changelog.md uni_modules/uv-slider/components/uv-slider/props.js uni_modules/uv-slider/components/uv-slider/uv-slider.vue uni_modules/uv-slider/package.json uni_modules/uv-slider/readme.md uni_modules/uv-steps/changelog.md uni_modules/uv-steps/components/uv-steps-item/props.js uni_modules/uv-steps/components/uv-steps-item/uv-steps-item.vue uni_modules/uv-steps/components/uv-steps/props.js uni_modules/uv-steps/components/uv-steps/uv-steps.vue uni_modules/uv-steps/package.json uni_modules/uv-steps/readme.md uni_modules/uv-sticky/changelog.md uni_modules/uv-sticky/components/uv-sticky/props.js uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue uni_modules/uv-sticky/package.json uni_modules/uv-sticky/readme.md uni_modules/uv-subsection/changelog.md uni_modules/uv-subsection/components/uv-subsection/props.js uni_modules/uv-subsection/components/uv-subsection/uv-subsection.vue uni_modules/uv-subsection/package.json uni_modules/uv-subsection/readme.md uni_modules/uv-swipe-action/changelog.md uni_modules/uv-swipe-action/components/uv-swipe-action-item/index - backup.wxs uni_modules/uv-swipe-action/components/uv-swipe-action-item/index.wxs uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue - backup.js uni_modules/uv-swipe-action/components/uv-swipe-action-item/nvue.js uni_modules/uv-swipe-action/components/uv-swipe-action-item/props.js uni_modules/uv-swipe-action/components/uv-swipe-action-item/uv-swipe-action-item.vue uni_modules/uv-swipe-action/components/uv-swipe-action-item/wxs.js uni_modules/uv-swipe-action/components/uv-swipe-action/props.js uni_modules/uv-swipe-action/components/uv-swipe-action/uv-swipe-action.vue uni_modules/uv-swipe-action/package.json uni_modules/uv-swipe-action/readme.md uni_modules/uv-swiper/changelog.md uni_modules/uv-swiper/components/uv-swiper-indicator/props.js uni_modules/uv-swiper/components/uv-swiper-indicator/uv-swiper-indicator.vue uni_modules/uv-swiper/components/uv-swiper/props.js uni_modules/uv-swiper/components/uv-swiper/uv-swiper.vue uni_modules/uv-swiper/package.json uni_modules/uv-swiper/readme.md uni_modules/uv-switch/changelog.md uni_modules/uv-switch/components/uv-switch/props.js uni_modules/uv-switch/components/uv-switch/uv-switch.vue uni_modules/uv-switch/package.json uni_modules/uv-switch/readme.md uni_modules/uv-tabbar/changelog.md uni_modules/uv-tabbar/components/uv-tabbar-item/props.js uni_modules/uv-tabbar/components/uv-tabbar-item/uv-tabbar-item.vue uni_modules/uv-tabbar/components/uv-tabbar/props.js uni_modules/uv-tabbar/components/uv-tabbar/uv-tabbar.vue uni_modules/uv-tabbar/package.json uni_modules/uv-tabbar/readme.md uni_modules/uv-tabs/changelog.md uni_modules/uv-tabs/components/uv-tabs/props.js uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue uni_modules/uv-tabs/package.json uni_modules/uv-tabs/readme.md uni_modules/uv-tags/changelog.md uni_modules/uv-tags/components/uv-tags/props.js uni_modules/uv-tags/components/uv-tags/uv-tags.vue uni_modules/uv-tags/package.json uni_modules/uv-tags/readme.md uni_modules/uv-text/changelog.md uni_modules/uv-text/components/uv-text/props.js uni_modules/uv-text/components/uv-text/uv-text.vue uni_modules/uv-text/components/uv-text/value.js uni_modules/uv-text/package.json uni_modules/uv-text/readme.md uni_modules/uv-toast/changelog.md uni_modules/uv-toast/components/uv-toast/uv-toast.vue uni_modules/uv-toast/package.json uni_modules/uv-toast/readme.md uni_modules/uv-toolbar/components/uv-toolbar/props.js uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue uni_modules/uv-toolbar/readme.md uni_modules/uv-tooltip/changelog.md uni_modules/uv-tooltip/components/uv-tooltip/props.js uni_modules/uv-tooltip/components/uv-tooltip/uv-tooltip.vue uni_modules/uv-tooltip/package.json uni_modules/uv-tooltip/readme.md uni_modules/uv-ui/changelog.md uni_modules/uv-ui/components/uv-ui/uv-ui.vue uni_modules/uv-ui/package.json uni_modules/uv-ui/readme.md uni_modules/uv-upload/changelog.md uni_modules/uv-upload/components/uv-preview-video/uv-preview-video.vue uni_modules/uv-upload/components/uv-upload/mixin.js uni_modules/uv-upload/components/uv-upload/props.js uni_modules/uv-upload/components/uv-upload/utils.js uni_modules/uv-upload/components/uv-upload/uv-upload.vue uni_modules/uv-upload/package.json uni_modules/uv-upload/readme.md uni_modules/uv-vtabs/changelog.md uni_modules/uv-vtabs/components/uv-vtabs-item/uv-vtabs-item.vue uni_modules/uv-vtabs/components/uv-vtabs/props.js uni_modules/uv-vtabs/components/uv-vtabs/uv-vtabs.vue uni_modules/uv-vtabs/package.json uni_modules/uv-vtabs/readme.md uni_modules/uv-waterfall/changelog.md uni_modules/uv-waterfall/components/uv-waterfall/props.js uni_modules/uv-waterfall/components/uv-waterfall/uv-waterfall.vue uni_modules/uv-waterfall/package.json uni_modules/uv-waterfall/readme.md util.js